protected override SslHandshakeStatus ProcessMessage(HandshakeMessage message) { // throws SslExceptions SslHandshakeStatus ret = new SslHandshakeStatus(); switch(message.type) { case HandshakeType.ServerHello: ret = ProcessServerHello(message); break; case HandshakeType.Certificate: // optional ret = ProcessCertificate(message, true); break; case HandshakeType.ServerKeyExchange: // optional ret = ProcessServerKeyExchange(message); break; case HandshakeType.CertificateRequest: // optional ret = ProcessCertificateRequest(message); break; case HandshakeType.ServerHelloDone: ret = ProcessServerHelloDone(message); break; case HandshakeType.Finished: ret = ProcessFinished(message); break; case HandshakeType.HelloRequest: ret = ProcessHelloRequest(message); break; default: throw new SslException(AlertDescription.UnexpectedMessage, "The received message was not expected from a server."); } return ret; }
protected override byte[] GetFinishedMessage() { HandshakeMessage hm = new HandshakeMessage(HandshakeType.Finished, new byte[36]); Ssl3HandshakeMac md5 = new Ssl3HandshakeMac(HashType.MD5, m_LocalMD5Hash, m_MasterSecret); Ssl3HandshakeMac sha1 = new Ssl3HandshakeMac(HashType.SHA1, m_LocalSHA1Hash, m_MasterSecret); md5.TransformFinalBlock(new byte[]{0x53, 0x52, 0x56, 0x52}, 0, 4); sha1.TransformFinalBlock(new byte[]{0x53, 0x52, 0x56, 0x52}, 0, 4); Array.Copy(md5.Hash, 0, hm.fragment, 0, 16); Array.Copy(sha1.Hash, 0, hm.fragment, 16, 20); md5.Clear(); sha1.Clear(); return hm.ToBytes(); }
protected override byte[] GetFinishedMessage() { HandshakeMessage hm = new HandshakeMessage(HandshakeType.Finished, null); byte[] hash = new byte[36]; m_LocalMD5Hash.TransformFinalBlock(new byte[0], 0, 0); m_LocalSHA1Hash.TransformFinalBlock(new byte[0], 0, 0); Array.Copy(m_LocalMD5Hash.Hash, 0, hash, 0, 16); Array.Copy(m_LocalSHA1Hash.Hash, 0, hash, 16, 20); PseudoRandomDeriveBytes prf = new PseudoRandomDeriveBytes(m_MasterSecret, "client finished", hash); hm.fragment = prf.GetBytes(12); prf.Dispose(); return hm.ToBytes(); }
protected override byte[] GetFinishedMessage() { byte[] temp, hash = new byte[36]; m_LocalMD5Hash.TransformFinalBlock(new byte[0], 0, 0); m_LocalSHA1Hash.TransformFinalBlock(new byte[0], 0, 0); Array.Copy(m_LocalMD5Hash.Hash, 0, hash, 0, 16); Array.Copy(m_LocalSHA1Hash.Hash, 0, hash, 16, 20); PseudoRandomDeriveBytes prf = new PseudoRandomDeriveBytes(m_MasterSecret, "server finished", hash); HandshakeMessage hm = new HandshakeMessage(HandshakeType.Finished, prf.GetBytes(12)); temp = hm.ToBytes(); prf.Dispose(); return temp; }
protected override SslHandshakeStatus ProcessMessage(HandshakeMessage message) { // throws SslExceptions SslHandshakeStatus ret; switch(message.type) { case HandshakeType.ClientHello: ret = ProcessClientHello(message); break; case HandshakeType.Certificate: // optional ret = ProcessCertificate(message, false); break; case HandshakeType.ClientKeyExchange: ret = ProcessClientKeyExchange(message); break; case HandshakeType.CertificateVerify: // optional ret = ProcessCertificateVerify(message); break; case HandshakeType.Finished: ret = ProcessFinished(message); break; default: throw new SslException(AlertDescription.UnexpectedMessage, "The received message was not expected from a client."); } return ret; }
protected SslHandshakeStatus ProcessServerHelloDone(HandshakeMessage message) { if (m_State != HandshakeType.Certificate && m_State != HandshakeType.ServerKeyExchange && m_State != HandshakeType.CertificateRequest) { throw new SslException(AlertDescription.UnexpectedMessage, "ServerHello message must be preceded by a ClientHello message."); } if (message.fragment.Length != 0) { throw new SslException(AlertDescription.IllegalParameter, "The ServerHelloDone message is invalid."); } UpdateHashes(message, HashUpdate.All); // input message MemoryStream ms = new MemoryStream(); HandshakeMessage hm = new HandshakeMessage(HandshakeType.ClientKeyExchange, null); byte[] buffer; // send Certificate [optional] if (m_MutualAuthentication) { hm.type = HandshakeType.Certificate; hm.fragment = GetCertificateBytes(m_Options.Certificate); buffer = m_RecordLayer.EncryptBytes(hm.ToBytes(), 0, hm.fragment.Length + 4, ContentType.Handshake); ms.Write(buffer, 0, buffer.Length); UpdateHashes(hm, HashUpdate.All); // output message } // send ClientKeyExchange if (m_KeyCipher == null) { m_KeyCipher = (RSACryptoServiceProvider)m_RemoteCertificate.PublicKey; } RSAKeyTransform kf = new RSAKeyTransform(m_KeyCipher); byte[] preMasterSecret = new byte[48]; m_RNG.GetBytes(preMasterSecret); ProtocolVersion pv = CompatibilityLayer.GetMaxProtocol(m_Options.Protocol); preMasterSecret[0] = pv.major; preMasterSecret[1] = pv.minor; buffer = kf.CreateKeyExchange(preMasterSecret); // public-key-encrypt the preMasterSecret hm.type = HandshakeType.ClientKeyExchange; if (GetProtocol() == SecureProtocol.Ssl3) // SSL { hm.fragment = buffer; } else // TLS { hm.fragment = new byte[buffer.Length + 2]; Array.Copy(buffer, 0, hm.fragment, 2, buffer.Length); hm.fragment[0] = (byte)(buffer.Length / 256); // prepend the length of the preMasterSecret hm.fragment[1] = (byte)(buffer.Length % 256); } GenerateCiphers(preMasterSecret); // generate the local ciphers buffer = m_RecordLayer.EncryptBytes(hm.ToBytes(), 0, hm.fragment.Length + 4, ContentType.Handshake); ms.Write(buffer, 0, buffer.Length); UpdateHashes(hm, HashUpdate.All); // output message m_KeyCipher.Clear(); m_KeyCipher = null; // send CertificateVerify [optional] if (m_MutualAuthentication && m_Options.Certificate != null) { m_CertSignHash.MasterKey = this.m_MasterSecret; m_CertSignHash.TransformFinalBlock(buffer, 0, 0); // finalize hash buffer = m_CertSignHash.CreateSignature(m_Options.Certificate); hm.type = HandshakeType.CertificateVerify; hm.fragment = new byte[buffer.Length + 2]; hm.fragment[0] = (byte)(buffer.Length / 256); hm.fragment[1] = (byte)(buffer.Length % 256); Array.Copy(buffer, 0, hm.fragment, 2, buffer.Length); buffer = m_RecordLayer.EncryptBytes(hm.ToBytes(), 0, hm.fragment.Length + 4, ContentType.Handshake); ms.Write(buffer, 0, buffer.Length); UpdateHashes(hm, HashUpdate.LocalRemote); // output message } // send ChangeCipherSpec buffer = m_RecordLayer.EncryptBytes(new byte[] { 1 }, 0, 1, ContentType.ChangeCipherSpec); ms.Write(buffer, 0, buffer.Length); m_RecordLayer.ChangeLocalState(null, m_CipherSuite.Encryptor, m_CipherSuite.LocalHasher); // send Finished buffer = GetFinishedMessage(); UpdateHashes(buffer, HashUpdate.Remote); // output message buffer = m_RecordLayer.EncryptBytes(buffer, 0, buffer.Length, ContentType.Handshake); ms.Write(buffer, 0, buffer.Length); // send empty record [http://www.openssl.org/~bodo/tls-cbc.txt] if (this.m_CipherSuite.Encryptor.OutputBlockSize != 1) // is bulk cipher? { if (((int)m_Options.Flags & (int)SecurityFlags.DontSendEmptyRecord) == 0) { byte[] empty = m_RecordLayer.EncryptBytes(new byte[0], 0, 0, ContentType.ApplicationData); ms.Write(empty, 0, empty.Length); } } // finalize buffer = ms.ToArray(); ms.Close(); return(new SslHandshakeStatus(SslStatus.ContinueNeeded, buffer)); }
protected SslHandshakeStatus GetClientHelloResult() { MemoryStream retMessage = new MemoryStream(); SslHandshakeStatus ret = new SslHandshakeStatus(); HandshakeMessage temp; byte[] bytes; // ServerHello message temp = new HandshakeMessage(HandshakeType.ServerHello, new byte[38]); m_ServerTime = GetUnixTime(); m_ServerRandom = new byte[28]; m_RNG.GetBytes(m_ServerRandom); temp.fragment[0] = GetVersion().major; temp.fragment[1] = GetVersion().minor; Array.Copy(m_ServerTime, 0, temp.fragment, 2, 4); Array.Copy(m_ServerRandom, 0, temp.fragment, 6, 28); temp.fragment[34] = 0; // do not resume a session, and do not let the other side cache it Array.Copy(CipherSuites.GetCipherAlgorithmBytes(m_EncryptionScheme), 0, temp.fragment, 35, 2); temp.fragment[37] = CompressionAlgorithm.GetAlgorithmByte(m_CompressionMethod); bytes = temp.ToBytes(); retMessage.Write(bytes, 0, bytes.Length); // Certificate message byte[] certs = GetCertificateList(m_Options.Certificate); temp.type = HandshakeType.Certificate; temp.fragment = certs; bytes = temp.ToBytes(); retMessage.Write(bytes, 0, bytes.Length); // ServerKeyExchange message [optional] => only with RSA_EXPORT and public key > 512 bits if (m_Options.Certificate.GetPublicKeyLength() > 512 && CipherSuites.GetCipherDefinition(m_EncryptionScheme).Exportable) { MemoryStream kes = new MemoryStream(); MD5SHA1CryptoServiceProvider mscsp = new MD5SHA1CryptoServiceProvider(); // hash the client and server random values mscsp.TransformBlock(m_ClientTime, 0, 4, m_ClientTime, 0); mscsp.TransformBlock(m_ClientRandom, 0, 28, m_ClientRandom, 0); mscsp.TransformBlock(m_ServerTime, 0, 4, m_ServerTime, 0); mscsp.TransformBlock(m_ServerRandom, 0, 28, m_ServerRandom, 0); // create a new 512 bit RSA key m_KeyCipher = new RSACryptoServiceProvider(512); RSAParameters p = m_KeyCipher.ExportParameters(false); // write the key parameters to the output stream bytes = new byte[]{(byte)(p.Modulus.Length / 256), (byte)(p.Modulus.Length % 256)}; kes.Write(bytes, 0, 2); kes.Write(p.Modulus, 0, p.Modulus.Length); mscsp.TransformBlock(bytes, 0, 2, bytes, 0); mscsp.TransformBlock(p.Modulus, 0, p.Modulus.Length, p.Modulus, 0); bytes = new byte[]{(byte)(p.Exponent.Length / 256), (byte)(p.Exponent.Length % 256)}; kes.Write(bytes, 0, 2); kes.Write(p.Exponent, 0, p.Exponent.Length); mscsp.TransformBlock(bytes, 0, 2, bytes, 0); mscsp.TransformFinalBlock(p.Exponent, 0, p.Exponent.Length); // create signature bytes = mscsp.CreateSignature(m_Options.Certificate); kes.Write(new byte[]{(byte)(bytes.Length / 256), (byte)(bytes.Length % 256)}, 0, 2); kes.Write(bytes, 0, bytes.Length); // write to output temp.type = HandshakeType.ServerKeyExchange; temp.fragment = kes.ToArray(); bytes = temp.ToBytes(); retMessage.Write(bytes, 0, bytes.Length); kes.Close(); } else { m_KeyCipher = (RSACryptoServiceProvider)m_Options.Certificate.PrivateKey; } // CertificateRequest message [optional] if (m_MutualAuthentication) { bytes = GetDistinguishedNames(); if (bytes.Length != 0) { // make sure at least one certificate is returned temp.type = HandshakeType.CertificateRequest; temp.fragment = new byte[bytes.Length + 4]; temp.fragment[0] = 1; // one certificate type supported temp.fragment[1] = 1; // cert type RSA temp.fragment[2] = (byte)(bytes.Length / 256); temp.fragment[3] = (byte)(bytes.Length % 256); Array.Copy(bytes, 0, temp.fragment, 4, bytes.Length); bytes = temp.ToBytes(); retMessage.Write(bytes, 0, bytes.Length); } } // ServerHelloDone message temp.type = HandshakeType.ServerHelloDone; temp.fragment = new byte[0]; bytes = temp.ToBytes(); retMessage.Write(bytes, 0, bytes.Length); // final adjustments ret.Status = SslStatus.ContinueNeeded; ret.Message = retMessage.ToArray(); retMessage.Close(); UpdateHashes(ret.Message, HashUpdate.All); // output message ret.Message = m_RecordLayer.EncryptBytes(ret.Message, 0, ret.Message.Length, ContentType.Handshake); return ret; }
protected SslHandshakeStatus ProcessCertificateRequest(HandshakeMessage message) { if (m_State == HandshakeType.ServerKeyExchange) { CipherDefinition cd = CipherSuites.GetCipherDefinition(m_EncryptionScheme); if (this.m_RemoteCertificate.GetPublicKeyLength() <= 512 || !cd.Exportable) { throw new SslException(AlertDescription.HandshakeFailure, "Invalid message."); } } else if (m_State != HandshakeType.Certificate) { throw new SslException(AlertDescription.UnexpectedMessage, "CertificateRequest message must be preceded by a Certificate or ServerKeyExchange message."); } UpdateHashes(message, HashUpdate.All); // input message // get supported certificate types bool supportsRsaCerts = false; byte[] certTypes = new byte[message.fragment[0]]; // currently we're not doing anything with the supported certificate types Array.Copy(message.fragment, 1, certTypes, 0, certTypes.Length); for (int i = 0; i < certTypes.Length; i++) { if (certTypes[i] == 1) // rsa_sign { supportsRsaCerts = true; break; } } // get list of distinguished names if (m_Options.RequestHandler != null && supportsRsaCerts) // make sure the client passed a delegate { Queue q = new Queue(); DistinguishedNameList r = new DistinguishedNameList(); int size, offset = message.fragment[0] + 3; byte[] buffer; while (offset < message.fragment.Length) { size = message.fragment[offset] * 256 + message.fragment[offset + 1]; buffer = new byte[size]; Array.Copy(message.fragment, offset + 2, buffer, 0, size); q.Enqueue(buffer); offset += size + 2; } // decode RDN structures while (q.Count > 0) { r.Add(ProcessName((byte[])q.Dequeue())); } RequestEventArgs e = new RequestEventArgs(); try { m_Options.RequestHandler(Parent, r, e); if (e.Certificate != null) { m_Options.Certificate = e.Certificate; } } catch (Exception de) { throw new SslException(de, AlertDescription.InternalError, "The code in the CertRequestEventHandler delegate threw an error."); } } if (!supportsRsaCerts) { m_Options.Certificate = null; // do not send client certificate } m_MutualAuthentication = true; return(new SslHandshakeStatus(SslStatus.MessageIncomplete, null)); }
protected SslHandshakeStatus ProcessClientHello(HandshakeMessage message) { if (m_State != HandshakeType.Nothing && m_State != HandshakeType.Finished) throw new SslException(AlertDescription.UnexpectedMessage, "ClientHello message must be the first message or must be preceded by a Finished message."); m_IsNegotiating = true; UpdateHashes(message, HashUpdate.All); // input message // process ClientHello ProtocolVersion pv = new ProtocolVersion(message.fragment[0], message.fragment[1]); m_MaxClientVersion = pv; if (CompatibilityLayer.SupportsProtocol(m_Options.Protocol, pv) && pv.GetVersionInt() != GetVersion().GetVersionInt()) throw new SslException(AlertDescription.IllegalParameter, "Unknown protocol version of the client."); try { // extract the time from the client [== 1 uint] m_ClientTime = new byte[4]; Array.Copy(message.fragment, 2, m_ClientTime, 0, 4); // extract the random bytes [== 28 bytes] m_ClientRandom = new byte[28]; Array.Copy(message.fragment, 6, m_ClientRandom, 0, 28); // extact the session ID [== 0..32 bytes] int length = message.fragment[34]; if (length > 32) throw new SslException(AlertDescription.IllegalParameter, "The length of the SessionID cannot be more than 32 bytes."); m_SessionID = new byte[length]; Array.Copy(message.fragment, 35, m_SessionID, 0, length); // extract the available cipher suites length += 35; int ciphers_size = message.fragment[length] * 256 + message.fragment[length + 1]; if (ciphers_size < 2 || ciphers_size % 2 != 0) throw new SslException(AlertDescription.IllegalParameter, "The number of ciphers is invalid -or- the cipher length is not even."); byte[] ciphers = new byte[ciphers_size]; Array.Copy(message.fragment, length + 2, ciphers, 0, ciphers_size); m_EncryptionScheme = CipherSuites.GetCipherSuiteAlgorithm(ciphers, m_Options.AllowedAlgorithms); // extract the available compression algorithms length += ciphers_size + 2; int compressors_size = message.fragment[length]; if (compressors_size == 0) throw new SslException(AlertDescription.IllegalParameter, "No compressor specified."); byte[] compressors = new byte[compressors_size]; Array.Copy(message.fragment, length + 1, compressors, 0, compressors_size); m_CompressionMethod = CompressionAlgorithm.GetCompressionAlgorithm(compressors, m_Options.AllowedAlgorithms); } catch (Exception e) { throw new SslException(e, AlertDescription.InternalError, "The message is invalid."); } // create reply return GetClientHelloResult(); }
protected SslHandshakeStatus ProcessHelloRequest(HandshakeMessage message) { if (IsNegotiating()) return new SslHandshakeStatus(SslStatus.OK, null); // ignore hello request return new SslHandshakeStatus(SslStatus.ContinueNeeded, GetClientHello()); }
protected SslHandshakeStatus ProcessServerHelloDone(HandshakeMessage message) { if (m_State != HandshakeType.Certificate && m_State != HandshakeType.ServerKeyExchange && m_State != HandshakeType.CertificateRequest) throw new SslException(AlertDescription.UnexpectedMessage, "ServerHello message must be preceded by a ClientHello message."); if (message.fragment.Length != 0) throw new SslException(AlertDescription.IllegalParameter, "The ServerHelloDone message is invalid."); UpdateHashes(message, HashUpdate.All); // input message MemoryStream ms = new MemoryStream(); HandshakeMessage hm = new HandshakeMessage(HandshakeType.ClientKeyExchange, null); byte[] buffer; // send Certificate [optional] if (m_MutualAuthentication) { hm.type = HandshakeType.Certificate; hm.fragment = GetCertificateBytes(m_Options.Certificate); buffer = m_RecordLayer.EncryptBytes(hm.ToBytes(), 0, hm.fragment.Length + 4, ContentType.Handshake); ms.Write(buffer, 0, buffer.Length); UpdateHashes(hm, HashUpdate.All); // output message } // send ClientKeyExchange if (m_KeyCipher == null) m_KeyCipher = (RSACryptoServiceProvider)m_RemoteCertificate.PublicKey; RSAKeyTransform kf = new RSAKeyTransform(m_KeyCipher); byte[] preMasterSecret = new byte[48]; m_RNG.GetBytes(preMasterSecret); ProtocolVersion pv = CompatibilityLayer.GetMaxProtocol(m_Options.Protocol); preMasterSecret[0] = pv.major; preMasterSecret[1] = pv.minor; buffer = kf.CreateKeyExchange(preMasterSecret); // public-key-encrypt the preMasterSecret hm.type = HandshakeType.ClientKeyExchange; if (GetProtocol() == SecureProtocol.Ssl3) { // SSL hm.fragment = buffer; } else { // TLS hm.fragment = new byte[buffer.Length + 2]; Buffer.BlockCopy(buffer, 0, hm.fragment, 2, buffer.Length); hm.fragment[0] = (byte)(buffer.Length / 256); // prepend the length of the preMasterSecret hm.fragment[1] = (byte)(buffer.Length % 256); } GenerateCiphers(preMasterSecret); // generate the local ciphers buffer = m_RecordLayer.EncryptBytes(hm.ToBytes(), 0, hm.fragment.Length + 4, ContentType.Handshake); ms.Write(buffer, 0, buffer.Length); UpdateHashes(hm, HashUpdate.All); // output message m_KeyCipher.Clear(); m_KeyCipher = null; // send CertificateVerify [optional] if (m_MutualAuthentication && m_Options.Certificate != null) { m_CertSignHash.MasterKey = this.m_MasterSecret; m_CertSignHash.TransformFinalBlock(buffer, 0, 0); // finalize hash buffer = m_CertSignHash.CreateSignature(m_Options.Certificate); hm.type = HandshakeType.CertificateVerify; hm.fragment = new byte[buffer.Length + 2]; hm.fragment[0] = (byte)(buffer.Length / 256); hm.fragment[1] = (byte)(buffer.Length % 256); Buffer.BlockCopy(buffer, 0, hm.fragment, 2, buffer.Length); buffer = m_RecordLayer.EncryptBytes(hm.ToBytes(), 0, hm.fragment.Length + 4, ContentType.Handshake); ms.Write(buffer, 0, buffer.Length); UpdateHashes(hm, HashUpdate.LocalRemote); // output message } // send ChangeCipherSpec buffer = m_RecordLayer.EncryptBytes(new byte[]{1}, 0, 1, ContentType.ChangeCipherSpec); ms.Write(buffer, 0, buffer.Length); m_RecordLayer.ChangeLocalState(null, m_CipherSuite.Encryptor, m_CipherSuite.LocalHasher); //ExtAnswers if (this.serverHelloExts != null) { foreach (var extension in this.serverHelloExts) { var responseMessage = extension.GetExtensionResponseMessage(); if (responseMessage != null) { buffer = m_RecordLayer.EncryptBytes(responseMessage.ToBytes(), 0, responseMessage.fragment.Length + 4, ContentType.Handshake); ms.Write(buffer, 0, buffer.Length); UpdateHashes(responseMessage, HashUpdate.LocalRemote); // output message } } } // send Finished buffer = GetFinishedMessage(); UpdateHashes(buffer, HashUpdate.Remote); // output message buffer = m_RecordLayer.EncryptBytes(buffer, 0, buffer.Length, ContentType.Handshake); ms.Write(buffer, 0, buffer.Length); // send empty record [http://www.openssl.org/~bodo/tls-cbc.txt] if (this.m_CipherSuite.Encryptor.OutputBlockSize != 1) { // is bulk cipher? if (((int)m_Options.Flags & (int)SecurityFlags.DontSendEmptyRecord) == 0) { byte[] empty = m_RecordLayer.EncryptBytes(new byte[0], 0, 0, ContentType.ApplicationData); ms.Write(empty, 0, empty.Length); } } // finalize buffer = ms.ToArray(); ms.Close(); return new SslHandshakeStatus(SslStatus.ContinueNeeded, buffer); }
protected void UpdateHashes(HandshakeMessage message, HashUpdate update) { byte[] header = new byte[4]; header[0] = (byte)message.type; header[1] = (byte)(message.fragment.Length / 65536); header[2] = (byte)((message.fragment.Length % 65536) / 256); header[3] = (byte)(message.fragment.Length % 256); UpdateHashes(header, update); UpdateHashes(message.fragment, update); }
protected SslHandshakeStatus ProcessCertificateRequest(HandshakeMessage message) { if (m_State == HandshakeType.ServerKeyExchange) { CipherDefinition cd = CipherSuites.GetCipherDefinition(m_EncryptionScheme); if (this.m_RemoteCertificate.GetPublicKeyLength() <= 512 || !cd.Exportable) throw new SslException(AlertDescription.HandshakeFailure, "Invalid message."); } else if (m_State != HandshakeType.Certificate) { throw new SslException(AlertDescription.UnexpectedMessage, "CertificateRequest message must be preceded by a Certificate or ServerKeyExchange message."); } UpdateHashes(message, HashUpdate.All); // input message // get supported certificate types bool supportsRsaCerts = false; byte[] certTypes = new byte[message.fragment[0]]; // currently we're not doing anything with the supported certificate types Buffer.BlockCopy(message.fragment, 1, certTypes, 0, certTypes.Length); for(int i = 0; i < certTypes.Length; i++) { if (certTypes[i] == 1) { // rsa_sign supportsRsaCerts = true; break; } } // get list of distinguished names if (m_Options.RequestHandler != null && supportsRsaCerts) { // make sure the client passed a delegate Queue q = new Queue(); DistinguishedNameList r = new DistinguishedNameList(); int size, offset = message.fragment[0] + 3; byte[] buffer; while(offset < message.fragment.Length) { size = message.fragment[offset] * 256 + message.fragment[offset + 1]; buffer = new byte[size]; Buffer.BlockCopy(message.fragment, offset + 2, buffer, 0, size); q.Enqueue(buffer); offset += size + 2; } // decode RDN structures while(q.Count > 0) { r.Add(ProcessName((byte[])q.Dequeue())); } RequestEventArgs e = new RequestEventArgs(); try { m_Options.RequestHandler(Parent, r, e); if (e.Certificate != null) m_Options.Certificate = e.Certificate; } catch (Exception de) { throw new SslException(de, AlertDescription.InternalError, "The code in the CertRequestEventHandler delegate threw an error."); } } if (!supportsRsaCerts) m_Options.Certificate = null; // do not send client certificate m_MutualAuthentication = true; return new SslHandshakeStatus(SslStatus.MessageIncomplete, null); }
protected SslHandshakeStatus ProcessCertificate(HandshakeMessage message, bool client) { if (client) { if (m_State != HandshakeType.ServerHello) throw new SslException(AlertDescription.UnexpectedMessage, "Certificate message must be preceded by a ServerHello message."); } else { // server if (m_State != HandshakeType.ClientHello) throw new SslException(AlertDescription.UnexpectedMessage, "Certificate message must be preceded by a ClientHello message."); } UpdateHashes(message, HashUpdate.All); // input message Certificate[] certs = null; try { certs = ParseCertificateList(message.fragment); if (certs.Length == 0) { if (!m_MutualAuthentication) return new SslHandshakeStatus(SslStatus.MessageIncomplete, null); } } catch (SslException t) { throw t; } catch (Exception f) { throw new SslException(f, AlertDescription.InternalError, "The Certificate message is invalid."); } CertificateChain chain = null; m_RemoteCertificate = null; if (certs.Length != 0) { m_RemoteCertificate = certs[0]; if (m_RemoteCertificate.GetPublicKeyLength() < 512) { throw new SslException(AlertDescription.HandshakeFailure, "The pulic key should be at least 512 bits."); } CertificateStore cs = new CertificateStore(certs); for(int i = 0; i < certs.Length; i++) { certs[i].Store = cs; } chain = new CertificateChain(m_RemoteCertificate, cs); } VerifyChain(chain, client); return new SslHandshakeStatus(SslStatus.MessageIncomplete, null); }
protected SslHandshakeStatus GetClientHelloResult() { MemoryStream retMessage = new MemoryStream(); SslHandshakeStatus ret = new SslHandshakeStatus(); HandshakeMessage temp; byte[] bytes; // ServerHello message temp = new HandshakeMessage(HandshakeType.ServerHello, new byte[38]); m_ServerTime = GetUnixTime(); m_ServerRandom = new byte[28]; m_RNG.GetBytes(m_ServerRandom); temp.fragment[0] = GetVersion().major; temp.fragment[1] = GetVersion().minor; Array.Copy(m_ServerTime, 0, temp.fragment, 2, 4); Array.Copy(m_ServerRandom, 0, temp.fragment, 6, 28); temp.fragment[34] = 0; // do not resume a session, and do not let the other side cache it Array.Copy(CipherSuites.GetCipherAlgorithmBytes(m_EncryptionScheme), 0, temp.fragment, 35, 2); temp.fragment[37] = CompressionAlgorithm.GetAlgorithmByte(m_CompressionMethod); bytes = temp.ToBytes(); retMessage.Write(bytes, 0, bytes.Length); // Certificate message byte[] certs = GetCertificateList(m_Options.Certificate); temp.type = HandshakeType.Certificate; temp.fragment = certs; bytes = temp.ToBytes(); retMessage.Write(bytes, 0, bytes.Length); // ServerKeyExchange message [optional] => only with RSA_EXPORT and public key > 512 bits if (m_Options.Certificate.GetPublicKeyLength() > 512 && CipherSuites.GetCipherDefinition(m_EncryptionScheme).Exportable) { MemoryStream kes = new MemoryStream(); MD5SHA1CryptoServiceProvider mscsp = new MD5SHA1CryptoServiceProvider(); // hash the client and server random values mscsp.TransformBlock(m_ClientTime, 0, 4, m_ClientTime, 0); mscsp.TransformBlock(m_ClientRandom, 0, 28, m_ClientRandom, 0); mscsp.TransformBlock(m_ServerTime, 0, 4, m_ServerTime, 0); mscsp.TransformBlock(m_ServerRandom, 0, 28, m_ServerRandom, 0); // create a new 512 bit RSA key m_KeyCipher = new RSACryptoServiceProvider(512); RSAParameters p = m_KeyCipher.ExportParameters(false); // write the key parameters to the output stream bytes = new byte[] { (byte)(p.Modulus.Length / 256), (byte)(p.Modulus.Length % 256) }; kes.Write(bytes, 0, 2); kes.Write(p.Modulus, 0, p.Modulus.Length); mscsp.TransformBlock(bytes, 0, 2, bytes, 0); mscsp.TransformBlock(p.Modulus, 0, p.Modulus.Length, p.Modulus, 0); bytes = new byte[] { (byte)(p.Exponent.Length / 256), (byte)(p.Exponent.Length % 256) }; kes.Write(bytes, 0, 2); kes.Write(p.Exponent, 0, p.Exponent.Length); mscsp.TransformBlock(bytes, 0, 2, bytes, 0); mscsp.TransformFinalBlock(p.Exponent, 0, p.Exponent.Length); // create signature bytes = mscsp.CreateSignature(m_Options.Certificate); kes.Write(new byte[] { (byte)(bytes.Length / 256), (byte)(bytes.Length % 256) }, 0, 2); kes.Write(bytes, 0, bytes.Length); // write to output temp.type = HandshakeType.ServerKeyExchange; temp.fragment = kes.ToArray(); bytes = temp.ToBytes(); retMessage.Write(bytes, 0, bytes.Length); kes.Close(); } else { m_KeyCipher = (RSACryptoServiceProvider)m_Options.Certificate.PrivateKey; } // CertificateRequest message [optional] if (m_MutualAuthentication) { bytes = GetDistinguishedNames(); if (bytes.Length != 0) // make sure at least one certificate is returned { temp.type = HandshakeType.CertificateRequest; temp.fragment = new byte[bytes.Length + 4]; temp.fragment[0] = 1; // one certificate type supported temp.fragment[1] = 1; // cert type RSA temp.fragment[2] = (byte)(bytes.Length / 256); temp.fragment[3] = (byte)(bytes.Length % 256); Array.Copy(bytes, 0, temp.fragment, 4, bytes.Length); bytes = temp.ToBytes(); retMessage.Write(bytes, 0, bytes.Length); } } // ServerHelloDone message temp.type = HandshakeType.ServerHelloDone; temp.fragment = new byte[0]; bytes = temp.ToBytes(); retMessage.Write(bytes, 0, bytes.Length); // final adjustments ret.Status = SslStatus.ContinueNeeded; ret.Message = retMessage.ToArray(); retMessage.Close(); UpdateHashes(ret.Message, HashUpdate.All); // output message ret.Message = m_RecordLayer.EncryptBytes(ret.Message, 0, ret.Message.Length, ContentType.Handshake); return(ret); }
protected override byte[] GetClientHello() { if (m_State != HandshakeType.Nothing && m_State != HandshakeType.Finished) throw new SslException(AlertDescription.UnexpectedMessage, "ClientHello message must be the first message or must be preceded by a Finished message."); m_IsNegotiating = true; m_State = HandshakeType.ClientHello; byte[] ciphers = CipherSuites.GetCipherAlgorithmBytes(m_Options.AllowedAlgorithms); byte[] compr = CompressionAlgorithm.GetCompressionAlgorithmBytes(m_Options.AllowedAlgorithms); HandshakeMessage temp = new HandshakeMessage(HandshakeType.ClientHello, new byte[38 + ciphers.Length + compr.Length]); m_ClientTime = GetUnixTime(); m_ClientRandom = new byte[28]; m_RNG.GetBytes(m_ClientRandom); ProtocolVersion pv = CompatibilityLayer.GetMaxProtocol(m_Options.Protocol); temp.fragment[0] = pv.major; temp.fragment[1] = pv.minor; Array.Copy(m_ClientTime, 0, temp.fragment, 2, 4); Array.Copy(m_ClientRandom, 0, temp.fragment, 6, 28); temp.fragment[34] = 0; // do not resume a session, and do not let the other side cache it temp.fragment[35] = (byte)(ciphers.Length / 256); temp.fragment[36] = (byte)(ciphers.Length % 256); Array.Copy(ciphers, 0, temp.fragment, 37, ciphers.Length); temp.fragment[37 + ciphers.Length] = (byte)compr.Length; Array.Copy(compr, 0, temp.fragment, 38 + ciphers.Length, compr.Length); byte[] ret = temp.ToBytes(); UpdateHashes(ret, HashUpdate.All); // client hello message return m_RecordLayer.EncryptBytes(ret, 0, ret.Length, ContentType.Handshake); }
protected SslHandshakeStatus ProcessClientHello(HandshakeMessage message) { if (m_State != HandshakeType.Nothing && m_State != HandshakeType.Finished) throw new SslException(AlertDescription.UnexpectedMessage, "ClientHello message must be the first message or must be preceded by a Finished message."); Alert alert = null; m_IsNegotiating = true; UpdateHashes(message, HashUpdate.All); // input message // process ClientHello int currentLen = 0; ProtocolVersion pv = new ProtocolVersion(message.fragment[currentLen++], message.fragment[currentLen++]); m_MaxClientVersion = pv; //Violation with tls spec. If remote side uses higher protocol version than your, then we must answer with your highest version //if (CompatibilityLayer.SupportsProtocol(m_Options.Protocol, pv) && pv.GetVersionInt() != GetVersion().GetVersionInt()) // throw new SslException(AlertDescription.IllegalParameter, "Unknown protocol version of the client."); try { // extract the time from the client [== 1 uint] m_ClientTime = new byte[4]; Buffer.BlockCopy(message.fragment, currentLen, m_ClientTime, 0, 4); currentLen += 4; // extract the random bytes [== 28 bytes] m_ClientRandom = new byte[28]; Buffer.BlockCopy(message.fragment, currentLen, m_ClientRandom, 0, 28); currentLen += 28; // extact the session ID [== 0..32 bytes] byte length = message.fragment[currentLen++]; if (length > 32) throw new SslException(AlertDescription.IllegalParameter, "The length of the SessionID cannot be more than 32 bytes."); m_SessionID = new byte[length]; Buffer.BlockCopy(message.fragment, currentLen, m_SessionID, 0, length); currentLen += length; // extract the available cipher suites Int16 ciphers_size = BinaryHelper.Int16FromBytes(message.fragment[currentLen++], message.fragment[currentLen++]);//message.fragment[length] * 256 + message.fragment[length + 1]; if (ciphers_size < 2 || ciphers_size % 2 != 0) throw new SslException(AlertDescription.IllegalParameter, "The number of ciphers is invalid -or- the cipher length is not even."); byte[] ciphers = new byte[ciphers_size]; Buffer.BlockCopy(message.fragment, currentLen, ciphers, 0, ciphers_size); currentLen += ciphers_size; m_EncryptionScheme = CipherSuites.GetCipherSuiteAlgorithm(ciphers, m_Options.AllowedAlgorithms); // extract the available compression algorithms int compressors_size = message.fragment[currentLen++]; if (compressors_size == 0) throw new SslException(AlertDescription.IllegalParameter, "No compressor specified."); byte[] compressors = new byte[compressors_size]; Buffer.BlockCopy(message.fragment, currentLen, compressors, 0, compressors_size); m_CompressionMethod = CompressionAlgorithm.GetCompressionAlgorithm(compressors, m_Options.AllowedAlgorithms); currentLen += compressors_size; try { if (message.fragment.Length > currentLen) this.ProcessExtensions(message.fragment, ref currentLen, ConnectionEnd.Server); } catch (ALPNCantSelectProtocolException ex) { alert = new Alert(ex.Description, ex.Level); } } catch (Exception e) { throw new SslException(e, AlertDescription.InternalError, "The message is invalid."); } // create reply return GetClientHelloResult(alert); }
protected SslHandshakeStatus ProcessServerHello(HandshakeMessage message) { if (m_State != HandshakeType.ClientHello && m_State != HandshakeType.HelloRequest) throw new SslException(AlertDescription.UnexpectedMessage, "ServerHello message must be preceded by a ClientHello message."); UpdateHashes(message, HashUpdate.All); // input message if (message.fragment.Length < 2 || message.fragment[0] != GetVersion().major || message.fragment[1] != GetVersion().minor) throw new SslException(AlertDescription.IllegalParameter, "Unknown protocol version of the client."); try { // extract the time from the client [== 1 uint] m_ServerTime = new byte[4]; Array.Copy(message.fragment, 2, m_ServerTime, 0, 4); // extract the random bytes [== 28 bytes] m_ServerRandom = new byte[28]; Array.Copy(message.fragment, 6, m_ServerRandom, 0, 28); // extact the session ID [== 0..32 bytes] int length = message.fragment[34]; if (length > 32) throw new SslException(AlertDescription.IllegalParameter, "The length of the SessionID cannot be more than 32 bytes."); m_SessionID = new byte[length]; Array.Copy(message.fragment, 35, m_SessionID, 0, length); // extract the selected cipher suite m_EncryptionScheme = CipherSuites.GetCipherAlgorithmType(message.fragment, 35 + length); // extract the selected compression method m_CompressionMethod = CompressionAlgorithm.GetCompressionAlgorithmType(message.fragment, 37 + length); } catch (Exception e) { throw new SslException(e, AlertDescription.InternalError, "The message is invalid."); } return new SslHandshakeStatus(SslStatus.MessageIncomplete, null); }
protected abstract SslHandshakeStatus ProcessMessage(HandshakeMessage message);
protected SslHandshakeStatus ProcessClientKeyExchange(HandshakeMessage message) { if (!(this.m_MutualAuthentication ? m_State == HandshakeType.Certificate : m_State == HandshakeType.ClientHello)) throw new SslException(AlertDescription.UnexpectedMessage, "ClientKeyExchange message must be preceded by a ClientHello or Certificate message."); byte[] preMasterSecret; UpdateHashes(message, HashUpdate.All); // input message try { if (message.fragment.Length % 8 == 2) { // check whether the length is prepended or not if (message.fragment[0] * 256 + message.fragment[1] != message.fragment.Length - 2) throw new SslException(AlertDescription.DecodeError, "Invalid ClientKeyExchange message."); preMasterSecret = new byte[message.fragment.Length - 2]; Array.Copy(message.fragment, 2, preMasterSecret, 0, preMasterSecret.Length); message.fragment = preMasterSecret; } RSAKeyTransform df = new RSAKeyTransform(m_KeyCipher); preMasterSecret = df.DecryptKeyExchange(message.fragment); if (preMasterSecret.Length != 48) throw new SslException(AlertDescription.IllegalParameter, "Invalid message."); if (((int)m_Options.Flags & (int)SecurityFlags.IgnoreMaxProtocol) == 0) { if (preMasterSecret[0] != m_MaxClientVersion.major || preMasterSecret[1] != m_MaxClientVersion.minor) throw new SslException(AlertDescription.IllegalParameter, "Version rollback detected."); } else { if (preMasterSecret[0] != 3 || (preMasterSecret[1] != 0 && preMasterSecret[1] != 1)) throw new SslException(AlertDescription.IllegalParameter, "Invalid protocol version detected."); } m_KeyCipher.Clear(); m_KeyCipher = null; } catch { // this is to avoid RSA PKCS#1 padding attacks // and the Klima-Pokorny-Rosa attack on RSA in SSL/TLS preMasterSecret = new byte[48]; m_RNG.GetBytes(preMasterSecret); } GenerateCiphers(preMasterSecret); return new SslHandshakeStatus(SslStatus.MessageIncomplete, null); }
protected override byte[] GetClientHello() { if (m_State != HandshakeType.Nothing && m_State != HandshakeType.Finished) throw new SslException(AlertDescription.UnexpectedMessage, "ClientHello message must be the first message or must be preceded by a Finished message."); m_IsNegotiating = true; m_State = HandshakeType.ClientHello; byte[] ciphers = CipherSuites.GetCipherAlgorithmBytes(m_Options.AllowedAlgorithms); byte[] compr = CompressionAlgorithm.GetCompressionAlgorithmBytes(m_Options.AllowedAlgorithms); MemoryStream clientHello = new MemoryStream(); m_ClientTime = GetUnixTime(); m_ClientRandom = new byte[28]; m_RNG.GetBytes(m_ClientRandom); ProtocolVersion pv = CompatibilityLayer.GetMaxProtocol(m_Options.Protocol); clientHello.WriteByte(pv.major); clientHello.WriteByte(pv.minor); clientHello.Write(m_ClientTime, 0, m_ClientTime.Length); clientHello.Write(m_ClientRandom, 0, m_ClientRandom.Length); clientHello.WriteByte(0); clientHello.WriteByte((byte)(ciphers.Length / 256)); clientHello.WriteByte((byte)(ciphers.Length % 256)); clientHello.Write(ciphers, 0, ciphers.Length); clientHello.WriteByte((byte)compr.Length); clientHello.Write(compr, 0, compr.Length); if (this.clientHelloExts != null) this.clientHelloExts.WriteExtensions(clientHello, ConnectionEnd.Client); HandshakeMessage hm = new HandshakeMessage(HandshakeType.ClientHello, clientHello.ToArray()); byte[] ret = hm.ToBytes(); UpdateHashes(ret, HashUpdate.All); // client hello message return m_RecordLayer.EncryptBytes(ret, 0, ret.Length, ContentType.Handshake); }
protected SslHandshakeStatus ProcessCertificateVerify(HandshakeMessage message) { if (m_State != HandshakeType.ClientKeyExchange) throw new SslException(AlertDescription.UnexpectedMessage, "CertificateVerify message must be preceded by a ClientKeyExchange message."); UpdateHashes(message, HashUpdate.LocalRemote); byte[] signature; if (message.fragment.Length % 8 == 2) { // check whether the length is prepended or not if (message.fragment[0] * 256 + message.fragment[1] != message.fragment.Length - 2) throw new SslException(AlertDescription.DecodeError, "Invalid CertificateVerify message."); signature = new byte[message.fragment.Length - 2]; Array.Copy(message.fragment, 2, signature, 0, signature.Length); } else { signature = message.fragment; } m_CertSignHash.MasterKey = this.m_MasterSecret; m_CertSignHash.TransformFinalBlock(signature, 0, 0); // finalize hash if (!m_CertSignHash.VerifySignature(m_RemoteCertificate, signature)) throw new SslException(AlertDescription.CertificateUnknown, "The signature in the CertificateVerify message is invalid."); return new SslHandshakeStatus(SslStatus.MessageIncomplete, null); }
protected SslHandshakeStatus ProcessFinished(HandshakeMessage message) { if (m_State != HandshakeType.ChangeCipherSpec) throw new SslException(AlertDescription.UnexpectedMessage, "Finished message must be preceded by a ChangeCipherSpec message."); // check hash received from client this.VerifyFinishedMessage(message.fragment); m_IsNegotiating = false; if (this.OnHandshakeFinished != null) this.OnHandshakeFinished(this, null); ClearHandshakeStructures(); return new SslHandshakeStatus(SslStatus.OK, null); }
protected SslHandshakeStatus ProcessFinished(HandshakeMessage message) { if (m_State != HandshakeType.ChangeCipherSpec) throw new SslException(AlertDescription.UnexpectedMessage, "Finished message must be preceded by a ChangeCipherSpec message."); byte[] temp; // check hash received from client VerifyFinishedMessage(message.fragment); // send ChangeCipherSpec UpdateHashes(message, HashUpdate.Local); MemoryStream ms = new MemoryStream(); temp = m_RecordLayer.EncryptBytes(new byte[]{1}, 0, 1, ContentType.ChangeCipherSpec); ms.Write(temp, 0, temp.Length); m_RecordLayer.ChangeLocalState(null, m_CipherSuite.Encryptor, m_CipherSuite.LocalHasher); // send Finished message temp = GetFinishedMessage(); temp = m_RecordLayer.EncryptBytes(temp, 0, temp.Length, ContentType.Handshake); ms.Write(temp, 0, temp.Length); m_State = HandshakeType.Nothing; // send empty record [http://www.openssl.org/~bodo/tls-cbc.txt] if (this.m_CipherSuite.Encryptor.OutputBlockSize != 1) { // is bulk cipher? if (((int)m_Options.Flags & (int)SecurityFlags.DontSendEmptyRecord) == 0) { byte[] empty = m_RecordLayer.EncryptBytes(new byte[0], 0, 0, ContentType.ApplicationData); ms.Write(empty, 0, empty.Length); } } // finalize byte[] ret = ms.ToArray(); ms.Close(); m_IsNegotiating = false; ClearHandshakeStructures(); return new SslHandshakeStatus(SslStatus.OK, ret); }
protected SslHandshakeStatus ProcessServerHello(HandshakeMessage message) { if (m_State != HandshakeType.ClientHello && m_State != HandshakeType.HelloRequest) throw new SslException(AlertDescription.UnexpectedMessage, "ServerHello message must be preceded by a ClientHello message."); UpdateHashes(message, HashUpdate.All); // input message //Violation with tls spec. If remote side uses higher protocol version than your, then we must answer with your highest version //if (message.fragment.Length < 2 || message.fragment[0] != GetVersion().major || message.fragment[1] != GetVersion().minor) //throw new SslException(AlertDescription.IllegalParameter, "Unknown protocol version of the client."); int currentProcessedLen = 2; try { // extract the time from the client [== 1 uint] m_ServerTime = new byte[4]; Buffer.BlockCopy(message.fragment, currentProcessedLen, m_ServerTime, 0, 4); currentProcessedLen += 4; // extract the random bytes [== 28 bytes] m_ServerRandom = new byte[28]; Buffer.BlockCopy(message.fragment, currentProcessedLen, m_ServerRandom, 0, 28); currentProcessedLen += 28; // extact the session ID [== 0..32 bytes] int length = message.fragment[currentProcessedLen++]; if (length > 32) throw new SslException(AlertDescription.IllegalParameter, "The length of the SessionID cannot be more than 32 bytes."); m_SessionID = new byte[length]; Buffer.BlockCopy(message.fragment, currentProcessedLen, m_SessionID, 0, length); currentProcessedLen += length; // extract the selected cipher suite m_EncryptionScheme = CipherSuites.GetCipherAlgorithmType(message.fragment, currentProcessedLen); currentProcessedLen += 2; // extract the selected compression method m_CompressionMethod = CompressionAlgorithm.GetCompressionAlgorithmType(message.fragment, currentProcessedLen++); } catch (Exception e) { throw new SslException(e, AlertDescription.InternalError, "The message is invalid."); } if (message.fragment.Length == currentProcessedLen) return new SslHandshakeStatus(SslStatus.MessageIncomplete, null); this.ProcessExtensions(message.fragment, ref currentProcessedLen, ConnectionEnd.Client); return new SslHandshakeStatus(SslStatus.MessageIncomplete, null); }
protected override byte[] GetRenegotiateBytes() { if (IsNegotiating()) return null; HandshakeMessage hm = new HandshakeMessage(HandshakeType.HelloRequest, new byte[0]); return m_RecordLayer.EncryptBytes(hm.ToBytes(), 0, 4, ContentType.Handshake); }
protected SslHandshakeStatus ProcessServerKeyExchange(HandshakeMessage message) { if (m_State != HandshakeType.Certificate) throw new SslException(AlertDescription.UnexpectedMessage, "ServerKeyExchange message must be preceded by a Certificate message."); CipherDefinition cd = CipherSuites.GetCipherDefinition(m_EncryptionScheme); if (!cd.Exportable) throw new SslException(AlertDescription.HandshakeFailure, "The ServerKeyExchange message should not be sent for non-exportable ciphers."); if (m_RemoteCertificate.GetPublicKeyLength() <= 512) throw new SslException(AlertDescription.HandshakeFailure, "The ServerKeyExchange message should not be sent because the server certificate public key is of exportable length."); UpdateHashes(message, HashUpdate.All); // input message // extract modulus and exponent RSAParameters pars = new RSAParameters(); int size = message.fragment[0] * 256 + message.fragment[1]; pars.Modulus = new byte[size]; Buffer.BlockCopy(message.fragment, 2, pars.Modulus, 0, size); int offset = size + 2; size = message.fragment[offset] * 256 + message.fragment[offset + 1]; pars.Exponent = new byte[size]; Buffer.BlockCopy(message.fragment, offset + 2, pars.Exponent, 0, size); offset += size + 2; pars.Modulus = RemoveLeadingZeros(pars.Modulus); pars.Exponent = RemoveLeadingZeros(pars.Exponent); m_KeyCipher = new RSACryptoServiceProvider(); m_KeyCipher.ImportParameters(pars); // compute verification hashes MD5SHA1CryptoServiceProvider ms = new MD5SHA1CryptoServiceProvider(); ms.TransformBlock(m_ClientTime, 0, m_ClientTime.Length, m_ClientTime, 0); ms.TransformBlock(m_ClientRandom, 0, m_ClientRandom.Length, m_ClientRandom, 0); ms.TransformBlock(m_ServerTime, 0, m_ServerTime.Length, m_ServerTime, 0); ms.TransformBlock(m_ServerRandom, 0, m_ServerRandom.Length, m_ServerRandom, 0); ms.TransformFinalBlock(message.fragment, 0, offset); // verify the signature size = message.fragment[offset] * 256 + message.fragment[offset + 1]; byte[] signature = new byte[size]; // holds the signature returned by the server Buffer.BlockCopy(message.fragment, offset + 2, signature, 0, size); if (!ms.VerifySignature(m_RemoteCertificate, signature)) throw new SslException(AlertDescription.HandshakeFailure, "The data was not signed by the server certificate."); ms.Clear(); return new SslHandshakeStatus(SslStatus.MessageIncomplete, null); }