public TLSRecord DecompressRecord(TLSRecord record) { if (ReceiveEncryptionActive == false) { ReadSequenceNumber++; return(record); } /// Decompress, then verify hmac and seqnumber /// byte [] bFragment = null; byte [] bMAC = null; byte [] bCipherTextBlock = record.RawSetContent; /// Store the cipher text block so we can use it as the next IV (last block from the previous record) /// if (record.RawSetContent.Length > SecurityParameters.Cipher.BlockSizeBytes) { bCipherTextBlock = new byte[SecurityParameters.Cipher.BlockSizeBytes]; Array.Copy(record.RawSetContent, record.RawSetContent.Length - SecurityParameters.Cipher.BlockSizeBytes, bCipherTextBlock, 0, SecurityParameters.Cipher.BlockSizeBytes); } DecryptRecord(record.RawSetContent, out bFragment, out bMAC); record.RawSetContent = bFragment; /// Compute the MAC ourselves so we can see if the man is correct ///Encrypt the compressed data. The new data is now the encrypted data + a MAC. /// Compute the MAC before encrypting /// /// The MAC is generated as: /// HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type + /// TLSCompressed.version + TLSCompressed.length + /// TLSCompressed.fragment)); /// where "+" denotes concatenation. /// byte[] bTLSRecord = record.BytesFromRawContent; byte[] bDataToBeHashed = new byte[8 + bTLSRecord.Length]; byte[] bSequence = new byte[8]; bSequence[0] = (byte)((ReadSequenceNumber & 0xFF00000000000000) >> 56); bSequence[1] = (byte)((ReadSequenceNumber & 0x00FF000000000000) >> 48); bSequence[2] = (byte)((ReadSequenceNumber & 0x0000FF0000000000) >> 40); bSequence[3] = (byte)((ReadSequenceNumber & 0x000000FF00000000) >> 32); bSequence[4] = (byte)((ReadSequenceNumber & 0x00000000FF000000) >> 24); bSequence[5] = (byte)((ReadSequenceNumber & 0x0000000000FF0000) >> 16); bSequence[6] = (byte)((ReadSequenceNumber & 0x000000000000FF00) >> 08); bSequence[7] = (byte)((ReadSequenceNumber & 0x00000000000000FF) >> 00); Array.Copy(bSequence, 0, bDataToBeHashed, 0, 8); Array.Copy(bTLSRecord, 0, bDataToBeHashed, 8, bTLSRecord.Length); byte[] bMACComputed = ServerHMAC.ComputeHash(bDataToBeHashed); ReadSequenceNumber++; LastCipherTextBlock = bCipherTextBlock; if (ByteHelper.CompareArrays(bMAC, bMACComputed) == false) { System.Threading.Thread.Sleep(0); //throw new Exception("Computed MAC did not match incoming record's MAC"); } return(record); }
void HandleHandshakeMessage(TLSHandShakeMessage msg) { /// Append all our handshake data because we'll need it later for some stupid reason if (msg.HandShakeMessageType == HandShakeMessageType.ServerHello) { ClientTLSState = TLS.ClientTLSState.ReceivedServerHello; } else if (msg.HandShakeMessageType == HandShakeMessageType.ServerHelloDone) { if (m_bSendClientCertificate == true) /// got a certificate request from the server, but we don't have any, so send an empty one { TLSHandShakeMessage msgclientcert = new TLSHandShakeMessage(); msgclientcert.HandShakeMessageType = HandShakeMessageType.Certificate; TLSRecord recordclientcert = new TLSRecord(); recordclientcert.MajorVersion = 3; recordclientcert.MinorVersion = 1; recordclientcert.ContentType = TLSContentType.Handshake; recordclientcert.Messages.Add(msgclientcert); SendTLSRecord(recordclientcert, true); } /// Server has sent the algorithm they want us to use, certificates, parameters, /// and any request for certificates from us. Now let's respond /// First generate, encrypt, and send the PreMasterSecret byte [] bKey = state.SecurityParameters.PeerCertificate.GetPublicKey(); byte [] bPreMasterSecret = new byte[48]; RNGCryptoServiceProvider.GetBytes(bPreMasterSecret); /// Get some random bytes bPreMasterSecret[0] = 0x03; /// first two bytes get set to the version bPreMasterSecret[1] = 0x01; /// Openssl is showing our modulus as having an extra 00 on the front when using this command /// (ignore this, the modulus lower in the output doesn't show the 0) /// rsa -in test_key.pem -text if (SocketClient.ShowDebug == true) { System.Diagnostics.Debug.WriteLine("Client Random: +++++++++++\r\n{0}\r\n++++++++++++", ByteHelper.HexStringFromByte(state.SecurityParameters.ClientRandom, true, 16)); System.Diagnostics.Debug.WriteLine("Server Random: +++++++++++\r\n{0}\r\n++++++++++++", ByteHelper.HexStringFromByte(state.SecurityParameters.ServerRandom, true, 16)); System.Diagnostics.Debug.WriteLine("PreMasterSecret: +++++++++++\r\n{0}\r\n++++++++++++", ByteHelper.HexStringFromByte(bPreMasterSecret, true, 16)); } RSACryptoServiceProvider provider = new RSACryptoServiceProvider(); // Need to use this guys' class to parse the DER encoded ASN.1 described certificate to // extract the modulus and e from the certificate - these two are the public key RSAParameters rsaparams = Kishore.X509.Parser.X509PublicKeyParser.GetRSAPublicKeyParameters(bKey); provider.ImportParameters(rsaparams); //System.Diagnostics.Debug.WriteLine("Public key modulus: +++++++++++\r\n{0}\r\n++++++++++++", ByteHelper.HexStringFromByte(rsaparams.Modulus, true, 16)); //System.Diagnostics.Debug.WriteLine("Public key exponent: +++++++++++\r\n{0}\r\n++++++++++++", ByteHelper.HexStringFromByte(rsaparams.Exponent, true, 16)); //System.Diagnostics.Debug.WriteLine("Key Exchange Algorithm: {0}, KeySize: {1}", provider.KeyExchangeAlgorithm, provider.KeySize); byte[] EncryptedPreMasterSecret = provider.Encrypt(bPreMasterSecret, false); provider.Dispose(); /// Send a Client Key Exchange method with our PreMasterSecret /// TLSHandShakeMessage msgsend = new TLSHandShakeMessage(); msgsend.HandShakeMessageType = HandShakeMessageType.ClientKeyExchange; msgsend.HandShakeClientKeyExchange.EncryptedPreMasterSecret = EncryptedPreMasterSecret; msgsend.HandShakeClientKeyExchange.KeyExchangeAlgorithm = KeyExchangeAlgorithm.rsa; msgsend.HandShakeClientKeyExchange.PublicValueEncoding = PublicValueEncoding.explicit_en; TLSRecord record = new TLSRecord(); record.MajorVersion = 3; record.MinorVersion = 1; record.ContentType = TLSContentType.Handshake; record.Messages.Add(msgsend); SendTLSRecord(record, true); /// Now generate all our keys, etc /// Section 8.1 /// master_secret = PRF(pre_master_secret, "master secret",ClientHello.random + ServerHello.random) [0..47]; // Combine ClientHello.Random and ServerHello.Random ByteBuffer buf = new ByteBuffer(); buf.AppendData(state.SecurityParameters.ClientRandom); buf.AppendData(state.SecurityParameters.ServerRandom); byte [] bCSRandom = buf.GetAllSamples(); // Do it in reverse order for different algorithms buf.AppendData(state.SecurityParameters.ServerRandom); buf.AppendData(state.SecurityParameters.ClientRandom); byte [] bSCRandom = buf.GetAllSamples(); state.SecurityParameters.MasterSecret = state.PRF(bPreMasterSecret, "master secret", bCSRandom, 48); if (SocketClient.ShowDebug == true) { System.Diagnostics.Debug.WriteLine("MasterSecret: +++++++++++\r\n{0}\r\n++++++++++++", ByteHelper.HexStringFromByte(state.SecurityParameters.MasterSecret, true, 16)); } /// Verified that we are computing all our keys correctly by using the same input into the Mentalis library /// If we coded ours independently and get the same answers, it has to be correct, or we made the same mistakes :) state.ComputeKeys(state.SecurityParameters.MasterSecret, bSCRandom); /// Now send out a change cipher-spec, followed by a client finished message that is encrypted /// TLSChangeCipherSpecMessage msgChangeCipherSpec = new TLSChangeCipherSpecMessage(); msgChangeCipherSpec.CCSProtocolType = CCSProtocolType.Default; TLSRecord recordccs = new TLSRecord(); recordccs.MajorVersion = 3; recordccs.MinorVersion = 1; recordccs.ContentType = TLSContentType.ChangeCipherSpec; recordccs.Messages.Add(msgChangeCipherSpec); SendTLSRecord(recordccs, false); state.SendEncryptionActive = true; state.WriteSequenceNumber = 0; /// reset on changecipherspec /// Now send a finished method, encrypted with our generated things /// RFC2246 section 7.4.9 if (SocketClient.ShowDebug == true) { System.Diagnostics.Debug.WriteLine("FINAL: AllHandShakeMessages Length is now {0}", AllHandShakeMessages.Size); } SHA1Managed sha1 = new SHA1Managed(); byte[] bAllHandshakeData = AllHandShakeMessages.PeekAllSamples(); byte[] bmd5OfHandshakeMessages = MD5Core.GetHash(bAllHandshakeData); byte[] bsha1OfHandshakeMessages = sha1.ComputeHash(bAllHandshakeData); ByteBuffer bSum = new ByteBuffer(); bSum.AppendData(bmd5OfHandshakeMessages); bSum.AppendData(bsha1OfHandshakeMessages); byte [] bCombinedHashes = bSum.GetAllSamples(); /// No use in writing out this debug, only a few of the characters get seen, the rest are dropped ///System.Diagnostics.Debug.WriteLine("**** Start all handshake data ****\r\n{0}\r\n**** End all handshake data ****", ByteHelper.HexStringFromByte(bAllHandshakeData, true, 32)); /// verify_data /// PRF(master_secret, finished_label, MD5(handshake_messages) + SHA-1(handshake_messages)) [0..11]; TLSHandShakeMessage msgFinished = new TLSHandShakeMessage(); msgFinished.HandShakeMessageType = HandShakeMessageType.Finished; msgFinished.HandShakeFinished.verify_data = state.PRF(state.SecurityParameters.MasterSecret, "client finished", bCombinedHashes, 12); TLSRecord recordFinished = new TLSRecord(); recordFinished.MajorVersion = 3; recordFinished.MinorVersion = 1; recordFinished.ContentType = TLSContentType.Handshake; recordFinished.Messages.Add(msgFinished); state.WriteSequenceNumber = 0; /// This record must now be encrypted before it can be sent SendTLSRecord(recordFinished, true); ClientTLSState = ClientTLSState.SentClientFinished; } else if (msg.HandShakeMessageType == HandShakeMessageType.Finished) { /// Got the server finished method, let's verify the data /// SHA1Managed sha1 = new SHA1Managed(); byte[] bAllHandshakeData = AllHandShakeMessages.GetAllSamples(); // no need to peek here, just get it all and clear it byte[] bmd5OfHandshakeMessages = MD5Core.GetHash(bAllHandshakeData); byte[] bsha1OfHandshakeMessages = sha1.ComputeHash(bAllHandshakeData); ByteBuffer bSum = new ByteBuffer(); bSum.AppendData(bmd5OfHandshakeMessages); bSum.AppendData(bsha1OfHandshakeMessages); byte[] bCombinedHashes = bSum.GetAllSamples(); /// No use in writing out this debug, only a few of the characters get seen, the rest are dropped ///System.Diagnostics.Debug.WriteLine("**** Start all handshake data ****\r\n{0}\r\n**** End all handshake data ****", ByteHelper.HexStringFromByte(bAllHandshakeData, true, 32)); /// verify_data /// PRF(master_secret, finished_label, MD5(handshake_messages) + SHA-1(handshake_messages)) [0..11]; byte [] bComputedVerify = state.PRF(state.SecurityParameters.MasterSecret, "server finished", bCombinedHashes, 12); bool bMatch = ByteHelper.CompareArrays(msg.HandShakeFinished.verify_data, bComputedVerify); if (bMatch == false) { SendAlert(AlertLevel.fatal, AlertDescription.HandshakeFailure); Client.Disconnect(); } else { NegotiationFinished(); } } else if (msg.HandShakeMessageType == HandShakeMessageType.CertificateRequest) { m_bSendClientCertificate = true; } if (ClientTLSState == TLS.ClientTLSState.ReceivedServerHello) { /// Gathering information /// if (msg.HandShakeMessageType == HandShakeMessageType.ServerHello) { state.SecurityParameters.Cipher = Cipher.FindCipher(msg.HandShakeServerHello.CipherSuite); state.SecurityParameters.CompressionMethod = CompressionMethod.null0; state.SecurityParameters.ConnectionEnd = ConnectionEnd.client; state.SecurityParameters.ServerRandom = msg.HandShakeServerHello.RandomStruct.Bytes; } else if (msg.HandShakeMessageType == HandShakeMessageType.Certificate) { if (msg.HandShakeCertificateMessage.Certificates.Count > 0) { state.SecurityParameters.PeerCertificate = msg.HandShakeCertificateMessage.Certificates[0]; } } } }