public Record[] ProcessHandshakeMessages(ProtocolVersion version, HandshakeMessage[] messages, int maxFragmentLength) { List<Record> records = new List<Record>(); // Write all handshake data to memory stream MemoryStream memStream = new MemoryStream(); for (int i = 0; i < messages.Length; i++) { byte[] msgBytes = messages[i].Encode(); memStream.Write(msgBytes, 0, msgBytes.Length); } // Read handshake data from memory stream one at a time memStream.Position = 0; while (memStream.Position < memStream.Length) { long fragmentLength = Math.Min(memStream.Length - memStream.Position, maxFragmentLength); byte[] fragment = new byte[fragmentLength]; memStream.Read(fragment, 0, fragment.Length); Record record = new Record(RecordType.Handshake, version); record.Fragment = fragment; records.Add(record); } return records.ToArray(); }
protected virtual void ProcessClientKeyExchange(HandshakeMessage keyExchange) { InvalidMessageReceived(); }
protected virtual void ProcessNewSessionTicket(HandshakeMessage ticket) { InvalidMessageReceived(); }
protected virtual void ProcessServerHelloDone(HandshakeMessage serverHelloDone) { InvalidMessageReceived(); }
public void ProcessMessage(HandshakeMessage message) { byte[] msgData = message.Encode(); _handshakeStream.Write(msgData, 0, msgData.Length); switch (message.Type) { case HandshakeMessageType.HelloRequest: ProcessHelloRequest(); break; case HandshakeMessageType.ClientHello: ProcessClientHello((HandshakeClientHello)message); break; case HandshakeMessageType.ServerHello: ProcessServerHello((HandshakeServerHello)message); break; case HandshakeMessageType.NewSessionTicket: ProcessNewSessionTicket(message); break; case HandshakeMessageType.Certificate: ProcessCertificate((HandshakeCertificate)message); break; case HandshakeMessageType.ServerKeyExchange: ProcessServerKeyExchange(message); break; case HandshakeMessageType.CertificateRequest: ProcessCertificateRequest((HandshakeCertificateRequest)message); break; case HandshakeMessageType.ServerHelloDone: ProcessServerHelloDone(message); break; case HandshakeMessageType.CertificateVerify: ProcessCertificateVerify(message); break; case HandshakeMessageType.ClientKeyExchange: ProcessClientKeyExchange(message); break; case HandshakeMessageType.Finished: ProcessFinished(message); break; } }
protected void OutputMessage(HandshakeMessage message) { byte[] data = message.Encode(); _handshakeStream.Write(data, 0, data.Length); _outputMessages.Add(message); }
public static HandshakeMessage GetInstance(ProtocolVersion version, byte[] data) { HandshakeMessage ret = null; switch ((HandshakeMessageType) data[0]) { case HandshakeMessageType.HelloRequest: ret = new HandshakeMessage(HandshakeMessageType.HelloRequest, version); break; case HandshakeMessageType.ClientHello: ret = new HandshakeClientHello(); break; case HandshakeMessageType.ServerHello: ret = new HandshakeServerHello(); break; case HandshakeMessageType.HelloVerifyRequest: ret = new HandshakeMessage(HandshakeMessageType.HelloVerifyRequest, version); break; case HandshakeMessageType.NewSessionTicket: ret = new HandshakeMessage(HandshakeMessageType.NewSessionTicket, version); break; case HandshakeMessageType.Certificate: ret = new HandshakeCertificate(version); break; case HandshakeMessageType.ServerKeyExchange: ret = new HandshakeMessage(HandshakeMessageType.ServerKeyExchange, version); break; case HandshakeMessageType.CertificateRequest: ret = new HandshakeCertificateRequest(version); break; case HandshakeMessageType.ServerHelloDone: ret = new HandshakeMessage(HandshakeMessageType.ServerHelloDone, version); break; case HandshakeMessageType.CertificateVerify: ret = new HandshakeMessage(HandshakeMessageType.CertificateVerify, version); break; case HandshakeMessageType.ClientKeyExchange: ret = new HandshakeMessage(HandshakeMessageType.ClientKeyExchange, version); break; case HandshakeMessageType.Finished: ret = new HandshakeMessage(HandshakeMessageType.Finished, version); break; } if (ret != null) { ret.Decode(data); return ret; } return null; }
protected override void ProcessServerKeyExchange(HandshakeMessage keyExchange) { if (_state != HandshakeState.ReceivedServerHello && _state != HandshakeState.ReceivedCertificate) { throw new AlertException(AlertDescription.UnexpectedMessage, "Server key exchange received at the wrong time"); } if (!_cipherSuite.IsAnonymous && _serverCertificates.Count == 0) { throw new AlertException(AlertDescription.HandshakeFailure, "No certificate received even though cipher suite is not anonymous"); } // Process server keys, also returns us the signature data var cert = _serverCertificates[0]; byte[] signature = _cipherSuite.KeyExchangeAlgorithm.ProcessServerKeys(_version, keyExchange.Data, cert); // Extract the signed data from the complete payload byte[] serverKeys = new byte[keyExchange.Data.Length - signature.Length]; Buffer.BlockCopy(keyExchange.Data, 0, serverKeys, 0, serverKeys.Length); // Verify correctness of the signature if (!_cipherSuite.IsAnonymous) { CertificatePublicKey publicKey = new CertificatePublicKey(cert); // Generate the original data signed from server keys byte[] signedData = new byte[_connectionState.ClientRandom.Length + _connectionState.ServerRandom.Length + serverKeys.Length]; Buffer.BlockCopy(_connectionState.ClientRandom, 0, signedData, 0, _connectionState.ClientRandom.Length); Buffer.BlockCopy(_connectionState.ServerRandom, 0, signedData, _connectionState.ClientRandom.Length, _connectionState.ServerRandom.Length); Buffer.BlockCopy(serverKeys, 0, signedData, _connectionState.ClientRandom.Length + _connectionState.ServerRandom.Length, serverKeys.Length); bool signatureOk = VerifySignature(publicKey, signedData, signature); if (!signatureOk) { throw new AlertException(AlertDescription.DecodeError, "Signature from server incorrect"); } } // Wait for certificate request or server hello done _state = HandshakeState.ReceivedServerKeyExchange; }
protected override void ProcessFinished(HandshakeMessage finished) { if (_state != HandshakeState.ReceivedChangeCipherSpec) { throw new AlertException(AlertDescription.UnexpectedMessage, "Finished received at the wrong time"); } byte[] allMessages = _handshakeStream.ToArray(); byte[] handshakeMessages = new byte[allMessages.Length - finished.Encode().Length]; Buffer.BlockCopy(allMessages, 0, handshakeMessages, 0, handshakeMessages.Length); PseudoRandomFunction prf = _cipherSuite.PseudoRandomFunction; HashAlgorithm verifyHash = prf.CreateVerifyHashAlgorithm(_connectionState.MasterSecret); verifyHash.TransformBlock(handshakeMessages, 0, handshakeMessages.Length, handshakeMessages, 0); byte[] ourVerifyData; if (_version == ProtocolVersion.SSL3_0) { byte[] senderBytes = Encoding.ASCII.GetBytes("CLNT"); verifyHash.TransformFinalBlock(senderBytes, 0, senderBytes.Length); ourVerifyData = verifyHash.Hash; } else { verifyHash.TransformFinalBlock(new byte[0], 0, 0); byte[] hash = verifyHash.Hash; int length = _cipherSuite.PseudoRandomFunction.VerifyDataLength; ourVerifyData = _cipherSuite.PseudoRandomFunction.GetBytes(_connectionState.MasterSecret, "client finished", hash, length); } // Verify that the received data matches ours bool verifyOk = false; if (ourVerifyData.Length == finished.Data.Length) { verifyOk = true; for (int i = 0; i < ourVerifyData.Length; i++) { if (ourVerifyData[i] != finished.Data[i]) { verifyOk = false; } } } if (!verifyOk) { throw new AlertException(AlertDescription.DecryptError, "Finished packet contents incorrect"); } _connectionState.ClientVerifyData = ourVerifyData; // Send our own change cipher spec _state = HandshakeState.SendChangeCipherSpec; }
protected override void ProcessCertificateVerify(HandshakeMessage verify) { if (_state != HandshakeState.ReceivedClientKeyExchange) { throw new AlertException(AlertDescription.UnexpectedMessage, "Certificate verify received at the wrong time"); } if (!_clientCertificateReceived) { throw new AlertException(AlertDescription.UnexpectedMessage, "Certificate verify received even though client certificate not received"); } // Get all handshake messages up to, but not including, this one byte[] allMessages = _handshakeStream.ToArray(); byte[] handshakeMessages = new byte[allMessages.Length - verify.Encode().Length]; Buffer.BlockCopy(allMessages, 0, handshakeMessages, 0, handshakeMessages.Length); // Verify the signature of handshake messages CertificatePublicKey publicKey = new CertificatePublicKey(_clientCertificates[0]); bool signatureOk = VerifySignature(publicKey, handshakeMessages, verify.Data); if (!signatureOk) { throw new AlertException(AlertDescription.DecodeError, "Signature from client incorrect"); } // Wait for changecipherspec+finished _state = HandshakeState.ReceivedCertificateVerify; }
protected override void ProcessClientKeyExchange(HandshakeMessage keyExchange) { if (_state != HandshakeState.ReceivedClientHello && _state != HandshakeState.ReceivedCertificate) { throw new AlertException(AlertDescription.UnexpectedMessage, "Client key exchange received at the wrong time"); } if (_state == HandshakeState.ReceivedClientHello && _certificateRequestSent) { // SSLv3 can use NoCertificate warning alert instead if (_version != ProtocolVersion.SSL3_0) { throw new AlertException(AlertDescription.UnexpectedMessage, "Certificate request was sent, but client didn't send a certificate"); } } // Process client keys, they don't contain any signature but might be encrypted _cipherSuite.KeyExchangeAlgorithm.ProcessClientKeys(_version, _clientVersion, _selectedPrivateKey, keyExchange.Data); // Wait for certificate verify or changecipherspec+finished _state = HandshakeState.ReceivedClientKeyExchange; }
protected override void ProcessClientHello(HandshakeClientHello clientHello) { if (_state == HandshakeState.Finished) { throw new AlertException(AlertDescription.NoRenegotiation, "Renegotiation not supported"); } else if (_state != HandshakeState.Initial) { throw new AlertException(AlertDescription.UnexpectedMessage, "Client hello received at the wrong time"); } // Find the compressions that match our supported compressions List<Byte> matchedCompressions = new List<Byte>(); foreach (Byte id in _supportedCompressions) { if (clientHello.CompressionMethods.Contains(id)) { matchedCompressions.Add(id); } } if (matchedCompressions.Count == 0) { throw new AlertException(AlertDescription.HandshakeFailure, "None of the compression methods offered by client is accepted"); } _clientVersion = clientHello.ClientVersion; _cipherSuite = SelectCipherSuite(_pluginManager, _clientVersion, _minVersion, _maxVersion, clientHello.CipherSuites, new List<CipherSuiteId>(_supportedCipherSuites), _certificateSelectionCallback, _availableCertificates); _version = _cipherSuite.ProtocolVersion; // Select the certificate and private key we are going to send int certificateIndex = _certificateSelectionCallback(_cipherSuite, _availableCertificates.ToArray()); if (certificateIndex >= 0 && certificateIndex < _availableCertificates.Count) { _serverCertificates.AddRange(_availableCertificates[certificateIndex]); _selectedPrivateKey = _availablePrivateKeys[certificateIndex]; } // TODO: Create the server hello message correctly HandshakeServerHello serverHello = new HandshakeServerHello(_version); serverHello.CipherSuite = (CipherSuiteId)_cipherSuite.CipherSuiteID; serverHello.CompressionMethod = matchedCompressions[0]; OutputMessage(serverHello); // Initialize the handshake randoms and cipher suite _connectionState.ClientRandom = clientHello.Random.GetBytes(); _connectionState.ServerRandom = serverHello.Random.GetBytes(); // TODO: If we resumed our session, set state to SendChangeCipherSpec and return // Send certificate if required and valid if (!_cipherSuite.IsAnonymous) { if (_serverCertificates.Count == 0) { throw new AlertException(AlertDescription.HandshakeFailure, "Certificate required but not selected"); } string keyexAlg = _cipherSuite.KeyExchangeAlgorithm.CertificateKeyAlgorithm; string signAlg = _cipherSuite.SignatureAlgorithm.CertificateKeyAlgorithm; X509Certificate cert = _serverCertificates[0]; if (keyexAlg != null && !keyexAlg.Equals(cert.GetKeyAlgorithm())) { throw new AlertException(AlertDescription.HandshakeFailure, "Selected certificate type " + cert.GetKeyAlgorithm() + " doesn't match key exchange type " + keyexAlg); } if (signAlg != null && !signAlg.Equals(cert.GetKeyAlgorithm())) { throw new AlertException(AlertDescription.HandshakeFailure, "Selected certificate type " + cert.GetKeyAlgorithm() + " doesn't match signature type " + signAlg); } HandshakeCertificate certificate = new HandshakeCertificate(_version); certificate.CertificateList.AddRange(_serverCertificates); OutputMessage(certificate); } byte[] serverKeys = _cipherSuite.KeyExchangeAlgorithm.GetServerKeys(_maxVersion, _selectedPrivateKey); if (serverKeys != null) { // Generate the signature for server keys byte[] dataToSign = new byte[_connectionState.ClientRandom.Length + _connectionState.ServerRandom.Length + serverKeys.Length]; Buffer.BlockCopy(_connectionState.ClientRandom, 0, dataToSign, 0, _connectionState.ClientRandom.Length); Buffer.BlockCopy(_connectionState.ServerRandom, 0, dataToSign, _connectionState.ClientRandom.Length, _connectionState.ServerRandom.Length); Buffer.BlockCopy(serverKeys, 0, dataToSign, _connectionState.ClientRandom.Length + _connectionState.ServerRandom.Length, serverKeys.Length); byte[] signature = GenerateSignature(_selectedPrivateKey, dataToSign); Log.Debug("Server Signature: " + BitConverter.ToString(signature)); // Combine the server keys with the signature byte[] signedKeys = new byte[serverKeys.Length + signature.Length]; Buffer.BlockCopy(serverKeys, 0, signedKeys, 0, serverKeys.Length); Buffer.BlockCopy(signature, 0, signedKeys, serverKeys.Length, signature.Length); HandshakeMessage keyex = new HandshakeMessage(HandshakeMessageType.ServerKeyExchange, _version, signedKeys); OutputMessage(keyex); } // Send certificate request if needed if (!_cipherSuite.IsAnonymous && _certificateRequest != null) { OutputMessage(_certificateRequest); _certificateRequestSent = true; } HandshakeMessage serverHelloDone = new HandshakeMessage(HandshakeMessageType.ServerHelloDone, _version); OutputMessage(serverHelloDone); // Wait for client certificate or client key exchange _state = HandshakeState.ReceivedClientHello; }
protected override void ProcessServerHelloDone(HandshakeMessage serverHelloDone) { if (_state != HandshakeState.ReceivedCertificate && _state != HandshakeState.ReceivedServerKeyExchange && _state != HandshakeState.ReceivedCertificateRequest) { throw new AlertException(AlertDescription.UnexpectedMessage, "Server hello done received at the wrong time"); } bool clientCertificateSent = false; if (_clientCertificateRequested) { // Ask for correct certificate from the callback int certificateIndex = _certificateSelectionCallback(_availableCertificates.ToArray(), _serverCertificates); if (certificateIndex >= 0 && certificateIndex < _availableCertificates.Count) { _clientCertificates.AddRange(_availableCertificates[certificateIndex]); _selectedPrivateKey = _availablePrivateKeys[certificateIndex]; } // If certificate was selected, send it to server if (_clientCertificates.Count > 0) { HandshakeCertificate certificate = new HandshakeCertificate(_version); certificate.CertificateList.AddRange(_clientCertificates); OutputMessage(certificate); clientCertificateSent = true; } else { // TODO: In case of SSLv3 we should send warning alert instead? HandshakeCertificate certificate = new HandshakeCertificate(_version); OutputMessage(certificate); } } // Send client key exchange message byte[] clientKeys = null; if (_serverCertificates.Count > 0) { var cert = _serverCertificates[0]; CertificatePublicKey publicKey = new CertificatePublicKey(cert); this.logger?.Trace($"OID (lookup with http://www.oid-info.com/get/[oid]) of key from server certificate: {publicKey.Oid}"); var data = Asn1Object.FromByteArray(cert.GetKeyAlgorithmParameters()); this.logger?.Trace($" OID of key parameters from server certificate: {data}"); clientKeys = _cipherSuite.KeyExchangeAlgorithm.GetClientKeys(_version, _maxVersion, publicKey); } HandshakeMessage keyex = new HandshakeMessage(HandshakeMessageType.ClientKeyExchange, _version, clientKeys); OutputMessage(keyex); if (clientCertificateSent) { // Get all handshake messages byte[] handshakeMessages = _handshakeStream.ToArray(); // FIXME: Generate the signature of handshakeMessages with client cert signature // this breaks if client certificate signature is different type than negotiated byte[] signature = GenerateSignature(_selectedPrivateKey, handshakeMessages); // Create CertificateVerify message and send it to server HandshakeMessage verify = new HandshakeMessage(HandshakeMessageType.CertificateVerify, _version, signature); OutputMessage(verify); } // Generate the master secret from key exchange _connectionState.MasterSecret = GenerateMasterSecret(_version, _cipherSuite, _connectionState); // Wait for changecipherspec+finished _state = HandshakeState.SendChangeCipherSpec; }
protected virtual void ProcessCertificateVerify(HandshakeMessage verify) { InvalidMessageReceived(); }
protected override void ProcessLocalChangeCipherSpec() { if (_state != HandshakeState.SendChangeCipherSpec) { throw new AlertException(AlertDescription.UnexpectedMessage, "Change cipher spec not after server hello done"); } byte[] handshakeMessages = _handshakeStream.ToArray(); PseudoRandomFunction prf = _cipherSuite.PseudoRandomFunction; HashAlgorithm verifyHash = prf.CreateVerifyHashAlgorithm(_connectionState.MasterSecret); verifyHash.TransformBlock(handshakeMessages, 0, handshakeMessages.Length, handshakeMessages, 0); byte[] verifyData; if (_version == ProtocolVersion.SSL3_0) { byte[] senderBytes = Encoding.ASCII.GetBytes("SRVR"); verifyHash.TransformFinalBlock(senderBytes, 0, senderBytes.Length); verifyData = verifyHash.Hash; } else { verifyHash.TransformFinalBlock(new byte[0], 0, 0); byte[] hash = verifyHash.Hash; int length = _cipherSuite.PseudoRandomFunction.VerifyDataLength; verifyData = _cipherSuite.PseudoRandomFunction.GetBytes(_connectionState.MasterSecret, "server finished", hash, length); } _connectionState.ServerVerifyData = verifyData; HandshakeMessage finished = new HandshakeMessage(HandshakeMessageType.Finished, _version, verifyData); OutputMessage(finished); // Cleanup the handshake stream used for verify hashes _handshakeStream = new MemoryStream(); // Everything done and handshake finished _state = HandshakeState.Finished; }
protected virtual void ProcessFinished(HandshakeMessage finished) { InvalidMessageReceived(); }
public static HandshakeMessage GetInstance(ProtocolVersion version, byte[] data) { HandshakeMessage ret = null; switch ((HandshakeMessageType)data[0]) { case HandshakeMessageType.HelloRequest: ret = new HandshakeMessage(HandshakeMessageType.HelloRequest, version); break; case HandshakeMessageType.ClientHello: ret = new HandshakeClientHello(); break; case HandshakeMessageType.ServerHello: ret = new HandshakeServerHello(); break; case HandshakeMessageType.HelloVerifyRequest: ret = new HandshakeMessage(HandshakeMessageType.HelloVerifyRequest, version); break; case HandshakeMessageType.NewSessionTicket: ret = new HandshakeMessage(HandshakeMessageType.NewSessionTicket, version); break; case HandshakeMessageType.Certificate: ret = new HandshakeCertificate(version); break; case HandshakeMessageType.ServerKeyExchange: ret = new HandshakeMessage(HandshakeMessageType.ServerKeyExchange, version); break; case HandshakeMessageType.CertificateRequest: ret = new HandshakeCertificateRequest(version); break; case HandshakeMessageType.ServerHelloDone: ret = new HandshakeMessage(HandshakeMessageType.ServerHelloDone, version); break; case HandshakeMessageType.CertificateVerify: ret = new HandshakeMessage(HandshakeMessageType.CertificateVerify, version); break; case HandshakeMessageType.ClientKeyExchange: ret = new HandshakeMessage(HandshakeMessageType.ClientKeyExchange, version); break; case HandshakeMessageType.Finished: ret = new HandshakeMessage(HandshakeMessageType.Finished, version); break; } if (ret != null) { ret.Decode(data); return(ret); } return(null); }