示例#1
0
        /// <summary>
        /// Securely exchanges a secret key
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="ecParams">ECDSA public / private keypair used for signing</param>
        /// <returns>A tuple containing a 256 bit hashed secret key, and the fingerprint of the remote</returns>
        /// <exception cref="CryptographicException"></exception>
        /// <exception cref="InvalidDataException">Thrown when the remote sends invalid data</exception>
        public static async Task <(byte[], ECPoint)> ExchangeKeyAsync(this WsStream stream, ECParameters ecParams)
        {
            if (ecParams.D is null)
            {
                throw new CryptographicException("Private key must be provided");
            }
            ECDsa ecDsa = ECDsa.Create(ecParams);
            // TODO: Harden security (prevent abuse, double check everything)
            // host authentication
            var pubBytes = ecDsa.ExportSubjectPublicKeyInfo();

            // key exchange
            var ecdh       = ECDiffieHellman.Create();
            var kePubBytes = ecdh.ExportSubjectPublicKeyInfo();

            // sign ecdh key to authenticate
            var signed = ecDsa.SignData(kePubBytes, HashAlgorithmName.SHA256);

            var bw = new AsyncBinaryWriter(stream);
            var br = new AsyncBinaryReader(stream);
            //1
            await bw.WriteAsync(pubBytes.Length);

            await bw.WriteAsync(pubBytes);

            //2
            await bw.WriteAsync(signed.Length);

            await bw.WriteAsync(signed);

            //3
            await bw.WriteAsync(kePubBytes.Length);

            await bw.WriteAsync(kePubBytes);

            // read remote public key and verify signature
            //1
            var remotePubKey   = ECDsa.Create();
            var remotePubBytes = await br.ReadBytesAsync(await br.ReadAssertAsync(120));

            remotePubKey.ImportSubjectPublicKeyInfo(remotePubBytes, out _);
            //2
            var remoteSignature = await br.ReadBytesAsync(await br.ReadAssertAsync(96));

            //3
            var remoteKePub = await br.ReadBytesAsync(await br.ReadAssertAsync(158));

            var remoteEcdh = ECDiffieHellman.Create();

            remoteEcdh.ImportSubjectPublicKeyInfo(remoteKePub, out _);

            // verify signed public key exchange key
            if (!remotePubKey.VerifyData(remoteKePub, remoteSignature, HashAlgorithmName.SHA256))
            {
                throw new CryptographicException("Remote public key does not match hash!");
            }
            // derive shared secret
            var sharedSecret = ecdh.DeriveKeyMaterial(remoteEcdh.PublicKey);

            // return the public key (fingerprint) of the remote, and the hashed shared secret
            return(SHA256.HashData(sharedSecret), remotePubKey.ExportParameters(false).Q);
        }