📄 tlsengine.as
字号:
return null;
}
}
private function verifyHandshake(verifyData:ByteArray):void {
var data:ByteArray = _securityParameters.computeVerifyData(1-_entity, _handshakePayloads);
if (ArrayUtil.equals(verifyData, data)) {
_state = STATE_READY;
dispatchEvent(new TLSEvent(TLSEvent.READY));
} else {
throw new TLSError("Invalid Finished mac.", TLSError.bad_record_mac);
}
}
private function enforceClient():Boolean {
if (_entity == SERVER) {
trace("Invalid state for a TLS server.");
return false;
}
return true;
}
private function enforceServer():Boolean {
if (_entity == CLIENT) {
trace("Invalid state for a TLS client.");
return false;
}
return true;
}
private function parseHandshakeClientKeyExchange(type:uint, length:uint, rec:ByteArray):void {
if (_securityParameters.useRSA) {
// skip 2 bytes for length.
var len:uint = rec.readShort();
var cipher:ByteArray = new ByteArray;
rec.readBytes(cipher, 0, len);
var preMasterSecret:ByteArray = new ByteArray;
_config.privateKey.decrypt(cipher, preMasterSecret, len);
_securityParameters.setPreMasterSecret(preMasterSecret);
// now is a good time to get our pending states
var o:Object = _securityParameters.getConnectionStates();
_pendingReadState = o.read;
_pendingWriteState = o.write;
} else {
throw new TLSError("parseHandshakeClientKeyExchange not implemented for DH modes.", TLSError.internal_error);
}
}
private function parseHandshakeHello(type:uint, length:uint, rec:IDataInput):Object {
var ret:Object;
var ver:uint = rec.readShort();
if (ver != TLS_VERSION) {
throw new TLSError("Unsupported TLS version: "+ver.toString(16), TLSError.protocol_version);
}
var random:ByteArray = new ByteArray;
rec.readBytes(random, 0, 32);
var session_length:uint = rec.readByte();
var session:ByteArray = new ByteArray;
rec.readBytes(session, 0, session_length);
var suites:Array = [];
if (type==HANDSHAKE_CLIENT_HELLO) {
var suites_length:uint = rec.readShort();
for (var i:uint=0;i<suites_length/2;i++) {
suites.push(rec.readShort());
}
} else {
suites.push(rec.readShort()); // just one winner.
}
var compressions:Array = [];
if (type==HANDSHAKE_CLIENT_HELLO) {
var comp_length:uint = rec.readByte();
for (i=0;i<comp_length;i++) {
compressions.push(rec.readByte());
}
} else {
compressions.push(rec.readByte());
}
ret = {random:random, session:session, suites:suites, compressions:compressions};
if (type==HANDSHAKE_CLIENT_HELLO) {
var sofar:uint = 2+32+1+session_length+2+suites_length+1+comp_length;
var extensions:Array = [];
if (sofar<length) {
// we have extensions. great.
var ext_total_length:uint = rec.readShort();
while (ext_total_length>0) {
var ext_type:uint = rec.readShort();
var ext_length:uint = rec.readShort();
var ext_data:ByteArray = new ByteArray;
rec.readBytes(ext_data, 0, ext_length);
ext_total_length -= 4+ext_length;
extensions.push({type:ext_type, length:ext_length, data:ext_data});
}
}
ret.ext = extensions;
}
return ret;
}
private function sendClientHello():void {
var rec:ByteArray = new ByteArray;
// version
rec.writeShort(TLS_VERSION);
// random
var prng:Random = new Random;
var clientRandom:ByteArray = new ByteArray;
prng.nextBytes(clientRandom, 32);
_securityParameters.setClientRandom(clientRandom);
rec.writeBytes(clientRandom,0,32);
// session
rec.writeByte(32);
prng.nextBytes(rec, 32);
// Cipher suites
var cs:Array = _config.cipherSuites;
rec.writeShort(2* cs.length);
for (var i:int=0;i<cs.length;i++) {
rec.writeShort(cs[i]);
}
// Compression
cs = _config.compressions;
rec.writeByte(cs.length);
for (i=0;i<cs.length;i++) {
rec.writeByte(cs[i]);
}
// no extensions, yet.
rec.position = 0;
sendHandshake(HANDSHAKE_CLIENT_HELLO, rec.length, rec);
}
private function findMatch(a1:Array, a2:Array):int {
for (var i:int=0;i<a1.length;i++) {
var e:uint = a1[i];
if (a2.indexOf(e)>-1) {
return e;
}
}
return -1;
}
private function sendServerHello(v:Object):void {
var cipher:int = findMatch(_config.cipherSuites, v.suites);
if (cipher == -1) {
throw new TLSError("No compatible cipher found.", TLSError.handshake_failure);
}
_securityParameters.setCipher(cipher);
var comp:int = findMatch(_config.compressions, v.compressions);
if (comp == 01) {
throw new TLSError("No compatible compression method found.", TLSError.handshake_failure);
}
_securityParameters.setCompression(comp);
_securityParameters.setClientRandom(v.random);
var rec:ByteArray = new ByteArray;
rec.writeShort(TLS_VERSION);
var prng:Random = new Random;
var serverRandom:ByteArray = new ByteArray;
prng.nextBytes(serverRandom, 32);
_securityParameters.setServerRandom(serverRandom);
rec.writeBytes(serverRandom,0,32);
// session
rec.writeByte(32);
prng.nextBytes(rec, 32);
// Cipher suite
rec.writeShort(v.suites[0]);
// Compression
rec.writeByte(v.compressions[0]);
rec.position = 0;
sendHandshake(HANDSHAKE_SERVER_HELLO, rec.length, rec);
}
private function sendCertificate():void {
var cert:ByteArray = _config.certificate;
if (cert == null) return; // no cert for you!
var len:uint = cert.length;
var len2:uint = len + 3; // this implies we only ever send 1 certificate. XXX okay for now.
var rec:ByteArray = new ByteArray;
rec.writeByte(len2>>16);
rec.writeShort(len2&65535);
rec.writeByte(len>>16);
rec.writeShort(len&65535);
rec.writeBytes(cert);
rec.position = 0;
sendHandshake(HANDSHAKE_CERTIFICATE, rec.length, rec);
}
private function sendServerHelloDone():void {
var rec:ByteArray = new ByteArray;
sendHandshake(HANDSHAKE_HELLO_DONE, rec.length, rec);
}
private function sendClientKeyExchange():void {
if (_securityParameters.useRSA) {
var p:ByteArray = new ByteArray;
p.writeShort(TLS_VERSION);
var prng:Random = new Random;
prng.nextBytes(p, 46);
p.position = 0;
var preMasterSecret:ByteArray = new ByteArray;
preMasterSecret.writeBytes(p, 0, p.length);
_securityParameters.setPreMasterSecret(preMasterSecret);
var tmp:ByteArray = new ByteArray;
_otherCertificate.getPublicKey().encrypt(p, tmp, p.length);
var rec:ByteArray = new ByteArray;
rec.writeShort(tmp.length);
rec.writeBytes(tmp, 0, tmp.length);
rec.position=0;
sendHandshake(HANDSHAKE_CLIENT_KEY_EXCHANGE, rec.length, rec);
// now is a good time to get our pending states
var o:Object = _securityParameters.getConnectionStates();
_pendingReadState = o.read;
_pendingWriteState = o.write;
} else {
throw new TLSError("Non-RSA Client Key Exchange not implemented.", TLSError.internal_error);
}
}
private function sendFinished():void {
var data:ByteArray = _securityParameters.computeVerifyData(_entity, _handshakePayloads);
data.position=0;
sendHandshake(HANDSHAKE_FINISHED, data.length, data);
}
private function sendHandshake(type:uint, len:uint, payload:IDataInput):void {
var rec:ByteArray = new ByteArray;
rec.writeByte(type);
rec.writeByte(0);
rec.writeShort(len);
payload.readBytes(rec, rec.position, len);
_handshakePayloads.writeBytes(rec, 0, rec.length);
sendRecord(PROTOCOL_HANDSHAKE, rec);
}
private function sendChangeCipherSpec():void {
var rec:ByteArray = new ByteArray;
rec[0] = 1;
sendRecord(PROTOCOL_CHANGE_CIPHER_SPEC, rec);
// right after, switch the cipher for writing.
_currentWriteState = _pendingWriteState;
_pendingWriteState = null;
}
public function sendApplicationData(data:ByteArray, offset:uint=0, length:uint=0):void {
var rec:ByteArray = new ByteArray;
var len:uint = length;
while (len>16384) {
rec.position = 0;
rec.writeBytes(data, offset, 16384);
rec.position = 0;
sendRecord(PROTOCOL_APPLICATION_DATA, rec);
offset += 16384;
len -= 16384;
}
rec.position = 0;
rec.writeBytes(data, offset, len);
rec.position = 0;
sendRecord(PROTOCOL_APPLICATION_DATA, rec);
}
private function sendRecord(type:uint, payload:ByteArray):void {
// encrypt
payload = _currentWriteState.encrypt(type, payload);
_oStream.writeByte(type);
_oStream.writeShort(TLS_VERSION);
_oStream.writeShort(payload.length);
_oStream.writeBytes(payload, 0, payload.length);
scheduleWrite();
}
private var _writeScheduler:uint;
private function scheduleWrite():void {
if (_writeScheduler!=0) return;
_writeScheduler = setTimeout(commitWrite, 0);
}
private function commitWrite():void {
clearTimeout(_writeScheduler);
_writeScheduler = 0;
if (_state != STATE_CLOSED) {
dispatchEvent(new ProgressEvent(ProgressEvent.SOCKET_DATA));
}
}
private function sendClientAck():void {
// send a client cert if we were asked for one. (although we don't support that yet. XXX)
// send a client key exchange
sendClientKeyExchange();
// send a change cipher spec
sendChangeCipherSpec();
// send a finished
sendFinished();
}
/**
* Vaguely gross function that parses a RSA key out of a certificate.
*
* As long as that certificate looks just the way we expect it to.
*
* @param cert: A bytearray that contains some DER-encoded goodness.
*
*/
private function loadCertificates(certs:Array):void {
var firstCert:X509Certificate = null;
for (var i:int=0;i<certs.length;i++) {
var x509:X509Certificate = new X509Certificate(certs[i]);
_store.addCertificate(x509);
if (firstCert==null) {
firstCert = x509;
}
}
if (firstCert.isSigned(_store, _config.CAStore)) {
// ok, that's encouraging. now for the hostname match.
if (_otherIdentity==null) {
// we don't care who we're talking with. groovy.
trace("TLS WARNING: No check made on the certificate's identity.");
_otherCertificate = firstCert;
} else {
if (firstCert.getCommonName()==_otherIdentity) {
_otherCertificate = firstCert;
} else {
throw new TLSError("Invalid common name: "+firstCert.getCommonName()+", expected "+_otherIdentity, TLSError.bad_certificate);
}
}
} else {
throw new TLSError("Cannot verify certificate", TLSError.bad_certificate);
}
}
private function parseAlert(p:ByteArray):void {
//throw new Error("Alert not implemented.");
// 7.2
trace("GOT ALERT! type="+p[1]);
close();
}
private function parseChangeCipherSpec(p:ByteArray):void {
p.readUnsignedByte();
if (_pendingReadState==null) {
throw new TLSError("Not ready to Change Cipher Spec, damnit.", TLSError.unexpected_message);
}
_currentReadState = _pendingReadState;
_pendingReadState = null;
// 7.1
}
private function parseApplicationData(p:ByteArray):void {
dispatchEvent(new TLSEvent(TLSEvent.DATA, p));
}
private function handleTLSError(e:TLSError):void {
// basic rules to keep things simple:
// - Make a good faith attempt at notifying peers
// - TLSErrors are always fatal.
close(e);
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -