< prev index next >

src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java

Print this page

        

*** 1,7 **** /* ! * Copyright (c) 1996, 2016, 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 --- 1,7 ---- /* ! * 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,269 **** * 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; /** ! * 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. ! * */ ! final class RSAClientKeyExchange extends HandshakeMessage { ! /* ! * 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. */ ! 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()); } - 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); } } ! /* ! * 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()); } } ! 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()); } } - return "(cipher/provider names not available)"; } ! /* ! * Server gets the PKCS #1 (block format 02) data, decrypts ! * it with its private key. */ ! @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()); } ! if (currentVersion.useTLS10PlusSpec()) { ! encrypted = input.getBytes16(); ! } else { ! encrypted = new byte [messageSize]; ! if (input.read(encrypted) != messageSize) { ! throw new SSLProtocolException( ! "SSL: read PreMasterSecret: short read"); } } - - 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 (debug != null && Debug.isOn("handshake")) { ! System.out.println("Generating a premaster secret"); } 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); } ! throw new RuntimeException("Could not generate premaster secret", iae); } } ! @Override ! int messageType() { ! return ht_client_key_exchange; } @Override ! int messageLength() { ! if (protocolVersion.useTLS10PlusSpec()) { ! return encrypted.length + 2; ! } else { ! return encrypted.length; } } ! @Override ! void send(HandshakeOutStream s) throws IOException { ! if (protocolVersion.useTLS10PlusSpec()) { ! s.putBytes16(encrypted); ! } else { ! s.write(encrypted); } } ! @Override ! void print(PrintStream s) throws IOException { ! String version = "version not available/extractable"; ! byte[] ba = preMaster.getEncoded(); ! if (ba != null && ba.length >= 2) { ! version = ProtocolVersion.valueOf(ba[0], ba[1]).name; } ! s.println("*** ClientKeyExchange, RSA PreMasterSecret, " + version); } } --- 21,320 ---- * 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.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; /** ! * Pack of the "ClientKeyExchange" handshake message. */ ! final class RSAClientKeyExchange { ! static final SSLConsumer rsaHandshakeConsumer = ! new RSAClientKeyExchangeConsumer(); ! static final HandshakeProducer rsaHandshakeProducer = ! new RSAClientKeyExchangeProducer(); ! /** ! * The RSA ClientKeyExchange handshake message. */ ! 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); } } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.CLIENT_KEY_EXCHANGE; } ! @Override ! public int messageLength() { ! if (useTLS10PlusSpec) { ! return encrypted.length + 2; ! } else { ! return encrypted.length; } } ! ! @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); } } ! /** ! * The RSA "ClientKeyExchange" handshake message producer. */ ! private static final ! class RSAClientKeyExchangeProducer implements HandshakeProducer { ! // Prevent instantiation of this class. ! private RSAClientKeyExchangeProducer() { ! // blank } ! @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; } } } ! if (rsaCredentials == null && x509Credentials == null) { ! chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, ! "No RSA credentials negotiated for client key exchange"); ! } ! 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 { ! 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); } ! } ! ! // The handshake message has been delivered. ! return null; } } ! /** ! * The RSA "ClientKeyExchange" handshake message consumer. ! */ ! private static final ! class RSAClientKeyExchangeConsumer implements SSLConsumer { ! // Prevent instantiation of this class. ! private RSAClientKeyExchangeConsumer() { ! // blank } @Override ! 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; ! } } } ! 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"); } ! RSAClientKeyExchangeMessage ckem = ! new RSAClientKeyExchangeMessage(shc, message); ! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { ! SSLLogger.fine( ! "Consuming RSA ClientKeyExchange handshake message", ckem); ! } ! // 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); } ! // 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 >