public void generateClientKeyExchange(OutputStream os) throws IOException {
   /*
    * RFC 2246 7.4.7.2 If the client certificate already contains a suitable
    * Diffie-Hellman key, then Yc is implicit and does not need to be sent again. In
    * this case, the Client Key Exchange message will be sent, but will be empty.
    */
   if (agreementCredentials != null) {
     TlsUtils.writeUint24(0, os);
   } else {
     generateEphemeralClientKeyExchange(dhAgreeServerPublicKey.getParameters(), os);
   }
 }
  protected DHPublicKeyParameters validateDHPublicKey(DHPublicKeyParameters key)
      throws IOException {
    BigInteger Y = key.getY();
    DHParameters params = key.getParameters();
    BigInteger p = params.getP();
    BigInteger g = params.getG();

    if (!p.isProbablePrime(2)) {
      throw new TlsFatalAlert(AlertDescription.illegal_parameter);
    }
    if (g.compareTo(TWO) < 0 || g.compareTo(p.subtract(TWO)) > 0) {
      throw new TlsFatalAlert(AlertDescription.illegal_parameter);
    }
    if (Y.compareTo(TWO) < 0 || Y.compareTo(p.subtract(ONE)) > 0) {
      throw new TlsFatalAlert(AlertDescription.illegal_parameter);
    }

    // TODO See RFC 2631 for more discussion of Diffie-Hellman validation

    return key;
  }
  @Override
  public byte[] prepareMessageAction() {
    // To use true DH ephemeral we need to precompute the prime number P(DH modulus)
    /**
     * int defaultPrimeProbability = 30;
     *
     * <p>DHParametersGenerator generator = new DHParametersGenerator(); //Genration of a higher bit
     * prime number takes too long (512 bits takes 2 seconds) generator.init(512,
     * defaultPrimeProbability, new SecureRandom()); DHParameters params =
     * generator.generateParameters();
     */
    DHPublicKeyParameters dhPublic;

    // fixed DH modulus P and DH generator G
    byte[] pArray =
        ArrayConverter.hexStringToByteArray(
            "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc"
                + "74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d"
                + "51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24"
                + "117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83"
                + "655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca1821"
                + "7c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf695"
                + "5817183995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff");
    byte[] gArray = {0x02};
    BigInteger p = new BigInteger(1, pArray);
    BigInteger g = new BigInteger(1, gArray);
    DHParameters params = new DHParameters(p, g);

    KeyGenerationParameters kgp = new DHKeyGenerationParameters(new SecureRandom(), params);
    DHKeyPairGenerator keyGen = new DHKeyPairGenerator();
    keyGen.init(kgp);
    AsymmetricCipherKeyPair serverKeyPair = keyGen.generateKeyPair();

    dhPublic = (DHPublicKeyParameters) serverKeyPair.getPublic();
    DHPrivateKeyParameters dhPrivate = (DHPrivateKeyParameters) serverKeyPair.getPrivate();

    protocolMessage.setG(dhPublic.getParameters().getG());
    protocolMessage.setP(dhPublic.getParameters().getP());
    protocolMessage.setPublicKey(dhPublic.getY());
    protocolMessage.setPrivateKey(dhPrivate.getX());
    tlsContext.setServerDHPrivateKeyParameters(dhPrivate);

    byte[] serializedP = BigIntegers.asUnsignedByteArray(protocolMessage.getP().getValue());
    protocolMessage.setSerializedP(serializedP);
    protocolMessage.setSerializedPLength(serializedP.length);

    byte[] serializedG = BigIntegers.asUnsignedByteArray(protocolMessage.getG().getValue());
    protocolMessage.setSerializedG(serializedG);
    protocolMessage.setSerializedGLength(serializedG.length);

    byte[] serializedPublicKey =
        BigIntegers.asUnsignedByteArray(protocolMessage.getPublicKey().getValue());
    protocolMessage.setSerializedPublicKey(serializedPublicKey);
    protocolMessage.setSerializedPublicKeyLength(serializedPublicKey.length);

    byte[] dhParams =
        ArrayConverter.concatenate(
            ArrayConverter.intToBytes(
                protocolMessage.getSerializedPLength().getValue(),
                HandshakeByteLength.DH_PARAM_LENGTH),
            protocolMessage.getSerializedP().getValue(),
            ArrayConverter.intToBytes(
                protocolMessage.getSerializedGLength().getValue(),
                HandshakeByteLength.DH_PARAM_LENGTH),
            protocolMessage.getSerializedG().getValue(),
            ArrayConverter.intToBytes(
                protocolMessage.getSerializedPublicKeyLength().getValue(),
                HandshakeByteLength.DH_PARAM_LENGTH),
            protocolMessage.getSerializedPublicKey().getValue());
    InputStream is = new ByteArrayInputStream(dhParams);

    try {
      ServerDHParams publicKeyParameters = ServerDHParams.parse(is);

      tlsContext.setServerDHParameters(publicKeyParameters);

      KeyStore ks = tlsContext.getKeyStore();

      // could be extended to choose the algorithms depending on the certificate
      SignatureAndHashAlgorithm selectedSignatureHashAlgo =
          new SignatureAndHashAlgorithm(SignatureAlgorithm.RSA, HashAlgorithm.SHA1);
      protocolMessage.setSignatureAlgorithm(
          selectedSignatureHashAlgo.getSignatureAlgorithm().getValue());
      protocolMessage.setHashAlgorithm(selectedSignatureHashAlgo.getHashAlgorithm().getValue());

      Key key = ks.getKey(tlsContext.getAlias(), tlsContext.getPassword().toCharArray());

      RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;

      Signature instance = Signature.getInstance(selectedSignatureHashAlgo.getJavaName());
      instance.initSign(rsaKey);
      LOGGER.debug(
          "SignatureAndHashAlgorithm for ServerKeyExchange message: {}",
          selectedSignatureHashAlgo.getJavaName());

      byte[] toBeSignedBytes =
          ArrayConverter.concatenate(
              tlsContext.getClientRandom(), tlsContext.getServerRandom(), dhParams);

      instance.update(toBeSignedBytes);
      byte[] signature = instance.sign();
      protocolMessage.setSignature(signature);
      protocolMessage.setSignatureLength(signature.length);

      byte[] result =
          ArrayConverter.concatenate(
              dhParams,
              new byte[] {
                protocolMessage.getHashAlgorithm().getValue(),
                protocolMessage.getSignatureAlgorithm().getValue()
              },
              ArrayConverter.intToBytes(
                  protocolMessage.getSignatureLength().getValue(),
                  HandshakeByteLength.SIGNATURE_LENGTH),
              protocolMessage.getSignature().getValue());

      protocolMessage.setLength(result.length);

      long header =
          (HandshakeMessageType.SERVER_KEY_EXCHANGE.getValue() << 24)
              + protocolMessage.getLength().getValue();

      protocolMessage.setCompleteResultingMessage(
          ArrayConverter.concatenate(ArrayConverter.longToUint32Bytes(header), result));

    } catch (KeyStoreException
        | NoSuchAlgorithmException
        | UnrecoverableKeyException
        | InvalidKeyException
        | SignatureException
        | IOException ex) {
      throw new ConfigurationException(ex.getLocalizedMessage(), ex);
    }

    return protocolMessage.getCompleteResultingMessage().getValue();
  }