package org.bouncycastle.crypto.tls;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.X509CertificateStructure;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import org.bouncycastle.crypto.engines.RSABlindedEngine;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.util.PublicKeyFactory;

TLS 1.0 RSA key exchange.
/** * TLS 1.0 RSA key exchange. */
class TlsRSAKeyExchange implements TlsKeyExchange { protected TlsClientContext context; protected AsymmetricKeyParameter serverPublicKey = null; protected RSAKeyParameters rsaServerPublicKey = null; protected byte[] premasterSecret; TlsRSAKeyExchange(TlsClientContext context) { this.context = context; } public void skipServerCertificate() throws IOException { throw new TlsFatalAlert(AlertDescription.unexpected_message); } public void processServerCertificate(Certificate serverCertificate) throws IOException { X509CertificateStructure x509Cert = serverCertificate.certs[0]; SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo(); try { this.serverPublicKey = PublicKeyFactory.createKey(keyInfo); } catch (RuntimeException e) { throw new TlsFatalAlert(AlertDescription.unsupported_certificate); } // Sanity check the PublicKeyFactory if (this.serverPublicKey.isPrivate()) { throw new TlsFatalAlert(AlertDescription.internal_error); } this.rsaServerPublicKey = validateRSAPublicKey((RSAKeyParameters)this.serverPublicKey); TlsUtils.validateKeyUsage(x509Cert, KeyUsage.keyEncipherment); // TODO /* * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the * signing algorithm for the certificate must be the same as the algorithm for the * certificate key." */ } public void skipServerKeyExchange() throws IOException { // OK } public void processServerKeyExchange(InputStream is) throws IOException { // TODO throw new TlsFatalAlert(AlertDescription.unexpected_message); } public void validateCertificateRequest(CertificateRequest certificateRequest) throws IOException { 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.ecdsa_sign: break; default: throw new TlsFatalAlert(AlertDescription.illegal_parameter); } } } public void skipClientCredentials() throws IOException { // OK } public void processClientCredentials(TlsCredentials clientCredentials) throws IOException { if (!(clientCredentials instanceof TlsSignerCredentials)) { throw new TlsFatalAlert(AlertDescription.internal_error); } } public void generateClientKeyExchange(OutputStream os) throws IOException { /* * Choose a PremasterSecret and send it encrypted to the server */ premasterSecret = new byte[48]; context.getSecureRandom().nextBytes(premasterSecret); TlsUtils.writeVersion(premasterSecret, 0); PKCS1Encoding encoding = new PKCS1Encoding(new RSABlindedEngine()); encoding.init(true, new ParametersWithRandom(this.rsaServerPublicKey, context.getSecureRandom())); try { byte[] keData = encoding.processBlock(premasterSecret, 0, premasterSecret.length); TlsUtils.writeUint24(keData.length + 2, os); TlsUtils.writeOpaque16(keData, os); } catch (InvalidCipherTextException e) { /* * This should never happen, only during decryption. */ throw new TlsFatalAlert(AlertDescription.internal_error); } } public byte[] generatePremasterSecret() throws IOException { byte[] tmp = this.premasterSecret; this.premasterSecret = null; return tmp; } // Would be needed to process RSA_EXPORT server key exchange // protected void processRSAServerKeyExchange(InputStream is, Signer signer) throws IOException // { // InputStream sigIn = is; // if (signer != null) // { // sigIn = new SignerInputStream(is, signer); // } // // byte[] modulusBytes = TlsUtils.readOpaque16(sigIn); // byte[] exponentBytes = TlsUtils.readOpaque16(sigIn); // // if (signer != null) // { // byte[] sigByte = TlsUtils.readOpaque16(is); // // if (!signer.verifySignature(sigByte)) // { // handler.failWithError(AlertLevel.fatal, AlertDescription.bad_certificate); // } // } // // BigInteger modulus = new BigInteger(1, modulusBytes); // BigInteger exponent = new BigInteger(1, exponentBytes); // // this.rsaServerPublicKey = validateRSAPublicKey(new RSAKeyParameters(false, modulus, // exponent)); // } protected RSAKeyParameters validateRSAPublicKey(RSAKeyParameters key) throws IOException { // TODO What is the minimum bit length required? // key.getModulus().bitLength(); if (!key.getExponent().isProbablePrime(2)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } return key; } }