4
* This is the "end-user" TLS class.
5
* It works just like a Socket, by encapsulating a Socket and
6
* wrapping the TLS protocol around the data that passes over it.
7
* This class can either create a socket connection, or reuse an
8
* existing connected socket. The later is useful for STARTTLS flows.
10
* Copyright (c) 2007 Henri Torgemane
12
* See LICENSE.txt for full license information.
14
package com.hurlant.crypto.tls {
15
import flash.events.Event;
16
import flash.events.EventDispatcher;
17
import flash.events.IOErrorEvent;
18
import flash.events.ProgressEvent;
19
import flash.events.SecurityErrorEvent;
20
import flash.net.ObjectEncoding;
21
import flash.net.Socket;
22
import flash.utils.ByteArray;
23
import flash.utils.Endian;
24
import flash.utils.IDataInput;
25
import flash.utils.IDataOutput;
26
import flash.utils.clearTimeout;
27
import flash.utils.setTimeout;
28
import com.hurlant.crypto.cert.X509Certificate;
31
[Event(name="close", type="flash.events.Event")]
32
[Event(name="connect", type="flash.events.Event")]
33
[Event(name="ioError", type="flash.events.IOErrorEvent")]
34
[Event(name="securityError", type="flash.events.SecurityErrorEvent")]
35
[Event(name="socketData", type="flash.events.ProgressEvent")]
36
[Event(name="acceptPeerCertificatePrompt", type="flash.events.Event")]
39
* It feels like a socket, but it wraps the stream
45
public class TLSSocket extends Socket implements IDataInput, IDataOutput {
47
private var _endian:String;
48
private var _objectEncoding:uint;
50
private var _iStream:ByteArray;
51
private var _oStream:ByteArray;
52
private var _iStream_cursor:uint;
54
private var _socket:Socket;
55
private var _config:TLSConfig;
56
private var _engine:TLSEngine;
57
public static const ACCEPT_PEER_CERT_PROMPT:String = "acceptPeerCertificatePrompt"
59
public function TLSSocket(host:String = null, port:int = 0, config:TLSConfig = null) {
61
if (host!=null && port!=0) {
66
override public function get bytesAvailable():uint {
67
return _iStream.bytesAvailable;
69
override public function get connected():Boolean {
70
return _socket.connected;
72
override public function get endian():String {
75
override public function set endian(value:String):void {
77
_iStream.endian = value;
78
_oStream.endian = value;
80
override public function get objectEncoding():uint {
81
return _objectEncoding;
83
override public function set objectEncoding(value:uint):void {
84
_objectEncoding = value;
85
_iStream.objectEncoding = value;
86
_oStream.objectEncoding = value;
90
private function onTLSData(event:TLSEvent):void {
91
if (_iStream.position == _iStream.length) {
92
_iStream.position = 0;
96
var cursor:uint = _iStream.position;
97
_iStream.position = _iStream_cursor;
98
_iStream.writeBytes(event.data);
99
_iStream_cursor = _iStream.position;
100
_iStream.position = cursor;
101
dispatchEvent(new ProgressEvent(ProgressEvent.SOCKET_DATA, false, false, event.data.length));
104
private function onTLSReady(event:TLSEvent):void {
109
private function onTLSClose(event:Event):void {
110
dispatchEvent(event);
111
// trace("Received TLS close");
115
private var _ready:Boolean;
116
private var _writeScheduler:uint;
117
private function scheduleWrite():void {
118
if (_writeScheduler!=0) return;
119
_writeScheduler = setTimeout(commitWrite, 0);
121
private function commitWrite():void {
122
clearTimeout(_writeScheduler);
125
_engine.sendApplicationData(_oStream);
131
override public function close():void {
134
if (_socket.connected) {
139
public function setTLSConfig( config:TLSConfig) : void {
143
override public function connect(host:String, port:int):void {
144
init(new Socket, _config, host);
145
_socket.connect(host, port);
149
public function releaseSocket() : void {
150
_socket.removeEventListener(Event.CONNECT, dispatchEvent);
151
_socket.removeEventListener(IOErrorEvent.IO_ERROR, dispatchEvent);
152
_socket.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, dispatchEvent);
153
_socket.removeEventListener(Event.CLOSE, dispatchEvent);
154
_socket.removeEventListener(ProgressEvent.SOCKET_DATA, _engine.dataAvailable);
158
public function reinitialize(host:String, config:TLSConfig) : void {
159
// Reinitialize the connection using new values
160
// but re-use the existing socket
161
// Doubt this is useful in any valid context other than my specific case (VMWare)
162
var ba:ByteArray = new ByteArray;
164
if (_socket.bytesAvailable > 0) {
165
_socket.readBytes(ba, 0, _socket.bytesAvailable);
167
// Do nothing with it.
168
_iStream = new ByteArray;
169
_oStream = new ByteArray;
171
objectEncoding = ObjectEncoding.DEFAULT;
172
endian = Endian.BIG_ENDIAN;
174
_socket.addEventListener(Event.CONNECT, dispatchEvent);
175
_socket.addEventListener(IOErrorEvent.IO_ERROR, dispatchEvent);
176
_socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, dispatchEvent);
177
_socket.addEventListener(Event.CLOSE, dispatchEvent);
180
if (config == null) {
181
config = new TLSConfig(TLSEngine.CLIENT);
184
_engine = new TLSEngine(config, _socket, _socket, host);
185
_engine.addEventListener(TLSEvent.DATA, onTLSData);
186
_engine.addEventListener(TLSEvent.READY, onTLSReady);
187
_engine.addEventListener(Event.CLOSE, onTLSClose);
188
_engine.addEventListener(ProgressEvent.SOCKET_DATA, function(e:*):void { _socket.flush(); });
189
_socket.addEventListener(ProgressEvent.SOCKET_DATA, _engine.dataAvailable);
190
_engine.addEventListener( TLSEvent.PROMPT_ACCEPT_CERT, onAcceptCert );
196
public function startTLS(socket:Socket, host:String, config:TLSConfig = null):void {
197
if (!socket.connected) {
198
throw new Error("Cannot STARTTLS on a socket that isn't connected.");
200
init(socket, config, host);
204
private function init(socket:Socket, config:TLSConfig, host:String):void {
205
_iStream = new ByteArray;
206
_oStream = new ByteArray;
208
objectEncoding = ObjectEncoding.DEFAULT;
209
endian = Endian.BIG_ENDIAN;
211
_socket.addEventListener(Event.CONNECT, dispatchEvent);
212
_socket.addEventListener(IOErrorEvent.IO_ERROR, dispatchEvent);
213
_socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, dispatchEvent);
214
_socket.addEventListener(Event.CLOSE, dispatchEvent);
216
if (config == null) {
217
config = new TLSConfig(TLSEngine.CLIENT);
219
_engine = new TLSEngine(config, _socket, _socket, host);
220
_engine.addEventListener(TLSEvent.DATA, onTLSData);
221
_engine.addEventListener( TLSEvent.PROMPT_ACCEPT_CERT, onAcceptCert );
222
_engine.addEventListener(TLSEvent.READY, onTLSReady);
223
_engine.addEventListener(Event.CLOSE, onTLSClose);
224
_engine.addEventListener(ProgressEvent.SOCKET_DATA, function(e:*):void { if(connected) _socket.flush(); });
225
_socket.addEventListener(ProgressEvent.SOCKET_DATA, _engine.dataAvailable);
230
override public function flush():void {
235
override public function readBoolean():Boolean {
236
return _iStream.readBoolean();
239
override public function readByte():int {
240
return _iStream.readByte();
243
override public function readBytes(bytes:ByteArray, offset:uint = 0, length:uint = 0):void {
244
return _iStream.readBytes(bytes, offset, length);
247
override public function readDouble():Number {
248
return _iStream.readDouble();
251
override public function readFloat():Number {
252
return _iStream.readFloat();
255
override public function readInt():int {
256
return _iStream.readInt();
259
override public function readMultiByte(length:uint, charSet:String):String {
260
return _iStream.readMultiByte(length, charSet);
263
override public function readObject():* {
264
return _iStream.readObject();
267
override public function readShort():int {
268
return _iStream.readShort();
271
override public function readUnsignedByte():uint {
272
return _iStream.readUnsignedByte();
275
override public function readUnsignedInt():uint {
276
return _iStream.readUnsignedInt();
279
override public function readUnsignedShort():uint {
280
return _iStream.readUnsignedShort();
283
override public function readUTF():String {
284
return _iStream.readUTF();
287
override public function readUTFBytes(length:uint):String {
288
return _iStream.readUTFBytes(length);
291
override public function writeBoolean(value:Boolean):void {
292
_oStream.writeBoolean(value);
296
override public function writeByte(value:int):void {
297
_oStream.writeByte(value);
301
override public function writeBytes(bytes:ByteArray, offset:uint = 0, length:uint = 0):void {
302
_oStream.writeBytes(bytes, offset, length);
306
override public function writeDouble(value:Number):void {
307
_oStream.writeDouble(value);
311
override public function writeFloat(value:Number):void {
312
_oStream.writeFloat(value);
316
override public function writeInt(value:int):void {
317
_oStream.writeInt(value);
321
override public function writeMultiByte(value:String, charSet:String):void {
322
_oStream.writeMultiByte(value, charSet);
326
override public function writeObject(object:*):void {
327
_oStream.writeObject(object);
331
override public function writeShort(value:int):void {
332
_oStream.writeShort(value);
336
override public function writeUnsignedInt(value:uint):void {
337
_oStream.writeUnsignedInt(value);
341
override public function writeUTF(value:String):void {
342
_oStream.writeUTF(value);
346
override public function writeUTFBytes(value:String):void {
347
_oStream.writeUTFBytes(value);
351
public function getPeerCertificate() : X509Certificate {
352
return _engine.peerCertificate;
355
public function onAcceptCert( event:TLSEvent ) : void {
356
dispatchEvent( new TLSSocketEvent( _engine.peerCertificate ) );
359
// These are just a passthroughs to the engine. Encapsulation, et al
360
public function acceptPeerCertificate( event:Event ) : void {
361
_engine.acceptPeerCertificate();
364
public function rejectPeerCertificate( event:Event ) : void {
365
_engine.rejectPeerCertificate();