src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java
changeset 50768 68fa3d4026ea
child 53018 8bf9268df0e2
child 56858 829e9b5ace08
equal deleted inserted replaced
50767:356eaea05bf0 50768:68fa3d4026ea
       
     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.math.BigInteger;
       
    30 import java.security.GeneralSecurityException;
       
    31 import java.security.InvalidKeyException;
       
    32 import java.security.KeyFactory;
       
    33 import java.security.KeyPair;
       
    34 import java.security.KeyPairGenerator;
       
    35 import java.security.NoSuchAlgorithmException;
       
    36 import java.security.PrivateKey;
       
    37 import java.security.PublicKey;
       
    38 import java.security.SecureRandom;
       
    39 import java.security.spec.AlgorithmParameterSpec;
       
    40 import java.security.spec.InvalidKeySpecException;
       
    41 import javax.crypto.KeyAgreement;
       
    42 import javax.crypto.SecretKey;
       
    43 import javax.crypto.interfaces.DHPublicKey;
       
    44 import javax.crypto.spec.DHParameterSpec;
       
    45 import javax.crypto.spec.DHPublicKeySpec;
       
    46 import javax.crypto.spec.SecretKeySpec;
       
    47 import javax.net.ssl.SSLHandshakeException;
       
    48 import sun.security.action.GetPropertyAction;
       
    49 import sun.security.ssl.CipherSuite.HashAlg;
       
    50 import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
       
    51 import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
       
    52 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
       
    53 import sun.security.ssl.X509Authentication.X509Possession;
       
    54 import sun.security.util.KeyUtil;
       
    55 
       
    56 final class DHKeyExchange {
       
    57     static final SSLPossessionGenerator poGenerator =
       
    58             new DHEPossessionGenerator(false);
       
    59     static final SSLPossessionGenerator poExportableGenerator =
       
    60             new DHEPossessionGenerator(true);
       
    61     static final SSLKeyAgreementGenerator kaGenerator =
       
    62             new DHEKAGenerator();
       
    63 
       
    64     static final class DHECredentials implements SSLCredentials {
       
    65         final DHPublicKey popPublicKey;
       
    66         final NamedGroup namedGroup;
       
    67 
       
    68         DHECredentials(DHPublicKey popPublicKey, NamedGroup namedGroup) {
       
    69             this.popPublicKey = popPublicKey;
       
    70             this.namedGroup = namedGroup;
       
    71         }
       
    72 
       
    73         static DHECredentials valueOf(NamedGroup ng,
       
    74             byte[] encodedPublic) throws IOException, GeneralSecurityException {
       
    75 
       
    76             if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) {
       
    77                 throw new RuntimeException(
       
    78                         "Credentials decoding:  Not FFDHE named group");
       
    79             }
       
    80 
       
    81             if (encodedPublic == null || encodedPublic.length == 0) {
       
    82                 return null;
       
    83             }
       
    84 
       
    85             DHParameterSpec params = (DHParameterSpec)ng.getParameterSpec();
       
    86             if (params == null) {
       
    87                 return null;
       
    88             }
       
    89 
       
    90             KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
       
    91             DHPublicKeySpec spec = new DHPublicKeySpec(
       
    92                     new BigInteger(1, encodedPublic),
       
    93                     params.getP(), params.getG());
       
    94             DHPublicKey publicKey =
       
    95                     (DHPublicKey)kf.generatePublic(spec);
       
    96 
       
    97             return new DHECredentials(publicKey, ng);
       
    98         }
       
    99     }
       
   100 
       
   101     static final class DHEPossession implements SSLPossession {
       
   102         final PrivateKey privateKey;
       
   103         final DHPublicKey publicKey;
       
   104         final NamedGroup namedGroup;
       
   105 
       
   106         DHEPossession(NamedGroup namedGroup, SecureRandom random) {
       
   107             try {
       
   108                 KeyPairGenerator kpg =
       
   109                         JsseJce.getKeyPairGenerator("DiffieHellman");
       
   110                 DHParameterSpec params =
       
   111                         (DHParameterSpec)namedGroup.getParameterSpec();
       
   112                 kpg.initialize(params, random);
       
   113                 KeyPair kp = generateDHKeyPair(kpg);
       
   114                 if (kp == null) {
       
   115                     throw new RuntimeException("Could not generate DH keypair");
       
   116                 }
       
   117                 privateKey = kp.getPrivate();
       
   118                 publicKey = (DHPublicKey)kp.getPublic();
       
   119             } catch (GeneralSecurityException gse) {
       
   120                 throw new RuntimeException(
       
   121                         "Could not generate DH keypair", gse);
       
   122             }
       
   123 
       
   124             this.namedGroup = namedGroup;
       
   125         }
       
   126 
       
   127         DHEPossession(int keyLength, SecureRandom random) {
       
   128             DHParameterSpec params =
       
   129                     PredefinedDHParameterSpecs.definedParams.get(keyLength);
       
   130             try {
       
   131                 KeyPairGenerator kpg =
       
   132                     JsseJce.getKeyPairGenerator("DiffieHellman");
       
   133                 if (params != null) {
       
   134                     kpg.initialize(params, random);
       
   135                 } else {
       
   136                     kpg.initialize(keyLength, random);
       
   137                 }
       
   138 
       
   139                 KeyPair kp = generateDHKeyPair(kpg);
       
   140                 if (kp == null) {
       
   141                     throw new RuntimeException(
       
   142                             "Could not generate DH keypair of " +
       
   143                             keyLength + " bits");
       
   144                 }
       
   145                 privateKey = kp.getPrivate();
       
   146                 publicKey = (DHPublicKey)kp.getPublic();
       
   147             } catch (GeneralSecurityException gse) {
       
   148                 throw new RuntimeException(
       
   149                         "Could not generate DH keypair", gse);
       
   150             }
       
   151 
       
   152             this.namedGroup = NamedGroup.valueOf(publicKey.getParams());
       
   153         }
       
   154 
       
   155         DHEPossession(DHECredentials credentials, SecureRandom random) {
       
   156             try {
       
   157                 KeyPairGenerator kpg =
       
   158                         JsseJce.getKeyPairGenerator("DiffieHellman");
       
   159                 kpg.initialize(credentials.popPublicKey.getParams(), random);
       
   160                 KeyPair kp = generateDHKeyPair(kpg);
       
   161                 if (kp == null) {
       
   162                     throw new RuntimeException("Could not generate DH keypair");
       
   163                 }
       
   164                 privateKey = kp.getPrivate();
       
   165                 publicKey = (DHPublicKey)kp.getPublic();
       
   166             } catch (GeneralSecurityException gse) {
       
   167                 throw new RuntimeException(
       
   168                         "Could not generate DH keypair", gse);
       
   169             }
       
   170 
       
   171             this.namedGroup = credentials.namedGroup;
       
   172         }
       
   173 
       
   174         // Generate and validate DHPublicKeySpec
       
   175         private KeyPair generateDHKeyPair(
       
   176                 KeyPairGenerator kpg) throws GeneralSecurityException {
       
   177             boolean doExtraValiadtion =
       
   178                     (!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName()));
       
   179             boolean isRecovering = false;
       
   180             for (int i = 0; i <= 2; i++) {      // Try to recover from failure.
       
   181                 KeyPair kp = kpg.generateKeyPair();
       
   182                 // validate the Diffie-Hellman public key
       
   183                 if (doExtraValiadtion) {
       
   184                     DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
       
   185                     try {
       
   186                         KeyUtil.validate(spec);
       
   187                     } catch (InvalidKeyException ivke) {
       
   188                         if (isRecovering) {
       
   189                             throw ivke;
       
   190                         }
       
   191                         // otherwise, ignore the exception and try again
       
   192                         isRecovering = true;
       
   193                         continue;
       
   194                     }
       
   195                 }
       
   196 
       
   197                 return kp;
       
   198             }
       
   199 
       
   200             return null;
       
   201         }
       
   202 
       
   203         private static DHPublicKeySpec getDHPublicKeySpec(PublicKey key) {
       
   204             if (key instanceof DHPublicKey) {
       
   205                 DHPublicKey dhKey = (DHPublicKey)key;
       
   206                 DHParameterSpec params = dhKey.getParams();
       
   207                 return new DHPublicKeySpec(dhKey.getY(),
       
   208                                         params.getP(), params.getG());
       
   209             }
       
   210             try {
       
   211                 KeyFactory factory = JsseJce.getKeyFactory("DiffieHellman");
       
   212                 return factory.getKeySpec(key, DHPublicKeySpec.class);
       
   213             } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
       
   214                 // unlikely
       
   215                 throw new RuntimeException("Unable to get DHPublicKeySpec", e);
       
   216             }
       
   217         }
       
   218 
       
   219         @Override
       
   220         public byte[] encode() {
       
   221             // Note: the DH public value is encoded as a big-endian integer
       
   222             // and padded to the left with zeros to the size of p in bytes.
       
   223             byte[] encoded = publicKey.getY().toByteArray();
       
   224             int pSize = KeyUtil.getKeySize(publicKey);
       
   225             if (pSize > 0 && encoded.length < pSize) {
       
   226                 byte[] buffer = new byte[pSize];
       
   227                 System.arraycopy(encoded, 0,
       
   228                         buffer, pSize - encoded.length, encoded.length);
       
   229                 encoded = buffer;
       
   230             }
       
   231 
       
   232             return encoded;
       
   233         }
       
   234     }
       
   235 
       
   236     private static final class
       
   237             DHEPossessionGenerator implements SSLPossessionGenerator {
       
   238         // Flag to use smart ephemeral DH key which size matches the
       
   239         // corresponding authentication key
       
   240         private static final boolean useSmartEphemeralDHKeys;
       
   241 
       
   242         // Flag to use legacy ephemeral DH key which size is 512 bits for
       
   243         // exportable cipher suites, and 768 bits for others
       
   244         private static final boolean useLegacyEphemeralDHKeys;
       
   245 
       
   246         // The customized ephemeral DH key size for non-exportable
       
   247         // cipher suites.
       
   248         private static final int customizedDHKeySize;
       
   249 
       
   250         // Is it for exportable cipher suite?
       
   251         private final boolean exportable;
       
   252 
       
   253         static {
       
   254             String property = GetPropertyAction.privilegedGetProperty(
       
   255                     "jdk.tls.ephemeralDHKeySize");
       
   256             if (property == null || property.length() == 0) {
       
   257                 useLegacyEphemeralDHKeys = false;
       
   258                 useSmartEphemeralDHKeys = false;
       
   259                 customizedDHKeySize = -1;
       
   260             } else if ("matched".equals(property)) {
       
   261                 useLegacyEphemeralDHKeys = false;
       
   262                 useSmartEphemeralDHKeys = true;
       
   263                 customizedDHKeySize = -1;
       
   264             } else if ("legacy".equals(property)) {
       
   265                 useLegacyEphemeralDHKeys = true;
       
   266                 useSmartEphemeralDHKeys = false;
       
   267                 customizedDHKeySize = -1;
       
   268             } else {
       
   269                 useLegacyEphemeralDHKeys = false;
       
   270                 useSmartEphemeralDHKeys = false;
       
   271 
       
   272                 try {
       
   273                     // DH parameter generation can be extremely slow, best to
       
   274                     // use one of the supported pre-computed DH parameters
       
   275                     // (see DHCrypt class).
       
   276                     customizedDHKeySize = Integer.parseUnsignedInt(property);
       
   277                     if (customizedDHKeySize < 1024 ||
       
   278                             customizedDHKeySize > 8192 ||
       
   279                             (customizedDHKeySize & 0x3f) != 0) {
       
   280                         throw new IllegalArgumentException(
       
   281                             "Unsupported customized DH key size: " +
       
   282                             customizedDHKeySize + ". " +
       
   283                             "The key size must be multiple of 64, " +
       
   284                             "and range from 1024 to 8192 (inclusive)");
       
   285                     }
       
   286                 } catch (NumberFormatException nfe) {
       
   287                     throw new IllegalArgumentException(
       
   288                         "Invalid system property jdk.tls.ephemeralDHKeySize");
       
   289                 }
       
   290             }
       
   291         }
       
   292 
       
   293         // Prevent instantiation of this class.
       
   294         private DHEPossessionGenerator(boolean exportable) {
       
   295             this.exportable = exportable;
       
   296         }
       
   297 
       
   298         // Used for ServerKeyExchange, TLS 1.2 and prior versions.
       
   299         @Override
       
   300         public SSLPossession createPossession(HandshakeContext context) {
       
   301             NamedGroup preferableNamedGroup = null;
       
   302             if (!useLegacyEphemeralDHKeys &&
       
   303                     (context.clientRequestedNamedGroups != null) &&
       
   304                     (!context.clientRequestedNamedGroups.isEmpty())) {
       
   305                 preferableNamedGroup =
       
   306                         SupportedGroups.getPreferredGroup(
       
   307                                 context.negotiatedProtocol,
       
   308                                 context.algorithmConstraints,
       
   309                                 NamedGroupType.NAMED_GROUP_FFDHE,
       
   310                                 context.clientRequestedNamedGroups);
       
   311                 if (preferableNamedGroup != null) {
       
   312                     return new DHEPossession(preferableNamedGroup,
       
   313                                 context.sslContext.getSecureRandom());
       
   314                 }
       
   315             }
       
   316 
       
   317             /*
       
   318              * 768 bits ephemeral DH private keys were used to be used in
       
   319              * ServerKeyExchange except that exportable ciphers max out at 512
       
   320              * bits modulus values. We still adhere to this behavior in legacy
       
   321              * mode (system property "jdk.tls.ephemeralDHKeySize" is defined
       
   322              * as "legacy").
       
   323              *
       
   324              * Old JDK (JDK 7 and previous) releases don't support DH keys
       
   325              * bigger than 1024 bits. We have to consider the compatibility
       
   326              * requirement. 1024 bits DH key is always used for non-exportable
       
   327              * cipher suites in default mode (system property
       
   328              * "jdk.tls.ephemeralDHKeySize" is not defined).
       
   329              *
       
   330              * However, if applications want more stronger strength, setting
       
   331              * system property "jdk.tls.ephemeralDHKeySize" to "matched"
       
   332              * is a workaround to use ephemeral DH key which size matches the
       
   333              * corresponding authentication key. For example, if the public key
       
   334              * size of an authentication certificate is 2048 bits, then the
       
   335              * ephemeral DH key size should be 2048 bits accordingly unless
       
   336              * the cipher suite is exportable.  This key sizing scheme keeps
       
   337              * the cryptographic strength consistent between authentication
       
   338              * keys and key-exchange keys.
       
   339              *
       
   340              * Applications may also want to customize the ephemeral DH key
       
   341              * size to a fixed length for non-exportable cipher suites. This
       
   342              * can be approached by setting system property
       
   343              * "jdk.tls.ephemeralDHKeySize" to a valid positive integer between
       
   344              * 1024 and 8192 bits, inclusive.
       
   345              *
       
   346              * Note that the minimum acceptable key size is 1024 bits except
       
   347              * exportable cipher suites or legacy mode.
       
   348              *
       
   349              * Note that per RFC 2246, the key size limit of DH is 512 bits for
       
   350              * exportable cipher suites.  Because of the weakness, exportable
       
   351              * cipher suites are deprecated since TLS v1.1 and they are not
       
   352              * enabled by default in Oracle provider. The legacy behavior is
       
   353              * reserved and 512 bits DH key is always used for exportable
       
   354              * cipher suites.
       
   355              */
       
   356             int keySize = exportable ? 512 : 1024;           // default mode
       
   357             if (!exportable) {
       
   358                 if (useLegacyEphemeralDHKeys) {          // legacy mode
       
   359                     keySize = 768;
       
   360                 } else if (useSmartEphemeralDHKeys) {    // matched mode
       
   361                     PrivateKey key = null;
       
   362                     ServerHandshakeContext shc =
       
   363                             (ServerHandshakeContext)context;
       
   364                     if (shc.interimAuthn instanceof X509Possession) {
       
   365                         key = ((X509Possession)shc.interimAuthn).popPrivateKey;
       
   366                     }
       
   367 
       
   368                     if (key != null) {
       
   369                         int ks = KeyUtil.getKeySize(key);
       
   370 
       
   371                         // DH parameter generation can be extremely slow, make
       
   372                         // sure to use one of the supported pre-computed DH
       
   373                         // parameters.
       
   374                         //
       
   375                         // Old deployed applications may not be ready to
       
   376                         // support DH key sizes bigger than 2048 bits.  Please
       
   377                         // DON'T use value other than 1024 and 2048 at present.
       
   378                         // May improve the underlying providers and key size
       
   379                         // limit in the future when the compatibility and
       
   380                         // interoperability impact is limited.
       
   381                         keySize = ks <= 1024 ? 1024 : 2048;
       
   382                     } // Otherwise, anonymous cipher suites, 1024-bit is used.
       
   383                 } else if (customizedDHKeySize > 0) {    // customized mode
       
   384                     keySize = customizedDHKeySize;
       
   385                 }
       
   386             }
       
   387 
       
   388             return new DHEPossession(
       
   389                     keySize, context.sslContext.getSecureRandom());
       
   390         }
       
   391     }
       
   392 
       
   393     private static final
       
   394             class DHEKAGenerator implements SSLKeyAgreementGenerator {
       
   395         static private DHEKAGenerator instance = new DHEKAGenerator();
       
   396 
       
   397         // Prevent instantiation of this class.
       
   398         private DHEKAGenerator() {
       
   399             // blank
       
   400         }
       
   401 
       
   402         @Override
       
   403         public SSLKeyDerivation createKeyDerivation(
       
   404                 HandshakeContext context) throws IOException {
       
   405             DHEPossession dhePossession = null;
       
   406             DHECredentials dheCredentials = null;
       
   407             for (SSLPossession poss : context.handshakePossessions) {
       
   408                 if (!(poss instanceof DHEPossession)) {
       
   409                     continue;
       
   410                 }
       
   411 
       
   412                 DHEPossession dhep = (DHEPossession)poss;
       
   413                 for (SSLCredentials cred : context.handshakeCredentials) {
       
   414                     if (!(cred instanceof DHECredentials)) {
       
   415                         continue;
       
   416                     }
       
   417                     DHECredentials dhec = (DHECredentials)cred;
       
   418                     if (dhep.namedGroup != null && dhec.namedGroup != null) {
       
   419                         if (dhep.namedGroup.equals(dhec.namedGroup)) {
       
   420                             dheCredentials = (DHECredentials)cred;
       
   421                             break;
       
   422                         }
       
   423                     } else {
       
   424                         DHParameterSpec pps = dhep.publicKey.getParams();
       
   425                         DHParameterSpec cps = dhec.popPublicKey.getParams();
       
   426                         if (pps.getP().equals(cps.getP()) &&
       
   427                                 pps.getG().equals(cps.getG())) {
       
   428                             dheCredentials = (DHECredentials)cred;
       
   429                             break;
       
   430                         }
       
   431                     }
       
   432                 }
       
   433 
       
   434                 if (dheCredentials != null) {
       
   435                     dhePossession = (DHEPossession)poss;
       
   436                     break;
       
   437                 }
       
   438             }
       
   439 
       
   440             if (dhePossession == null || dheCredentials == null) {
       
   441                 context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
       
   442                     "No sufficient DHE key agreement parameters negotiated");
       
   443             }
       
   444 
       
   445             return new DHEKAKeyDerivation(context,
       
   446                     dhePossession.privateKey, dheCredentials.popPublicKey);
       
   447         }
       
   448 
       
   449         private static final
       
   450                 class DHEKAKeyDerivation implements SSLKeyDerivation {
       
   451             private final HandshakeContext context;
       
   452             private final PrivateKey localPrivateKey;
       
   453             private final PublicKey peerPublicKey;
       
   454 
       
   455             DHEKAKeyDerivation(HandshakeContext context,
       
   456                     PrivateKey localPrivateKey,
       
   457                     PublicKey peerPublicKey) {
       
   458                 this.context = context;
       
   459                 this.localPrivateKey = localPrivateKey;
       
   460                 this.peerPublicKey = peerPublicKey;
       
   461             }
       
   462 
       
   463             @Override
       
   464             public SecretKey deriveKey(String algorithm,
       
   465                     AlgorithmParameterSpec params) throws IOException {
       
   466                 if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
       
   467                     return t12DeriveKey(algorithm, params);
       
   468                 } else {
       
   469                     return t13DeriveKey(algorithm, params);
       
   470                 }
       
   471             }
       
   472 
       
   473             private SecretKey t12DeriveKey(String algorithm,
       
   474                     AlgorithmParameterSpec params) throws IOException {
       
   475                 try {
       
   476                     KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");
       
   477                     ka.init(localPrivateKey);
       
   478                     ka.doPhase(peerPublicKey, true);
       
   479                     SecretKey preMasterSecret =
       
   480                             ka.generateSecret("TlsPremasterSecret");
       
   481                     SSLMasterKeyDerivation mskd =
       
   482                             SSLMasterKeyDerivation.valueOf(
       
   483                                     context.negotiatedProtocol);
       
   484                     if (mskd == null) {
       
   485                         // unlikely
       
   486                         throw new SSLHandshakeException(
       
   487                             "No expected master key derivation for protocol: " +
       
   488                             context.negotiatedProtocol.name);
       
   489                     }
       
   490                     SSLKeyDerivation kd = mskd.createKeyDerivation(
       
   491                             context, preMasterSecret);
       
   492                     return kd.deriveKey("MasterSecret", params);
       
   493                 } catch (GeneralSecurityException gse) {
       
   494                     throw (SSLHandshakeException) new SSLHandshakeException(
       
   495                         "Could not generate secret").initCause(gse);
       
   496                 }
       
   497             }
       
   498 
       
   499             private SecretKey t13DeriveKey(String algorithm,
       
   500                     AlgorithmParameterSpec params) throws IOException {
       
   501                 try {
       
   502                     KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");
       
   503                     ka.init(localPrivateKey);
       
   504                     ka.doPhase(peerPublicKey, true);
       
   505                     SecretKey sharedSecret =
       
   506                             ka.generateSecret("TlsPremasterSecret");
       
   507 
       
   508                     HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg;
       
   509                     SSLKeyDerivation kd = context.handshakeKeyDerivation;
       
   510                     HKDF hkdf = new HKDF(hashAlg.name);
       
   511                     if (kd == null) {   // No PSK is in use.
       
   512                         // If PSK is not in use Early Secret will still be
       
   513                         // HKDF-Extract(0, 0).
       
   514                         byte[] zeros = new byte[hashAlg.hashLength];
       
   515                         SecretKeySpec ikm =
       
   516                                 new SecretKeySpec(zeros, "TlsPreSharedSecret");
       
   517                         SecretKey earlySecret =
       
   518                                 hkdf.extract(zeros, ikm, "TlsEarlySecret");
       
   519                         kd = new SSLSecretDerivation(context, earlySecret);
       
   520                     }
       
   521 
       
   522                     // derive salt secret
       
   523                     SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
       
   524 
       
   525                     // derive handshake secret
       
   526                     return hkdf.extract(saltSecret, sharedSecret, algorithm);
       
   527                 } catch (GeneralSecurityException gse) {
       
   528                     throw (SSLHandshakeException) new SSLHandshakeException(
       
   529                         "Could not generate secret").initCause(gse);
       
   530                 }
       
   531             }
       
   532         }
       
   533     }
       
   534 }