예제 #1
0
        HandshakeType SendClientHello(ref int offset)
        {
            _pendingConnState = new ConnectionState();
            _handshakeData = new HandshakeData
            {
                HandshakeHash1 = Hasher.Create(TLSHashAlgorithm.SHA256),
                HandshakeHash2 = Hasher.Create(TLSHashAlgorithm.SHA256),
                HandshakeHash1_384 = Hasher.Create(TLSHashAlgorithm.SHA384),
                HandshakeHash2_384 = Hasher.Create(TLSHashAlgorithm.SHA384),
                HandshakeHash1_MD5SHA1 = Hasher.Create(TLSHashAlgorithm.MD5SHA1),
                HandshakeHash2_MD5SHA1 = Hasher.Create(TLSHashAlgorithm.MD5SHA1),
                CertificateVerifyHash_MD5 = Hasher.Create(TLSHashAlgorithm.MD5),
                CertificateVerifyHash_SHA1 = Hasher.Create(TLSHashAlgorithm.SHA1)
            };

            // Highest version supported
            offset += Utils.WriteUInt16(_buf, offset, (ushort)HighestTlsVersionSupported);

            // Client random
            var timestamp = (uint)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
            _pendingConnState.ClientRandom = new byte[32];
            _rng.GetBytes(_pendingConnState.ClientRandom);
            Utils.WriteUInt32(_pendingConnState.ClientRandom, 0, timestamp);

            Buffer.BlockCopy(_pendingConnState.ClientRandom, 0, _buf, offset, 32);
            offset += 32;

            // No session id
            _buf[offset++] = 0;

            // Cipher suites
            var supportedCipherSuites = CipherSuiteInfo.Supported;
            /*
            if (HighestTlsVersionSupported != TlsVersion.TLSv1_2)
                supportedCipherSuites = supportedCipherSuites.Where(cs => cs.IsAllowedBefore1_2).ToArray();
            */
            offset += Utils.WriteUInt16(_buf, offset, (ushort)(supportedCipherSuites.Length * sizeof(ushort)));
            foreach (var suite in supportedCipherSuites)
            {
                offset += Utils.WriteUInt16(_buf, offset, (ushort)suite.Id);
            }

            // Compression methods
            _buf[offset++] = 1; // Length
            _buf[offset++] = 0; // "null" compression method

            // Extensions length, fill in later
            var extensionLengthOffset = offset;
            offset += 2;

            // Renegotiation extension
            offset += Utils.WriteUInt16(_buf, offset, (ushort)ExtensionType.RenegotiationInfo);
            if (_connState.SecureRenegotiation)
            {
                // Extension length
                offset += Utils.WriteUInt16(_buf, offset, 13);

                // Renegotiated connection length
                _buf[offset++] = 12;
                // Renegotiated connection data
                Buffer.BlockCopy(_connState.ClientVerifyData, 0, _buf, offset, 12);
                offset += 12;
            }
            else
            {
                // Extension length
                offset += Utils.WriteUInt16(_buf, offset, 1);
                // Renegotiated connection length
                _buf[offset++] = 0;
            }

            // SNI extension
            if (_hostName != null)
            {
                // TODO: IDN Unicode -> Punycode

                // NOTE: IP addresses should not use SNI extension, per specification.
                System.Net.IPAddress ip;
                if (!System.Net.IPAddress.TryParse(_hostName, out ip))
                {
                    offset += Utils.WriteUInt16(_buf, offset, (ushort)ExtensionType.ServerName);
                    var byteLen = Encoding.ASCII.GetBytes(_hostName, 0, _hostName.Length, _buf, offset + 7);
                    offset += Utils.WriteUInt16(_buf, offset, (ushort)(5 + byteLen));
                    offset += Utils.WriteUInt16(_buf, offset, (ushort)(3 + byteLen));
                    _buf[offset++] = 0; // host_name
                    offset += Utils.WriteUInt16(_buf, offset, (ushort)byteLen);
                    offset += byteLen;
                }
            }

            if (HighestTlsVersionSupported == TlsVersion.TLSv1_2)
            {
                // Signature algorithms extension. At least IIS 7.5 needs this or it immediately resets the connection.
                // Used to specify what kind of server certificate hash/signature algorithms we can use to verify it.
                offset += Utils.WriteUInt16(_buf, offset, (ushort)ExtensionType.SignatureAlgorithms);
                offset += Utils.WriteUInt16(_buf, offset, 20);
                offset += Utils.WriteUInt16(_buf, offset, 18);
                _buf[offset++] = (byte)TLSHashAlgorithm.SHA1;
                _buf[offset++] = (byte)SignatureAlgorithm.ECDSA;
                _buf[offset++] = (byte)TLSHashAlgorithm.SHA256;
                _buf[offset++] = (byte)SignatureAlgorithm.ECDSA;
                _buf[offset++] = (byte)TLSHashAlgorithm.SHA384;
                _buf[offset++] = (byte)SignatureAlgorithm.ECDSA;
                _buf[offset++] = (byte)TLSHashAlgorithm.SHA512;
                _buf[offset++] = (byte)SignatureAlgorithm.ECDSA;
                _buf[offset++] = (byte)TLSHashAlgorithm.SHA1;
                _buf[offset++] = (byte)SignatureAlgorithm.RSA;
                _buf[offset++] = (byte)TLSHashAlgorithm.SHA256;
                _buf[offset++] = (byte)SignatureAlgorithm.RSA;
                _buf[offset++] = (byte)TLSHashAlgorithm.SHA384;
                _buf[offset++] = (byte)SignatureAlgorithm.RSA;
                _buf[offset++] = (byte)TLSHashAlgorithm.SHA512;
                _buf[offset++] = (byte)SignatureAlgorithm.RSA;
                _buf[offset++] = (byte)TLSHashAlgorithm.SHA1;
                _buf[offset++] = (byte)SignatureAlgorithm.DSA;
            }

            if (supportedCipherSuites.Any(s => s.KeyExchange == KeyExchange.ECDHE_RSA || s.KeyExchange == KeyExchange.ECDHE_ECDSA))
            {
                // Supported Elliptic Curves Extension

                offset += Utils.WriteUInt16(_buf, offset, (ushort)ExtensionType.SupportedEllipticCurves);
                offset += Utils.WriteUInt16(_buf, offset, 8);
                offset += Utils.WriteUInt16(_buf, offset, 6);
                offset += Utils.WriteUInt16(_buf, offset, (ushort)NamedCurve.secp256r1);
                offset += Utils.WriteUInt16(_buf, offset, (ushort)NamedCurve.secp384r1);
                offset += Utils.WriteUInt16(_buf, offset, (ushort)NamedCurve.secp521r1);

                // Supported Point Formats Extension

                offset += Utils.WriteUInt16(_buf, offset, (ushort)ExtensionType.SupportedPointFormats);
                offset += Utils.WriteUInt16(_buf, offset, 2);
                _buf[offset++] = 1; // Length
                _buf[offset++] = 0; // Uncompressed
            }


            Utils.WriteUInt16(_buf, extensionLengthOffset, (ushort)(offset - (extensionLengthOffset + 2)));

            return HandshakeType.ClientHello;
        }
예제 #2
0
        void ParseChangeCipherSpec()
        {
            if (_plaintextLen != 1 || _buf[_plaintextStart] != 1)
                SendAlertFatal(AlertDescription.IllegalParameter);
            if (_pendingConnState != _connState)
                SendAlertFatal(AlertDescription.UnexpectedMessage);

            // We don't accept buffered handshake messages to be completed after Change Cipher Spec,
            // since they would not be encrypted correctly
            if (_handshakeMessagesBuffer.HasBufferedData)
                SendAlertFatal(AlertDescription.UnexpectedMessage);

            _readConnState.Dispose();
            _readConnState = _connState;
            _pendingConnState = null;
        }
예제 #3
0
        // Here we send all client messages in response to server hello
        int GenerateHandshakeResponse()
        {
            var offset = 0;
            var ivLen = _connState.IvLen;
            if (_handshakeData.CertificateTypes != null) // Certificate request has been sent by the server
            {
                SendHandshakeMessage(SendClientCertificate, ref offset, ivLen);
            }
            switch (_pendingConnState.CipherSuite.KeyExchange)
            {
                case KeyExchange.DHE_RSA:
                case KeyExchange.DHE_DSS:
                    SendHandshakeMessage(SendClientKeyExchangeDhe, ref offset, ivLen);
                    break;
                case KeyExchange.ECDHE_RSA:
                case KeyExchange.ECDHE_ECDSA:
                    SendHandshakeMessage(SendClientKeyExchangeEcdhe, ref offset, ivLen);
                    break;
                case KeyExchange.ECDH_ECDSA:
                case KeyExchange.ECDH_RSA:
                    SendHandshakeMessage(SendClientKeyExchangeEcdh, ref offset, ivLen);
                    break;
                case KeyExchange.RSA:
                    SendHandshakeMessage(SendClientKeyExchangeRsa, ref offset, ivLen);
                    break;
                default:
                    throw new InvalidOperationException();
            }
            if (_handshakeData.CertificateTypes != null && _handshakeData.SelectedClientCertificate != null)
            {
                SendHandshakeMessage(SendCertificateVerify, ref offset, ivLen);
            }

            var cipherSpecStart = offset;
            SendChangeCipherSpec(ref offset, ivLen);
            offset = Encrypt(cipherSpecStart, 1);

            // Key generation from Master Secret
            var mode = _pendingConnState.CipherSuite.AesMode;
            var isCbc = mode == AesMode.CBC;
            var isGcm = mode == AesMode.GCM;

            var concRandom = new byte[_pendingConnState.ServerRandom.Length + _pendingConnState.ClientRandom.Length];
            Buffer.BlockCopy(_pendingConnState.ServerRandom, 0, concRandom, 0, _pendingConnState.ServerRandom.Length);
            Buffer.BlockCopy(_pendingConnState.ClientRandom, 0, concRandom, _pendingConnState.ServerRandom.Length, _pendingConnState.ClientRandom.Length);
            var macLen = isCbc ? _pendingConnState.CipherSuite.MACLen / 8 : 0;
            var aesKeyLen = _pendingConnState.CipherSuite.AesKeyLen / 8;
            var IVLen = isGcm ? 4 : _pendingConnState.TlsVersion != TlsVersion.TLSv1_0 ? 0 : _pendingConnState.BlockLen;
            var keyBlock = Utils.PRF(_pendingConnState.PRFAlgorithm, _pendingConnState.MasterSecret, "key expansion", concRandom, macLen * 2 + aesKeyLen * 2 + IVLen * 2);
            byte[] writeMac = new byte[macLen], readMac = new byte[macLen], writeKey = new byte[aesKeyLen], readKey = new byte[aesKeyLen];
            Buffer.BlockCopy(keyBlock, 0, writeMac, 0, macLen);
            Buffer.BlockCopy(keyBlock, macLen, readMac, 0, macLen);
            Buffer.BlockCopy(keyBlock, macLen * 2, writeKey, 0, aesKeyLen);
            Buffer.BlockCopy(keyBlock, macLen * 2 + aesKeyLen, readKey, 0, aesKeyLen);
            if (isCbc)
            {
                _pendingConnState.WriteMac = _pendingConnState.CipherSuite.CreateHMAC(writeMac);
                _pendingConnState.ReadMac = _pendingConnState.CipherSuite.CreateHMAC(readMac);
            }
            if (IVLen != 0)
            {
                // For GCM we make it bigger to later fill in sequence numbers
                var writeIv = new byte[isGcm ? 16 : IVLen];
                var readIv = new byte[isGcm ? 16 : IVLen];
                Buffer.BlockCopy(keyBlock, macLen * 2 + aesKeyLen * 2, writeIv, 0, IVLen);
                Buffer.BlockCopy(keyBlock, macLen * 2 + aesKeyLen * 2 + IVLen, readIv, 0, IVLen);
                _pendingConnState.WriteIv = writeIv;
                _pendingConnState.ReadIv = readIv;
            }
            else
            {
                _pendingConnState.ReadIv = _pendingConnState.WriteIv = new byte[_pendingConnState.BlockLen];
            }

            _pendingConnState.WriteAes = Aes.Create();
            _pendingConnState.WriteAes.Key = writeKey;
            _pendingConnState.WriteAes.Mode = isCbc ? CipherMode.CBC : CipherMode.ECB;
            _pendingConnState.WriteAes.Padding = PaddingMode.None;

            _pendingConnState.ReadAes = Aes.Create();
            _pendingConnState.ReadAes.Key = readKey;
            _pendingConnState.ReadAes.Mode = isCbc ? CipherMode.CBC : CipherMode.ECB;
            _pendingConnState.ReadAes.Padding = PaddingMode.None;

            // int tmpOffset = macLen * 2 + aesKeyLen * 2;
            if (isGcm)
            {
                _pendingConnState.WriteAesECB = _pendingConnState.WriteAes.CreateEncryptor(writeKey, null);
                _pendingConnState.ReadAesECB = _pendingConnState.ReadAes.CreateEncryptor(readKey, null);
                _pendingConnState.WriteGCMTable = GaloisCounterMode.GetH(_pendingConnState.WriteAesECB);
                _pendingConnState.ReadGCMTable = GaloisCounterMode.GetH(_pendingConnState.ReadAesECB);
                if (_temp512 == null)
                    _temp512 = new byte[512];
            }
            Utils.ClearArray(writeMac);
            Utils.ClearArray(readMac);
            Utils.ClearArray(writeKey);
            Utils.ClearArray(readKey);
            ivLen = _pendingConnState.IvLen;

            _connState = _pendingConnState;
            SendHandshakeMessage(SendFinished, ref offset, ivLen);
            //_handshakeData.HandshakeHash2.Final();

            // _buf is now ready to be written to the base stream, from pos 0 to offset
            return offset;
        }
예제 #4
0
 /// <summary>
 /// Creates a new TlsClientStream with the given underlying stream.
 /// The handshake must be manually initiated with the method PerformInitialHandshake.
 /// </summary>
 /// <param name="baseStream">Base stream</param>
 public TlsClientStream(Stream baseStream)
 {
     _connState = new ConnectionState() { TlsVersion = TlsVersion.TLSv1_0 };
     _readConnState = _connState;
     _baseStream = baseStream;
 }