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