src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java
branchJDK-8145252-TLS13-branch
changeset 56542 56aaa6cb3693
child 56603 f103e0c2be1e
equal deleted inserted replaced
56541:92cbbfc996f3 56542:56aaa6cb3693
       
     1 /*
       
     2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package sun.security.ssl;
       
    27 
       
    28 import java.io.IOException;
       
    29 import java.security.GeneralSecurityException;
       
    30 import java.security.InvalidAlgorithmParameterException;
       
    31 import java.security.InvalidKeyException;
       
    32 import java.security.KeyPair;
       
    33 import java.security.NoSuchAlgorithmException;
       
    34 import java.security.PrivateKey;
       
    35 import java.security.PublicKey;
       
    36 import java.security.SecureRandom;
       
    37 import java.security.interfaces.RSAPublicKey;
       
    38 import java.security.spec.AlgorithmParameterSpec;
       
    39 import javax.crypto.BadPaddingException;
       
    40 import javax.crypto.Cipher;
       
    41 import javax.crypto.KeyGenerator;
       
    42 import javax.crypto.SecretKey;
       
    43 import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
       
    44 import sun.security.util.KeyUtil;
       
    45 
       
    46 final class RSAKeyExchange {
       
    47     static final SSLPossessionGenerator poGenerator =
       
    48             new EphemeralRSAPossessionGenerator();
       
    49     static final SSLKeyAgreementGenerator kaGenerator =
       
    50             new RSAKAGenerator();
       
    51 
       
    52     static final class EphemeralRSAPossession implements SSLPossession {
       
    53         // Proof of possession of the private key corresponding to the public
       
    54         // key for which a certificate is being provided for authentication.
       
    55         final RSAPublicKey        popPublicKey;
       
    56         final PrivateKey          popPrivateKey;
       
    57 
       
    58         EphemeralRSAPossession(PrivateKey popPrivateKey,
       
    59                 RSAPublicKey popPublicKey) {
       
    60             this.popPublicKey = popPublicKey;
       
    61             this.popPrivateKey = popPrivateKey;
       
    62         }
       
    63     }
       
    64 
       
    65     static final class EphemeralRSACredentials implements SSLCredentials {
       
    66         final RSAPublicKey popPublicKey;
       
    67 
       
    68         EphemeralRSACredentials(RSAPublicKey popPublicKey) {
       
    69             this.popPublicKey = popPublicKey;
       
    70         }
       
    71     }
       
    72 
       
    73     private static final class EphemeralRSAPossessionGenerator
       
    74             implements SSLPossessionGenerator {
       
    75         // Prevent instantiation of this class.
       
    76         private EphemeralRSAPossessionGenerator() {
       
    77             // blank
       
    78         }
       
    79 
       
    80         @Override
       
    81         public SSLPossession createPossession(HandshakeContext context) {
       
    82             try {
       
    83                 EphemeralKeyManager ekm =
       
    84                         context.sslContext.getEphemeralKeyManager();
       
    85                 KeyPair kp = ekm.getRSAKeyPair(
       
    86                         true, context.sslContext.getSecureRandom());
       
    87                 if (kp != null) {
       
    88                     return new EphemeralRSAPossession(
       
    89                             kp.getPrivate(), (RSAPublicKey)kp.getPublic());
       
    90                 } else {
       
    91                     // Could not generate the ephemeral key, ignore.
       
    92                     return null;
       
    93                 }
       
    94             } catch (RuntimeException rte) {
       
    95                 // Could not determine keylength, ignore.
       
    96                 return null;
       
    97             }
       
    98         }
       
    99     }
       
   100 
       
   101     static final
       
   102             class RSAPremasterSecret implements SSLPossession, SSLCredentials {
       
   103         final SecretKey premasterSecret;
       
   104 
       
   105         RSAPremasterSecret(SecretKey premasterSecret) {
       
   106             this.premasterSecret = premasterSecret;
       
   107         }
       
   108 
       
   109         byte[] getEncoded(PublicKey publicKey,
       
   110                 SecureRandom secureRandom) throws GeneralSecurityException {
       
   111             Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
       
   112             cipher.init(Cipher.WRAP_MODE, publicKey, secureRandom);
       
   113             return cipher.wrap(premasterSecret);
       
   114         }
       
   115 
       
   116         @SuppressWarnings("deprecation")
       
   117         static RSAPremasterSecret createPremasterSecret(
       
   118                 ClientHandshakeContext chc) throws GeneralSecurityException {
       
   119             String algorithm = chc.negotiatedProtocol.useTLS12PlusSpec() ?
       
   120                     "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret";
       
   121             KeyGenerator kg = JsseJce.getKeyGenerator(algorithm);
       
   122             TlsRsaPremasterSecretParameterSpec spec =
       
   123                     new TlsRsaPremasterSecretParameterSpec(
       
   124                             chc.clientHelloVersion,
       
   125                             chc.negotiatedProtocol.id);
       
   126             kg.init(spec, chc.sslContext.getSecureRandom());
       
   127 
       
   128             return new RSAPremasterSecret(kg.generateKey());
       
   129         }
       
   130 
       
   131         @SuppressWarnings("deprecation")
       
   132         static RSAPremasterSecret decode(ServerHandshakeContext shc,
       
   133                 PrivateKey privateKey,
       
   134                 byte[] encrypted) throws GeneralSecurityException {
       
   135 
       
   136             byte[] encoded = null;
       
   137             boolean needFailover = false;
       
   138             Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
       
   139             try {
       
   140                 // Try UNWRAP_MODE mode firstly.
       
   141                 cipher.init(Cipher.UNWRAP_MODE, privateKey,
       
   142                         new TlsRsaPremasterSecretParameterSpec(
       
   143                                 shc.clientHelloVersion,
       
   144                                 shc.negotiatedProtocol.id),
       
   145                                 shc.sslContext.getSecureRandom());
       
   146 
       
   147                 // The provider selection can be delayed, please don't call
       
   148                 // any Cipher method before the call to Cipher.init().
       
   149                 needFailover = !KeyUtil.isOracleJCEProvider(
       
   150                         cipher.getProvider().getName());
       
   151             } catch (InvalidKeyException | UnsupportedOperationException iue) {
       
   152                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   153                     SSLLogger.warning("The Cipher provider "
       
   154                             + safeProviderName(cipher)
       
   155                             + " caused exception: " + iue.getMessage());
       
   156                 }
       
   157 
       
   158                 needFailover = true;
       
   159             }
       
   160 
       
   161             SecretKey preMaster;
       
   162             if (needFailover) {
       
   163                 // The cipher might be spoiled by unsuccessful call to init(),
       
   164                 // so request a fresh instance
       
   165                 cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
       
   166 
       
   167                 // Use DECRYPT_MODE and dispose the previous initialization.
       
   168                 cipher.init(Cipher.DECRYPT_MODE, privateKey);
       
   169                 boolean failed = false;
       
   170                 try {
       
   171                     encoded = cipher.doFinal(encrypted);
       
   172                 } catch (BadPaddingException bpe) {
       
   173                     // Note: encoded == null
       
   174                     failed = true;
       
   175                 }
       
   176                 encoded = KeyUtil.checkTlsPreMasterSecretKey(
       
   177                         shc.clientHelloVersion, shc.negotiatedProtocol.id,
       
   178                         shc.sslContext.getSecureRandom(), encoded, failed);
       
   179                 preMaster = generatePremasterSecret(
       
   180                         shc.clientHelloVersion, shc.negotiatedProtocol.id,
       
   181                         encoded, shc.sslContext.getSecureRandom());
       
   182             } else {
       
   183                 // the cipher should have been initialized
       
   184                 preMaster = (SecretKey)cipher.unwrap(encrypted,
       
   185                         "TlsRsaPremasterSecret", Cipher.SECRET_KEY);
       
   186             }
       
   187 
       
   188             return new RSAPremasterSecret(preMaster);
       
   189         }
       
   190 
       
   191         /*
       
   192          * Retrieving the cipher's provider name for the debug purposes
       
   193          * can throw an exception by itself.
       
   194          */
       
   195         private static String safeProviderName(Cipher cipher) {
       
   196             try {
       
   197                 return cipher.getProvider().toString();
       
   198             } catch (Exception e) {
       
   199                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   200                     SSLLogger.fine("Retrieving The Cipher provider name" +
       
   201                             " caused exception ", e);
       
   202                 }
       
   203             }
       
   204             try {
       
   205                 return cipher.toString() + " (provider name not available)";
       
   206             } catch (Exception e) {
       
   207                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   208                     SSLLogger.fine("Retrieving The Cipher name" +
       
   209                             " caused exception ", e);
       
   210                 }
       
   211             }
       
   212 
       
   213             return "(cipher/provider names not available)";
       
   214         }
       
   215 
       
   216         // generate a premaster secret with the specified version number
       
   217         @SuppressWarnings("deprecation")
       
   218         private static SecretKey generatePremasterSecret(
       
   219                 int clientVersion, int serverVersion, byte[] encodedSecret,
       
   220                 SecureRandom generator) throws GeneralSecurityException {
       
   221 
       
   222             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   223                 SSLLogger.fine("Generating a premaster secret");
       
   224             }
       
   225 
       
   226             try {
       
   227                 String s = ((clientVersion >= ProtocolVersion.TLS12.id) ?
       
   228                     "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret");
       
   229                 KeyGenerator kg = JsseJce.getKeyGenerator(s);
       
   230                 kg.init(new TlsRsaPremasterSecretParameterSpec(
       
   231                         clientVersion, serverVersion, encodedSecret),
       
   232                         generator);
       
   233                 return kg.generateKey();
       
   234             } catch (InvalidAlgorithmParameterException |
       
   235                     NoSuchAlgorithmException iae) {
       
   236                 // unlikely to happen, otherwise, must be a provider exception
       
   237                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   238                     SSLLogger.fine("RSA premaster secret generation error:");
       
   239                     iae.printStackTrace(System.out);
       
   240                 }
       
   241 
       
   242                 throw new GeneralSecurityException(
       
   243                         "Could not generate premaster secret", iae);
       
   244             }
       
   245         }
       
   246     }
       
   247 
       
   248     private static final
       
   249             class RSAKAGenerator implements SSLKeyAgreementGenerator {
       
   250         // Prevent instantiation of this class.
       
   251         private RSAKAGenerator() {
       
   252             // blank
       
   253         }
       
   254 
       
   255         @Override
       
   256         public SSLKeyDerivation createKeyDerivation(
       
   257                 HandshakeContext context) throws IOException {
       
   258             RSAPremasterSecret premaster = null;
       
   259             if (context instanceof ClientHandshakeContext) {
       
   260                 for (SSLPossession possession : context.handshakePossessions) {
       
   261                     if (possession instanceof RSAPremasterSecret) {
       
   262                         premaster = (RSAPremasterSecret)possession;
       
   263                         break;
       
   264                     }
       
   265                 }
       
   266             } else {
       
   267                 for (SSLCredentials credential : context.handshakeCredentials) {
       
   268                     if (credential instanceof RSAPremasterSecret) {
       
   269                         premaster = (RSAPremasterSecret)credential;
       
   270                         break;
       
   271                     }
       
   272                 }
       
   273             }
       
   274 
       
   275             if (premaster == null) {
       
   276                 context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
       
   277                     "No sufficient RSA key agreement parameters negotiated");
       
   278             }
       
   279 
       
   280             return new RSAKAKeyDerivation(context, premaster.premasterSecret);
       
   281         }
       
   282 
       
   283         private static final
       
   284                 class RSAKAKeyDerivation implements SSLKeyDerivation {
       
   285             private final HandshakeContext context;
       
   286             private final SecretKey preMasterSecret;
       
   287 
       
   288             RSAKAKeyDerivation(
       
   289                     HandshakeContext context, SecretKey preMasterSecret) {
       
   290                 this.context = context;
       
   291                 this.preMasterSecret = preMasterSecret;
       
   292             }
       
   293 
       
   294             @Override
       
   295             public SecretKey deriveKey(String algorithm,
       
   296                     AlgorithmParameterSpec params) throws IOException {
       
   297                 SSLMasterKeyDerivation mskd =
       
   298                         SSLMasterKeyDerivation.valueOf(
       
   299                                 context.negotiatedProtocol);
       
   300                 SSLKeyDerivation kd = mskd.createKeyDerivation(
       
   301                         context, preMasterSecret);
       
   302                 return kd.deriveKey("TODO", params);
       
   303             }
       
   304         }
       
   305     }
       
   306 }