/// <summary>
        ///
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="serverId"></param>
        /// <param name="clientKexInit"></param>
        /// <param name="serverKexInit"></param>
        public override void PerformClientExchange(String clientId,
                                                   String serverId,
                                                   byte[] clientKexInit,
                                                   byte[] serverKexInit)
        {
            this.clientId      = clientId;
            this.serverId      = serverId;
            this.clientKexInit = clientKexInit;
            this.serverKexInit = serverKexInit;

            BigInteger q = p.subtract(BigInteger.ONE).divide(g);

            do
            {
                x = new BigInteger(p.bitLength(), new RNGCryptoServiceProvider());
            }while((x.compareTo(BigInteger.ONE) < 0) &&
                   (x.compareTo(q) > 0));

            e = g.modPow(x, p);

            if (e.compareTo(BigInteger.ONE) < 0 ||
                e.compareTo(p.subtract(BigInteger.ONE)) > 0)
            {
                throw new SSHException("Key exchange failed to generate e value", SSHException.INTERNAL_ERROR);
            }

            SSHPacket packet = transport.GetSSHPacket(true);

            packet.WriteByte(SSH_MSG_KEXDH_INIT);
            packet.WriteBigInteger(e);

            transport.SendMessage(packet);

            packet = transport.NextMessage();

            if (packet.MessageID != SSH_MSG_KEXDH_REPLY)
            {
                throw new SSHException("Expected SSH_MSG_KEXDH_REPLY but got message id "
                                       + packet.MessageID, SSHException.KEY_EXCHANGE_FAILED);
            }

            hostKey   = packet.ReadBinaryString();
            f         = packet.ReadBigInteger();
            signature = packet.ReadBinaryString();

            secret = f.modPow(x, p);

            CalculateExchangeHash();
        }