package org.bouncycastle.crypto.tls;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.Vector;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.agreement.srp.SRP6Client;
import org.bouncycastle.crypto.agreement.srp.SRP6Server;
import org.bouncycastle.crypto.agreement.srp.SRP6Util;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.SRP6GroupParameters;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.io.TeeInputStream;
public class TlsSRPKeyExchange extends AbstractTlsKeyExchange
{
protected static TlsSigner createSigner(int keyExchange)
{
switch (keyExchange)
{
case KeyExchangeAlgorithm.SRP:
return null;
case KeyExchangeAlgorithm.SRP_RSA:
return new TlsRSASigner();
case KeyExchangeAlgorithm.SRP_DSS:
return new TlsDSSSigner();
default:
throw new IllegalArgumentException("unsupported key exchange algorithm");
}
}
protected TlsSigner tlsSigner;
protected TlsSRPGroupVerifier groupVerifier;
protected byte[] identity;
protected byte[] password;
protected AsymmetricKeyParameter serverPublicKey = null;
protected SRP6GroupParameters srpGroup = null;
protected SRP6Client srpClient = null;
protected SRP6Server srpServer = null;
protected BigInteger srpPeerCredentials = null;
protected BigInteger srpVerifier = null;
protected byte[] srpSalt = null;
protected TlsSignerCredentials serverCredentials = null;
public TlsSRPKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, byte[] identity, byte[] password)
{
this(keyExchange, supportedSignatureAlgorithms, new DefaultTlsSRPGroupVerifier(), identity, password);
}
public TlsSRPKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, TlsSRPGroupVerifier groupVerifier,
byte[] identity, byte[] password)
{
super(keyExchange, supportedSignatureAlgorithms);
this.tlsSigner = createSigner(keyExchange);
this.groupVerifier = groupVerifier;
this.identity = identity;
this.password = password;
this.srpClient = new SRP6Client();
}
public TlsSRPKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, byte[] identity,
TlsSRPLoginParameters loginParameters)
{
super(keyExchange, supportedSignatureAlgorithms);
this.tlsSigner = createSigner(keyExchange);
this.identity = identity;
this.srpServer = new SRP6Server();
this.srpGroup = loginParameters.getGroup();
this.srpVerifier = loginParameters.getVerifier();
this.srpSalt = loginParameters.getSalt();
}
public void init(TlsContext context)
{
super.init(context);
if (this.tlsSigner != null)
{
this.tlsSigner.init(context);
}
}
public void skipServerCredentials() throws IOException
{
if (tlsSigner != null)
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
}
public void processServerCertificate(Certificate serverCertificate) throws IOException
{
if (tlsSigner == null)
{
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.isValidPublicKey(this.serverPublicKey))
{
throw new TlsFatalAlert(AlertDescription.certificate_unknown);
}
TlsUtils.validateKeyUsage(x509Cert, KeyUsage.digitalSignature);
super.processServerCertificate(serverCertificate);
}
public void processServerCredentials(TlsCredentials serverCredentials)
throws IOException
{
if ((keyExchange == KeyExchangeAlgorithm.SRP) || !(serverCredentials instanceof TlsSignerCredentials))
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
processServerCertificate(serverCredentials.getCertificate());
this.serverCredentials = (TlsSignerCredentials)serverCredentials;
}
public boolean requiresServerKeyExchange()
{
return true;
}
public byte[] generateServerKeyExchange() throws IOException
{
srpServer.init(srpGroup, srpVerifier, TlsUtils.createHash(HashAlgorithm.sha1), context.getSecureRandom());
BigInteger B = srpServer.generateServerCredentials();
ServerSRPParams srpParams = new ServerSRPParams(srpGroup.getN(), srpGroup.getG(), srpSalt, B);
DigestInputBuffer buf = new DigestInputBuffer();
srpParams.encode(buf);
if (serverCredentials != null)
{
SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtils.getSignatureAndHashAlgorithm(
context, serverCredentials);
Digest d = TlsUtils.createHash(signatureAndHashAlgorithm);
SecurityParameters securityParameters = context.getSecurityParameters();
d.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
d.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
buf.updateDigest(d);
byte[] hash = new byte[d.getDigestSize()];
d.doFinal(hash, 0);
byte[] signature = serverCredentials.generateCertificateSignature(hash);
DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature);
signed_params.encode(buf);
}
return buf.toByteArray();
}
public void processServerKeyExchange(InputStream input) throws IOException
{
SecurityParameters securityParameters = context.getSecurityParameters();
SignerInputBuffer buf = null;
InputStream teeIn = input;
if (tlsSigner != null)
{
buf = new SignerInputBuffer();
teeIn = new TeeInputStream(input, buf);
}
ServerSRPParams srpParams = ServerSRPParams.parse(teeIn);
if (buf != null)
{
DigitallySigned signed_params = parseSignature(input);
Signer signer = initVerifyer(tlsSigner, signed_params.getAlgorithm(), securityParameters);
buf.updateSigner(signer);
if (!signer.verifySignature(signed_params.getSignature()))
{
throw new TlsFatalAlert(AlertDescription.decrypt_error);
}
}
this.srpGroup = new SRP6GroupParameters(srpParams.getN(), srpParams.getG());
if (!groupVerifier.accept(srpGroup))
{
throw new TlsFatalAlert(AlertDescription.insufficient_security);
}
this.srpSalt = srpParams.getS();
try
{
this.srpPeerCredentials = SRP6Util.validatePublicValue(srpGroup.getN(), srpParams.getB());
}
catch (CryptoException e)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
this.srpClient.init(srpGroup, TlsUtils.createHash(HashAlgorithm.sha1), context.getSecureRandom());
}
public void validateCertificateRequest(CertificateRequest certificateRequest) throws IOException
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
public void processClientCredentials(TlsCredentials clientCredentials) throws IOException
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
public void generateClientKeyExchange(OutputStream output) throws IOException
{
BigInteger A = srpClient.generateClientCredentials(srpSalt, identity, password);
TlsSRPUtils.writeSRPParameter(A, output);
context.getSecurityParameters().srpIdentity = Arrays.clone(identity);
}
public void processClientKeyExchange(InputStream input) throws IOException
{
try
{
this.srpPeerCredentials = SRP6Util.validatePublicValue(srpGroup.getN(), TlsSRPUtils.readSRPParameter(input));
}
catch (CryptoException e)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
context.getSecurityParameters().srpIdentity = Arrays.clone(identity);
}
public byte[] generatePremasterSecret() throws IOException
{
try
{
BigInteger S = srpServer != null
? srpServer.calculateSecret(srpPeerCredentials)
: srpClient.calculateSecret(srpPeerCredentials);
return BigIntegers.asUnsignedByteArray(S);
}
catch (CryptoException e)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
}
protected Signer initVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, SecurityParameters securityParameters)
{
Signer signer = tlsSigner.createVerifyer(algorithm, this.serverPublicKey);
signer.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
signer.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
return signer;
}
}