org.bouncycastle.tls.TlsSRPKeyExchange Maven / Gradle / Ivy
package org.bouncycastle.tls;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.Vector;
import org.bouncycastle.tls.crypto.TlsSRP6Client;
import org.bouncycastle.tls.crypto.TlsSRP6Server;
import org.bouncycastle.tls.crypto.TlsSRPConfig;
import org.bouncycastle.tls.crypto.TlsSecret;
import org.bouncycastle.tls.crypto.TlsVerifier;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.io.TeeInputStream;
/**
* (D)TLS SRP key exchange (RFC 5054).
*/
public class TlsSRPKeyExchange
extends AbstractTlsKeyExchange
{
private static int checkKeyExchange(int keyExchange)
{
switch (keyExchange)
{
case KeyExchangeAlgorithm.SRP:
case KeyExchangeAlgorithm.SRP_DSS:
case KeyExchangeAlgorithm.SRP_RSA:
return keyExchange;
default:
throw new IllegalArgumentException("unsupported key exchange algorithm");
}
}
protected TlsSRPConfigVerifier srpConfigVerifier;
protected byte[] identity;
protected byte[] password;
protected TlsSRPConfig srpConfig = null;
protected TlsSRP6Client srpClient = null;
protected TlsSRP6Server srpServer = null;
protected BigInteger srpPeerCredentials = null;
protected BigInteger srpVerifier = null;
protected byte[] srpSalt = null;
protected TlsCredentialedSigner serverCredentials = null;
protected TlsVerifier verifier = null;
public TlsSRPKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, TlsSRPConfigVerifier srpConfigVerifier,
byte[] identity, byte[] password)
{
super(checkKeyExchange(keyExchange), supportedSignatureAlgorithms);
this.srpConfigVerifier = srpConfigVerifier;
this.identity = identity;
this.password = password;
}
public TlsSRPKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, byte[] identity,
TlsSRPLoginParameters loginParameters)
{
super(checkKeyExchange(keyExchange), supportedSignatureAlgorithms);
this.identity = identity;
this.srpConfig = loginParameters.getConfig();
this.srpVerifier = loginParameters.getVerifier();
this.srpSalt = loginParameters.getSalt();
}
public void skipServerCredentials() throws IOException
{
if (keyExchange != KeyExchangeAlgorithm.SRP)
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
public void processServerCredentials(TlsCredentials serverCredentials) throws IOException
{
if (keyExchange == KeyExchangeAlgorithm.SRP)
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
if (!(serverCredentials instanceof TlsCredentialedSigner))
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
this.serverCredentials = (TlsCredentialedSigner)serverCredentials;
}
public void processServerCertificate(Certificate serverCertificate) throws IOException
{
if (keyExchange == KeyExchangeAlgorithm.SRP)
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
if (serverCertificate.isEmpty())
{
throw new TlsFatalAlert(AlertDescription.bad_certificate);
}
checkServerCertSigAlg(serverCertificate);
this.verifier = serverCertificate.getCertificateAt(0)
.createVerifier(TlsUtils.getSignatureAlgorithm(keyExchange));
}
public boolean requiresServerKeyExchange()
{
return true;
}
public byte[] generateServerKeyExchange() throws IOException
{
srpServer = context.getCrypto().createSRP6Server(srpConfig, srpVerifier);
BigInteger B = srpServer.generateServerCredentials();
BigInteger[] ng = srpConfig.getExplicitNG();
ServerSRPParams srpParams = new ServerSRPParams(ng[0], ng[1], srpSalt, B);
DigestInputBuffer buf = new DigestInputBuffer();
srpParams.encode(buf);
if (serverCredentials != null)
{
DigitallySigned signedParams = TlsUtils.generateServerKeyExchangeSignature(context, serverCredentials, buf);
signedParams.encode(buf);
}
return buf.toByteArray();
}
public void processServerKeyExchange(InputStream input) throws IOException
{
DigestInputBuffer buf = null;
InputStream teeIn = input;
if (keyExchange != KeyExchangeAlgorithm.SRP)
{
buf = new DigestInputBuffer();
teeIn = new TeeInputStream(input, buf);
}
ServerSRPParams srpParams = ServerSRPParams.parse(teeIn);
if (buf != null)
{
DigitallySigned signedParams = parseSignature(input);
TlsUtils.verifyServerKeyExchangeSignature(context, verifier, buf, signedParams);
}
this.srpConfig = new TlsSRPConfig();
srpConfig.setExplicitNG(new BigInteger[]{ srpParams.getN(), srpParams.getG() });
if (!srpConfigVerifier.accept(srpConfig))
{
throw new TlsFatalAlert(AlertDescription.insufficient_security);
}
this.srpSalt = srpParams.getS();
/*
* RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter" alert if
* B % N = 0.
*/
this.srpPeerCredentials = validatePublicValue(srpParams.getN(), srpParams.getB());
this.srpClient = context.getCrypto().createSRP6Client(srpConfig);
}
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
{
/*
* RFC 5054 2.5.4: The server MUST abort the handshake with an "illegal_parameter" alert if
* A % N = 0.
*/
this.srpPeerCredentials = validatePublicValue(srpConfig.getExplicitNG()[0], TlsSRPUtils.readSRPParameter(input));
context.getSecurityParameters().srpIdentity = Arrays.clone(identity);
}
public TlsSecret generatePreMasterSecret() throws IOException
{
BigInteger S = srpServer != null
? srpServer.calculateSecret(srpPeerCredentials)
: srpClient.calculateSecret(srpPeerCredentials);
// TODO Check if this needs to be a fixed size
return context.getCrypto().createSecret(BigIntegers.asUnsignedByteArray(S));
}
public static BigInteger validatePublicValue(BigInteger N, BigInteger val)
throws IOException
{
val = val.mod(N);
// Check that val % N != 0
if (val.equals(BigInteger.ZERO))
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter, new IllegalArgumentException("Invalid public value: 0"));
}
return val;
}
}