< prev index next >
src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java
Print this page
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
@@ -21,249 +21,300 @@
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
-
package sun.security.ssl;
-import java.io.*;
-import java.security.*;
-
-import javax.crypto.*;
-
-import javax.net.ssl.*;
-
-import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
-import sun.security.util.KeyUtil;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.text.MessageFormat;
+import java.util.Locale;
+import javax.crypto.SecretKey;
+import sun.security.ssl.RSAKeyExchange.EphemeralRSACredentials;
+import sun.security.ssl.RSAKeyExchange.EphemeralRSAPossession;
+import sun.security.ssl.RSAKeyExchange.RSAPremasterSecret;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.X509Authentication.X509Credentials;
+import sun.security.ssl.X509Authentication.X509Possession;
+import sun.security.util.HexDumpEncoder;
/**
- * This is the client key exchange message (CLIENT --> SERVER) used with
- * all RSA key exchanges; it holds the RSA-encrypted pre-master secret.
- *
- * The message is encrypted using PKCS #1 block type 02 encryption with the
- * server's public key. The padding and resulting message size is a function
- * of this server's public key modulus size, but the pre-master secret is
- * always exactly 48 bytes.
- *
+ * Pack of the "ClientKeyExchange" handshake message.
*/
-final class RSAClientKeyExchange extends HandshakeMessage {
+final class RSAClientKeyExchange {
+ static final SSLConsumer rsaHandshakeConsumer =
+ new RSAClientKeyExchangeConsumer();
+ static final HandshakeProducer rsaHandshakeProducer =
+ new RSAClientKeyExchangeProducer();
- /*
- * The following field values were encrypted with the server's public
- * key (or temp key from server key exchange msg) and are presented
- * here in DECRYPTED form.
+ /**
+ * The RSA ClientKeyExchange handshake message.
*/
- private ProtocolVersion protocolVersion; // preMaster [0,1]
- SecretKey preMaster;
- private byte[] encrypted; // same size as public modulus
-
- /*
- * Client randomly creates a pre-master secret and encrypts it
- * using the server's RSA public key; only the server can decrypt
- * it, using its RSA private key. Result is the same size as the
- * server's public key, and uses PKCS #1 block format 02.
- */
- @SuppressWarnings("deprecation")
- RSAClientKeyExchange(ProtocolVersion protocolVersion,
- ProtocolVersion maxVersion,
- SecureRandom generator, PublicKey publicKey) throws IOException {
- if (publicKey.getAlgorithm().equals("RSA") == false) {
- throw new SSLKeyException("Public key not of type RSA: " +
- publicKey.getAlgorithm());
+ private static final
+ class RSAClientKeyExchangeMessage extends HandshakeMessage {
+ final int protocolVersion;
+ final boolean useTLS10PlusSpec;
+ final byte[] encrypted;
+
+ RSAClientKeyExchangeMessage(HandshakeContext context,
+ RSAPremasterSecret premaster,
+ PublicKey publicKey) throws GeneralSecurityException {
+ super(context);
+ this.protocolVersion = context.clientHelloVersion;
+ this.encrypted = premaster.getEncoded(
+ publicKey, context.sslContext.getSecureRandom());
+ this.useTLS10PlusSpec = ProtocolVersion.useTLS10PlusSpec(
+ protocolVersion, context.sslContext.isDTLS());
+ }
+
+ RSAClientKeyExchangeMessage(HandshakeContext context,
+ ByteBuffer m) throws IOException {
+ super(context);
+ // This happens in server side only.
+ ServerHandshakeContext shc =
+ (ServerHandshakeContext)handshakeContext;
+
+ if (m.remaining() < 2) {
+ shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+ "Invalid RSA ClientKeyExchange message: insufficient data");
+ }
+
+ this.protocolVersion = context.clientHelloVersion;
+ this.useTLS10PlusSpec = ProtocolVersion.useTLS10PlusSpec(
+ protocolVersion, context.sslContext.isDTLS());
+ if (useTLS10PlusSpec) {
+ this.encrypted = Record.getBytes16(m);
+ } else { // SSL 3.0
+ this.encrypted = new byte[m.remaining()];
+ m.get(encrypted);
}
- this.protocolVersion = protocolVersion;
-
- try {
- String s = protocolVersion.useTLS12PlusSpec() ?
- "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret";
- KeyGenerator kg = JsseJce.getKeyGenerator(s);
- kg.init(new TlsRsaPremasterSecretParameterSpec(
- maxVersion.v, protocolVersion.v), generator);
- preMaster = kg.generateKey();
-
- Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
- cipher.init(Cipher.WRAP_MODE, publicKey, generator);
- encrypted = cipher.wrap(preMaster);
- } catch (GeneralSecurityException e) {
- throw (SSLKeyException)new SSLKeyException
- ("RSA premaster secret error").initCause(e);
}
+
+ @Override
+ public SSLHandshake handshakeType() {
+ return SSLHandshake.CLIENT_KEY_EXCHANGE;
}
- /*
- * Retrieving the cipher's provider name for the debug purposes
- * can throw an exception by itself.
- */
- private static String safeProviderName(Cipher cipher) {
- try {
- return cipher.getProvider().toString();
- } catch (Exception e) {
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Retrieving The Cipher provider name" +
- " caused exception " + e.getMessage());
+ @Override
+ public int messageLength() {
+ if (useTLS10PlusSpec) {
+ return encrypted.length + 2;
+ } else {
+ return encrypted.length;
}
}
- try {
- return cipher.toString() + " (provider name not available)";
- } catch (Exception e) {
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Retrieving The Cipher name" +
- " caused exception " + e.getMessage());
+
+ @Override
+ public void send(HandshakeOutStream hos) throws IOException {
+ if (useTLS10PlusSpec) {
+ hos.putBytes16(encrypted);
+ } else {
+ hos.write(encrypted);
+ }
}
+
+ @Override
+ public String toString() {
+ MessageFormat messageFormat = new MessageFormat(
+ "\"RSA ClientKeyExchange\": '{'\n" +
+ " \"client_version\": {0}\n" +
+ " \"encncrypted\": '{'\n" +
+ "{1}\n" +
+ " '}'\n" +
+ "'}'",
+ Locale.ENGLISH);
+
+ HexDumpEncoder hexEncoder = new HexDumpEncoder();
+ Object[] messageFields = {
+ ProtocolVersion.nameOf(protocolVersion),
+ Utilities.indent(
+ hexEncoder.encodeBuffer(encrypted), " "),
+ };
+ return messageFormat.format(messageFields);
}
- return "(cipher/provider names not available)";
}
- /*
- * Server gets the PKCS #1 (block format 02) data, decrypts
- * it with its private key.
+ /**
+ * The RSA "ClientKeyExchange" handshake message producer.
*/
- @SuppressWarnings("deprecation")
- RSAClientKeyExchange(ProtocolVersion currentVersion,
- ProtocolVersion maxVersion,
- SecureRandom generator, HandshakeInStream input,
- int messageSize, PrivateKey privateKey) throws IOException {
-
- if (privateKey.getAlgorithm().equals("RSA") == false) {
- throw new SSLKeyException("Private key not of type RSA: " +
- privateKey.getAlgorithm());
+ private static final
+ class RSAClientKeyExchangeProducer implements HandshakeProducer {
+ // Prevent instantiation of this class.
+ private RSAClientKeyExchangeProducer() {
+ // blank
}
- if (currentVersion.useTLS10PlusSpec()) {
- encrypted = input.getBytes16();
- } else {
- encrypted = new byte [messageSize];
- if (input.read(encrypted) != messageSize) {
- throw new SSLProtocolException(
- "SSL: read PreMasterSecret: short read");
+ @Override
+ public byte[] produce(ConnectionContext context,
+ HandshakeMessage message) throws IOException {
+ // This happens in client side only.
+ ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+ EphemeralRSACredentials rsaCredentials = null;
+ X509Credentials x509Credentials = null;
+ for (SSLCredentials credential : chc.handshakeCredentials) {
+ if (credential instanceof EphemeralRSACredentials) {
+ rsaCredentials = (EphemeralRSACredentials)credential;
+ if (x509Credentials != null) {
+ break;
}
+ } else if (credential instanceof X509Credentials) {
+ x509Credentials = (X509Credentials)credential;
+ if (rsaCredentials != null) {
+ break;
}
-
- byte[] encoded = null;
- try {
- boolean needFailover = false;
- Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
- try {
- // Try UNWRAP_MODE mode firstly.
- cipher.init(Cipher.UNWRAP_MODE, privateKey,
- new TlsRsaPremasterSecretParameterSpec(
- maxVersion.v, currentVersion.v),
- generator);
-
- // The provider selection can be delayed, please don't call
- // any Cipher method before the call to Cipher.init().
- needFailover = !KeyUtil.isOracleJCEProvider(
- cipher.getProvider().getName());
- } catch (InvalidKeyException | UnsupportedOperationException iue) {
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("The Cipher provider "
- + safeProviderName(cipher)
- + " caused exception: " + iue.getMessage());
- }
-
- needFailover = true;
- }
-
- if (needFailover) {
- // The cipher might be spoiled by unsuccessful call to init(),
- // so request a fresh instance
- cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
-
- // Use DECRYPT_MODE and dispose the previous initialization.
- cipher.init(Cipher.DECRYPT_MODE, privateKey);
- boolean failed = false;
- try {
- encoded = cipher.doFinal(encrypted);
- } catch (BadPaddingException bpe) {
- // Note: encoded == null
- failed = true;
- }
- encoded = KeyUtil.checkTlsPreMasterSecretKey(
- maxVersion.v, currentVersion.v,
- generator, encoded, failed);
- preMaster = generatePreMasterSecret(
- maxVersion.v, currentVersion.v,
- encoded, generator);
- } else {
- // the cipher should have been initialized
- preMaster = (SecretKey)cipher.unwrap(encrypted,
- "TlsRsaPremasterSecret", Cipher.SECRET_KEY);
- }
- } catch (InvalidKeyException ibk) {
- // the message is too big to process with RSA
- throw new SSLException(
- "Unable to process PreMasterSecret", ibk);
- } catch (Exception e) {
- // unlikely to happen, otherwise, must be a provider exception
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("RSA premaster secret decryption error:");
- e.printStackTrace(System.out);
- }
- throw new RuntimeException("Could not generate dummy secret", e);
}
}
- // generate a premaster secret with the specified version number
- @SuppressWarnings("deprecation")
- private static SecretKey generatePreMasterSecret(
- int clientVersion, int serverVersion,
- byte[] encodedSecret, SecureRandom generator) {
+ if (rsaCredentials == null && x509Credentials == null) {
+ chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+ "No RSA credentials negotiated for client key exchange");
+ }
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Generating a premaster secret");
+ PublicKey publicKey = (rsaCredentials != null) ?
+ rsaCredentials.popPublicKey : x509Credentials.popPublicKey;
+ if (!publicKey.getAlgorithm().equals("RSA")) { // unlikely
+ chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+ "Not RSA public key for client key exchange");
}
+ RSAPremasterSecret premaster;
+ RSAClientKeyExchangeMessage ckem;
try {
- String s = ((clientVersion >= ProtocolVersion.TLS12.v) ?
- "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret");
- KeyGenerator kg = JsseJce.getKeyGenerator(s);
- kg.init(new TlsRsaPremasterSecretParameterSpec(
- clientVersion, serverVersion, encodedSecret),
- generator);
- return kg.generateKey();
- } catch (InvalidAlgorithmParameterException |
- NoSuchAlgorithmException iae) {
- // unlikely to happen, otherwise, must be a provider exception
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("RSA premaster secret generation error:");
- iae.printStackTrace(System.out);
+ premaster = RSAPremasterSecret.createPremasterSecret(chc);
+ chc.handshakePossessions.add(premaster);
+ ckem = new RSAClientKeyExchangeMessage(
+ chc, premaster, publicKey);
+ } catch (GeneralSecurityException gse) {
+ chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+ "Cannot generate RSA premaster secret", gse);
+
+ return null; // make the compiler happy
+ }
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ SSLLogger.fine(
+ "Produced RSA ClientKeyExchange handshake message", ckem);
+ }
+
+ // Output the handshake message.
+ ckem.write(chc.handshakeOutput);
+ chc.handshakeOutput.flush();
+
+ // update the states
+ SSLKeyExchange ke =
+ SSLKeyExchange.valueOf(chc.negotiatedCipherSuite.keyExchange);
+ if (ke == null) { // unlikely
+ chc.conContext.fatal(Alert.INTERNAL_ERROR,
+ "Not supported key exchange type");
+ } else {
+ SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
+ SecretKey masterSecret = masterKD.deriveKey("TODO", null);
+
+ // update the states
+ chc.handshakeSession.setMasterSecret(masterSecret);
+ SSLTrafficKeyDerivation kd =
+ SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
+ if (kd == null) { // unlikely
+ chc.conContext.fatal(Alert.INTERNAL_ERROR,
+ "Not supported key derivation: " +
+ chc.negotiatedProtocol);
+ } else {
+ chc.handshakeKeyDerivation =
+ kd.createKeyDerivation(chc, masterSecret);
}
- throw new RuntimeException("Could not generate premaster secret", iae);
+ }
+
+ // The handshake message has been delivered.
+ return null;
}
}
- @Override
- int messageType() {
- return ht_client_key_exchange;
+ /**
+ * The RSA "ClientKeyExchange" handshake message consumer.
+ */
+ private static final
+ class RSAClientKeyExchangeConsumer implements SSLConsumer {
+ // Prevent instantiation of this class.
+ private RSAClientKeyExchangeConsumer() {
+ // blank
}
@Override
- int messageLength() {
- if (protocolVersion.useTLS10PlusSpec()) {
- return encrypted.length + 2;
- } else {
- return encrypted.length;
+ public void consume(ConnectionContext context,
+ ByteBuffer message) throws IOException {
+ // The consuming happens in server side only.
+ ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+ EphemeralRSAPossession rsaPossession = null;
+ X509Possession x509Possession = null;
+ for (SSLPossession possession : shc.handshakePossessions) {
+ if (possession instanceof EphemeralRSAPossession) {
+ rsaPossession = (EphemeralRSAPossession)possession;
+ break;
+ } else if (possession instanceof X509Possession) {
+ x509Possession = (X509Possession)possession;
+ if (rsaPossession != null) {
+ break;
+ }
}
}
- @Override
- void send(HandshakeOutStream s) throws IOException {
- if (protocolVersion.useTLS10PlusSpec()) {
- s.putBytes16(encrypted);
- } else {
- s.write(encrypted);
+ if (rsaPossession == null && x509Possession == null) { // unlikely
+ shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+ "No RSA possessions negotiated for client key exchange");
}
+
+ PrivateKey privateKey = (rsaPossession != null) ?
+ rsaPossession.popPrivateKey : x509Possession.popPrivateKey;
+ if (!privateKey.getAlgorithm().equals("RSA")) { // unlikely
+ shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+ "Not RSA private key for client key exchange");
}
- @Override
- void print(PrintStream s) throws IOException {
- String version = "version not available/extractable";
+ RSAClientKeyExchangeMessage ckem =
+ new RSAClientKeyExchangeMessage(shc, message);
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ SSLLogger.fine(
+ "Consuming RSA ClientKeyExchange handshake message", ckem);
+ }
- byte[] ba = preMaster.getEncoded();
- if (ba != null && ba.length >= 2) {
- version = ProtocolVersion.valueOf(ba[0], ba[1]).name;
+ // create the credentials
+ RSAPremasterSecret premaster;
+ try {
+ premaster =
+ RSAPremasterSecret.decode(shc, privateKey, ckem.encrypted);
+ shc.handshakeCredentials.add(premaster);
+ } catch (GeneralSecurityException gse) {
+ shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+ "Cannot decode RSA premaster secret", gse);
}
- s.println("*** ClientKeyExchange, RSA PreMasterSecret, " + version);
+ // update the states
+ SSLKeyExchange ke =
+ SSLKeyExchange.valueOf(shc.negotiatedCipherSuite.keyExchange);
+ if (ke == null) { // unlikely
+ shc.conContext.fatal(Alert.INTERNAL_ERROR,
+ "Not supported key exchange type");
+ } else {
+ SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
+ SecretKey masterSecret = masterKD.deriveKey("TODO", null);
+
+ // update the states
+ shc.handshakeSession.setMasterSecret(masterSecret);
+ SSLTrafficKeyDerivation kd =
+ SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
+ if (kd == null) { // unlikely
+ shc.conContext.fatal(Alert.INTERNAL_ERROR,
+ "Not supported key derivation: " +
+ shc.negotiatedProtocol);
+ } else {
+ shc.handshakeKeyDerivation =
+ kd.createKeyDerivation(shc, masterSecret);
+ }
+ }
+ }
}
}
< prev index next >