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 }
private void Start(Stream stream) { try { WriteBufferedStream bufferedStream; if (!(stream is WriteBufferedStream)) { bufferedStream = new WriteBufferedStream(stream, 8 * 1024); } else { bufferedStream = stream as WriteBufferedStream; } //write client hello SecureChannelHandshakeHello clientHello = new SecureChannelHandshakeHello(_supportedCiphers, _options); clientHello.WriteTo(bufferedStream); bufferedStream.Flush(); //read server hello SecureChannelHandshakeHello serverHello = new SecureChannelHandshakeHello(bufferedStream); switch (serverHello.Version) { case 1: ProtocolV1(bufferedStream, serverHello, clientHello); break; default: throw new SecureChannelException(SecureChannelCode.ProtocolVersionNotSupported, _remotePeerEP, _remotePeerUserId, "SecureChannel protocol version not supported: " + serverHello.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; } }