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; /** * (D)TLS SRP key exchange (RFC 5054). */ 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; /** * @deprecated Use constructor taking an explicit 'groupVerifier' argument */ 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) { /* * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 */ 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(); /* * RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter" alert if * B % N = 0. */ 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 { /* * RFC 5054 2.5.4: The server MUST abort the handshake with an "illegal_parameter" alert if * A % N = 0. */ 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); // TODO Check if this needs to be a fixed size 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; } }