org.bouncycastle.crypto.tls.TlsDHKeyExchange Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcprov-ext-debug-jdk15on Show documentation
Show all versions of bcprov-ext-debug-jdk15on Show documentation
The Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains JCE provider and lightweight API for the Bouncy Castle Cryptography APIs for JDK 1.5 to JDK 1.8. Note: this package includes the NTRU encryption algorithms.
package org.bouncycastle.crypto.tls;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Vector;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DHParameters;
import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
import org.bouncycastle.crypto.params.DHPublicKeyParameters;
import org.bouncycastle.crypto.util.PublicKeyFactory;
/**
* (D)TLS DH key exchange.
*/
public class TlsDHKeyExchange
extends AbstractTlsKeyExchange
{
protected TlsSigner tlsSigner;
protected TlsDHVerifier dhVerifier;
protected DHParameters dhParameters;
protected AsymmetricKeyParameter serverPublicKey;
protected TlsAgreementCredentials agreementCredentials;
protected DHPrivateKeyParameters dhAgreePrivateKey;
protected DHPublicKeyParameters dhAgreePublicKey;
/**
* @deprecated Use constructor that takes a TlsDHVerifier
*/
public TlsDHKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, DHParameters dhParameters)
{
this(keyExchange, supportedSignatureAlgorithms, new DefaultTlsDHVerifier(), dhParameters);
}
public TlsDHKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, TlsDHVerifier dhVerifier, DHParameters dhParameters)
{
super(keyExchange, supportedSignatureAlgorithms);
switch (keyExchange)
{
case KeyExchangeAlgorithm.DH_anon:
case KeyExchangeAlgorithm.DH_RSA:
case KeyExchangeAlgorithm.DH_DSS:
this.tlsSigner = null;
break;
case KeyExchangeAlgorithm.DHE_RSA:
this.tlsSigner = new TlsRSASigner();
break;
case KeyExchangeAlgorithm.DHE_DSS:
this.tlsSigner = new TlsDSSSigner();
break;
default:
throw new IllegalArgumentException("unsupported key exchange algorithm");
}
this.dhVerifier = dhVerifier;
this.dhParameters = dhParameters;
}
public void init(TlsContext context)
{
super.init(context);
if (this.tlsSigner != null)
{
this.tlsSigner.init(context);
}
}
public void skipServerCredentials()
throws IOException
{
if (keyExchange != KeyExchangeAlgorithm.DH_anon)
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
}
public void processServerCertificate(Certificate serverCertificate)
throws IOException
{
if (keyExchange == KeyExchangeAlgorithm.DH_anon)
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
if (serverCertificate.isEmpty())
{
throw new TlsFatalAlert(AlertDescription.bad_certificate);
}
org.bouncycastle.asn1.x509.Certificate x509Cert = serverCertificate.getCertificateAt(0);
SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
try
{
this.serverPublicKey = PublicKeyFactory.createKey(keyInfo);
}
catch (RuntimeException e)
{
throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
}
if (tlsSigner == null)
{
try
{
this.dhAgreePublicKey = (DHPublicKeyParameters)this.serverPublicKey;
this.dhParameters = dhAgreePublicKey.getParameters();
}
catch (ClassCastException e)
{
throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
}
TlsUtils.validateKeyUsage(x509Cert, KeyUsage.keyAgreement);
}
else
{
if (!tlsSigner.isValidPublicKey(this.serverPublicKey))
{
throw new TlsFatalAlert(AlertDescription.certificate_unknown);
}
TlsUtils.validateKeyUsage(x509Cert, KeyUsage.digitalSignature);
}
super.processServerCertificate(serverCertificate);
}
public boolean requiresServerKeyExchange()
{
switch (keyExchange)
{
case KeyExchangeAlgorithm.DH_anon:
case KeyExchangeAlgorithm.DHE_DSS:
case KeyExchangeAlgorithm.DHE_RSA:
return true;
default:
return false;
}
}
public byte[] generateServerKeyExchange() throws IOException
{
if (!requiresServerKeyExchange())
{
return null;
}
// DH_anon is handled here, DHE_* in a subclass
ByteArrayOutputStream buf = new ByteArrayOutputStream();
this.dhAgreePrivateKey = TlsDHUtils.generateEphemeralServerKeyExchange(context.getSecureRandom(),
this.dhParameters, buf);
return buf.toByteArray();
}
public void processServerKeyExchange(InputStream input) throws IOException
{
if (!requiresServerKeyExchange())
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
// DH_anon is handled here, DHE_* in a subclass
this.dhParameters = TlsDHUtils.receiveDHParameters(dhVerifier, input);
this.dhAgreePublicKey = new DHPublicKeyParameters(TlsDHUtils.readDHParameter(input), dhParameters);
}
public void validateCertificateRequest(CertificateRequest certificateRequest)
throws IOException
{
if (keyExchange == KeyExchangeAlgorithm.DH_anon)
{
throw new TlsFatalAlert(AlertDescription.handshake_failure);
}
short[] types = certificateRequest.getCertificateTypes();
for (int i = 0; i < types.length; ++i)
{
switch (types[i])
{
case ClientCertificateType.rsa_sign:
case ClientCertificateType.dss_sign:
case ClientCertificateType.rsa_fixed_dh:
case ClientCertificateType.dss_fixed_dh:
case ClientCertificateType.ecdsa_sign:
break;
default:
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
}
}
public void processClientCredentials(TlsCredentials clientCredentials)
throws IOException
{
if (keyExchange == KeyExchangeAlgorithm.DH_anon)
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
if (clientCredentials instanceof TlsAgreementCredentials)
{
// TODO Validate client cert has matching parameters (see 'areCompatibleParameters')?
this.agreementCredentials = (TlsAgreementCredentials)clientCredentials;
}
else if (clientCredentials instanceof TlsSignerCredentials)
{
// OK
}
else
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
public void generateClientKeyExchange(OutputStream output)
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)
{
this.dhAgreePrivateKey = TlsDHUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(),
dhParameters, output);
}
}
public void processClientCertificate(Certificate clientCertificate) throws IOException
{
if (keyExchange == KeyExchangeAlgorithm.DH_anon)
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
// TODO Extract the public key
// TODO If the certificate is 'fixed', take the public key as dhAgreePublicKey
}
public void processClientKeyExchange(InputStream input) throws IOException
{
if (dhAgreePublicKey != null)
{
// For dss_fixed_dh and rsa_fixed_dh, the key arrived in the client certificate
return;
}
this.dhAgreePublicKey = new DHPublicKeyParameters(TlsDHUtils.readDHParameter(input), dhParameters);
}
public byte[] generatePremasterSecret()
throws IOException
{
if (agreementCredentials != null)
{
return agreementCredentials.generateAgreement(dhAgreePublicKey);
}
if (dhAgreePrivateKey != null)
{
return TlsDHUtils.calculateDHBasicAgreement(dhAgreePublicKey, dhAgreePrivateKey);
}
throw new TlsFatalAlert(AlertDescription.internal_error);
}
}