public bool IsSignatureValid(SecureChannelHandshakeKeyExchange keyExchange, SecureChannelHandshakeHello serverHello, SecureChannelHandshakeHello clientHello) { if (!SecureChannelStream.IsUserIdValid(_publicKey, _userId)) { return(false); } switch (serverHello.SupportedCiphers) { case SecureChannelCipherSuite.DHE2048_RSA2048_WITH_AES256_CBC_HMAC_SHA256: using (RSA rsa = RSA.Create()) { RSAParameters rsaPublicKey = DEREncoding.DecodeRSAPublicKey(_publicKey); rsa.ImportParameters(rsaPublicKey); if (rsa.KeySize != 2048) { throw new SecureChannelException(SecureChannelCode.PeerAuthenticationFailed, null, _userId, "RSA key size is not valid for selected crypto option: " + serverHello.SupportedCiphers.ToString()); } using (MemoryStream mS = new MemoryStream()) { mS.Write(keyExchange.EphemeralPublicKey, 0, keyExchange.EphemeralPublicKey.Length); mS.Write(serverHello.Nonce.Value, 0, serverHello.Nonce.Value.Length); mS.Write(clientHello.Nonce.Value, 0, clientHello.Nonce.Value.Length); mS.Position = 0; return(rsa.VerifyData(mS, _signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)); } } default: throw new SecureChannelException(SecureChannelCode.NoMatchingCipherAvailable, null, null); } }
public SecureChannelHandshakeKeyExchange(KeyAgreement keyAgreement, SecureChannelHandshakeHello serverHello, SecureChannelHandshakeHello clientHello, byte[] psk) : base(SecureChannelCode.None) { _ephemeralPublicKey = keyAgreement.GetPublicKey(); if (serverHello.Options.HasFlag(SecureChannelOptions.PRE_SHARED_KEY_AUTHENTICATION_REQUIRED)) { _pskAuth = GetPskAuthValue(serverHello.SupportedCiphers, _ephemeralPublicKey, serverHello.Nonce.Value, clientHello.Nonce.Value, psk); } else { _pskAuth = new BinaryNumber(new byte[] { }); } }
protected void EnableEncryption(Stream inputStream, SecureChannelHandshakeHello serverHello, SecureChannelHandshakeHello clientHello, KeyAgreement keyAgreement, SecureChannelHandshakeKeyExchange otherPartyKeyExchange) { using (MemoryStream mS = new MemoryStream(128)) { mS.Write(serverHello.Nonce.Value); mS.Write(clientHello.Nonce.Value); keyAgreement.HmacKey = mS.ToArray(); } byte[] masterKey = keyAgreement.DeriveKeyMaterial(otherPartyKeyExchange.EphemeralPublicKey); switch (serverHello.SupportedCiphers) { case SecureChannelCipherSuite.DHE2048_ANON_WITH_AES256_CBC_HMAC_SHA256: case SecureChannelCipherSuite.DHE2048_RSA2048_WITH_AES256_CBC_HMAC_SHA256: case SecureChannelCipherSuite.ECDHE256_ANON_WITH_AES256_CBC_HMAC_SHA256: case SecureChannelCipherSuite.ECDHE256_RSA2048_WITH_AES256_CBC_HMAC_SHA256: //generating AES IV of 128bit block size using MD5 byte[] eIV; byte[] dIV; using (HashAlgorithm hash = HashAlgorithm.Create("MD5")) { if (this is SecureChannelServerStream) { eIV = hash.ComputeHash(serverHello.Nonce.Value); dIV = hash.ComputeHash(clientHello.Nonce.Value); } else { eIV = hash.ComputeHash(clientHello.Nonce.Value); dIV = hash.ComputeHash(serverHello.Nonce.Value); } } //create encryptor _encryptionAlgo = Aes.Create(); _encryptionAlgo.Key = masterKey; _encryptionAlgo.IV = eIV; _encryptionAlgo.Padding = PaddingMode.None; //padding is managed by secure channel _encryptionAlgo.Mode = CipherMode.CBC; _encryptor = _encryptionAlgo.CreateEncryptor(); _authHMACEncrypt = new HMACSHA256(masterKey); //create decryptor _decryptionAlgo = Aes.Create(); _decryptionAlgo.Key = masterKey; _decryptionAlgo.IV = dIV; _decryptionAlgo.Padding = PaddingMode.None; //padding is managed by secure channel _decryptionAlgo.Mode = CipherMode.CBC; _decryptor = _decryptionAlgo.CreateDecryptor(); _authHMACDecrypt = new HMACSHA256(masterKey); //init variables _blockSizeBytes = _encryptionAlgo.BlockSize / 8; _writeBufferPadding = new byte[_blockSizeBytes]; _authHMACSizeBytes = _authHMACEncrypt.HashSize / 8; break; default: throw new SecureChannelException(SecureChannelCode.NoMatchingCipherAvailable, _remotePeerEP, _remotePeerUserId); } //init variables _baseStream = inputStream; _bytesSent = 0; _connectedOn = DateTime.UtcNow; if (_renegotiationTimer == null) { if ((_renegotiateAfterBytesSent > 0) || (_renegotiateAfterSeconds > 0)) { _renegotiationTimer = new Timer(delegate(object state) { try { if (((_renegotiateAfterBytesSent > 0) && (_bytesSent > _renegotiateAfterBytesSent)) || ((_renegotiateAfterSeconds > 0) && (_connectedOn.AddSeconds(_renegotiateAfterSeconds) < DateTime.UtcNow))) { Debug.Write(this.GetType().Name, "Renegotiation triggered"); RenegotiateNow(); } } catch (Exception ex) { Debug.Write(this.GetType().Name, ex); } }, null, RENEGOTIATION_TIMER_INTERVAL, RENEGOTIATION_TIMER_INTERVAL); } } }
private void Start(Stream stream) { try { WriteBufferedStream bufferedStream; if (!(stream is WriteBufferedStream)) { bufferedStream = new WriteBufferedStream(stream, 8 * 1024); } else { bufferedStream = stream as WriteBufferedStream; } //read client hello SecureChannelHandshakeHello clientHello = new SecureChannelHandshakeHello(bufferedStream); switch (clientHello.Version) { case 1: ProtocolV1(bufferedStream, clientHello); break; default: throw new SecureChannelException(SecureChannelCode.ProtocolVersionNotSupported, _remotePeerEP, _remotePeerUserId, "SecureChannel protocol version not supported: " + clientHello.Version); } } catch (SecureChannelException ex) { if (ex.Code == SecureChannelCode.RemoteError) { throw new SecureChannelException(ex.Code, _remotePeerEP, _remotePeerUserId, ex.Message, ex); } else { try { Stream s; if (_baseStream == null) { s = stream; } else { s = this; } new SecureChannelHandshakePacket(ex.Code).WriteTo(s); s.Flush(); } catch { } if (ex.PeerEP == null) { throw new SecureChannelException(ex.Code, _remotePeerEP, _remotePeerUserId, ex.Message, ex); } throw; } } catch (IOException) { throw; } catch { try { Stream s; if (_baseStream == null) { s = stream; } else { s = this; } new SecureChannelHandshakePacket(SecureChannelCode.UnknownException).WriteTo(s); s.Flush(); } catch { } throw; } }
private void ProtocolV1(WriteBufferedStream bufferedStream, SecureChannelHandshakeHello clientHello) { #region 1. hello handshake check //select crypto option SecureChannelCipherSuite availableCiphers = _supportedCiphers & clientHello.SupportedCiphers; if (availableCiphers == SecureChannelCipherSuite.None) { throw new SecureChannelException(SecureChannelCode.NoMatchingCipherAvailable, _remotePeerEP, _remotePeerUserId); } else if (availableCiphers.HasFlag(SecureChannelCipherSuite.DHE2048_RSA2048_WITH_AES256_CBC_HMAC_SHA256)) { _selectedCipher = SecureChannelCipherSuite.DHE2048_RSA2048_WITH_AES256_CBC_HMAC_SHA256; } else if (availableCiphers.HasFlag(SecureChannelCipherSuite.DHE2048_ANON_WITH_AES256_CBC_HMAC_SHA256)) { _selectedCipher = SecureChannelCipherSuite.DHE2048_ANON_WITH_AES256_CBC_HMAC_SHA256; } else { throw new SecureChannelException(SecureChannelCode.NoMatchingCipherAvailable, _remotePeerEP, _remotePeerUserId); } //match options if (_options != clientHello.Options) { throw new SecureChannelException(SecureChannelCode.NoMatchingOptionsAvailable, _remotePeerEP, _remotePeerUserId); } //write server hello SecureChannelHandshakeHello serverHello = new SecureChannelHandshakeHello(_selectedCipher, _options); serverHello.WriteTo(bufferedStream); #endregion #region 2. key exchange KeyAgreement keyAgreement; switch (_selectedCipher) { case SecureChannelCipherSuite.DHE2048_ANON_WITH_AES256_CBC_HMAC_SHA256: case SecureChannelCipherSuite.DHE2048_RSA2048_WITH_AES256_CBC_HMAC_SHA256: keyAgreement = new DiffieHellman(DiffieHellmanGroupType.RFC3526_GROUP14_2048BIT, KeyAgreementKeyDerivationFunction.Hmac, KeyAgreementKeyDerivationHashAlgorithm.SHA256); break; default: throw new SecureChannelException(SecureChannelCode.NoMatchingCipherAvailable, _remotePeerEP, _remotePeerUserId); } //send server key exchange data SecureChannelHandshakeKeyExchange serverKeyExchange = new SecureChannelHandshakeKeyExchange(keyAgreement, serverHello, clientHello, _preSharedKey); serverKeyExchange.WriteTo(bufferedStream); bufferedStream.Flush(); //read client key exchange data SecureChannelHandshakeKeyExchange clientKeyExchange = new SecureChannelHandshakeKeyExchange(bufferedStream); if (_options.HasFlag(SecureChannelOptions.PRE_SHARED_KEY_AUTHENTICATION_REQUIRED)) { if (!clientKeyExchange.IsPskAuthValid(serverHello, clientHello, _preSharedKey)) { throw new SecureChannelException(SecureChannelCode.PskAuthenticationFailed, _remotePeerEP, _remotePeerUserId); } } #endregion #region 3. enable encryption EnableEncryption(bufferedStream, serverHello, clientHello, keyAgreement, clientKeyExchange); #endregion #region 4. UserId based authentication switch (_selectedCipher) { case SecureChannelCipherSuite.DHE2048_RSA2048_WITH_AES256_CBC_HMAC_SHA256: if (_options.HasFlag(SecureChannelOptions.CLIENT_AUTHENTICATION_REQUIRED)) { //read client auth SecureChannelHandshakeAuthentication clientAuth = new SecureChannelHandshakeAuthentication(this); _remotePeerUserId = clientAuth.UserId; //authenticate client if (!clientAuth.IsSignatureValid(clientKeyExchange, serverHello, clientHello)) { throw new SecureChannelException(SecureChannelCode.PeerAuthenticationFailed, _remotePeerEP, _remotePeerUserId); } //check if client is trusted if (!clientAuth.IsTrustedUserId(_trustedUserIds)) { throw new SecureChannelException(SecureChannelCode.UntrustedRemotePeerUserId, _remotePeerEP, _remotePeerUserId); } } //write server auth new SecureChannelHandshakeAuthentication(serverKeyExchange, serverHello, clientHello, _userId, _privateKey).WriteTo(this); this.Flush(); break; case SecureChannelCipherSuite.DHE2048_ANON_WITH_AES256_CBC_HMAC_SHA256: break; //no auth for ANON default: throw new SecureChannelException(SecureChannelCode.NoMatchingCipherAvailable, _remotePeerEP, _remotePeerUserId); } #endregion }
public SecureChannelHandshakeAuthentication(SecureChannelHandshakeKeyExchange keyExchange, SecureChannelHandshakeHello serverHello, SecureChannelHandshakeHello clientHello, BinaryNumber userId, byte[] privateKey) : base(SecureChannelCode.None) { switch (serverHello.SupportedCiphers) { case SecureChannelCipherSuite.DHE2048_RSA2048_WITH_AES256_CBC_HMAC_SHA256: _userId = userId; using (RSA rsa = RSA.Create()) { RSAParameters rsaPrivateKey = DEREncoding.DecodeRSAPrivateKey(privateKey); rsa.ImportParameters(rsaPrivateKey); if (rsa.KeySize != 2048) { throw new SecureChannelException(SecureChannelCode.PeerAuthenticationFailed, null, _userId, "RSA key size is not valid for selected crypto option: " + serverHello.SupportedCiphers.ToString()); } _publicKey = DEREncoding.EncodeRSAPublicKey(rsaPrivateKey); if (!SecureChannelStream.IsUserIdValid(_publicKey, _userId)) { throw new SecureChannelException(SecureChannelCode.PeerAuthenticationFailed, null, _userId, "UserId does not match with public key."); } using (MemoryStream mS = new MemoryStream()) { mS.Write(keyExchange.EphemeralPublicKey, 0, keyExchange.EphemeralPublicKey.Length); mS.Write(serverHello.Nonce.Value, 0, serverHello.Nonce.Value.Length); mS.Write(clientHello.Nonce.Value, 0, clientHello.Nonce.Value.Length); mS.Position = 0; _signature = rsa.SignData(mS, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); } } break; default: throw new SecureChannelException(SecureChannelCode.NoMatchingCipherAvailable, null, null); } }
public bool IsPskAuthValid(SecureChannelHandshakeHello serverHello, SecureChannelHandshakeHello clientHello, byte[] psk) { BinaryNumber generatedPskAuthValue = GetPskAuthValue(serverHello.SupportedCiphers, _ephemeralPublicKey, serverHello.Nonce.Value, clientHello.Nonce.Value, psk); return(_pskAuth.Equals(generatedPskAuthValue)); }