Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/ssl/DHKeyExchange.java
38830 views
/*1* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.ssl;2627import java.io.IOException;28import java.math.BigInteger;29import java.security.GeneralSecurityException;30import java.security.InvalidKeyException;31import java.security.KeyFactory;32import java.security.KeyPair;33import java.security.KeyPairGenerator;34import java.security.NoSuchAlgorithmException;35import java.security.PrivateKey;36import java.security.PublicKey;37import java.security.SecureRandom;38import java.security.spec.AlgorithmParameterSpec;39import java.security.spec.InvalidKeySpecException;40import javax.crypto.KeyAgreement;41import javax.crypto.SecretKey;42import javax.crypto.interfaces.DHPublicKey;43import javax.crypto.spec.DHParameterSpec;44import javax.crypto.spec.DHPublicKeySpec;45import javax.crypto.spec.SecretKeySpec;46import javax.net.ssl.SSLHandshakeException;47import sun.security.action.GetPropertyAction;48import sun.security.ssl.CipherSuite.HashAlg;49import sun.security.ssl.SupportedGroupsExtension.NamedGroup;50import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;51import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;52import sun.security.ssl.X509Authentication.X509Possession;53import sun.security.util.KeyUtil;5455final class DHKeyExchange {56static final SSLPossessionGenerator poGenerator =57new DHEPossessionGenerator(false);58static final SSLPossessionGenerator poExportableGenerator =59new DHEPossessionGenerator(true);60static final SSLKeyAgreementGenerator kaGenerator =61new DHEKAGenerator();6263static final class DHECredentials implements SSLCredentials {64final DHPublicKey popPublicKey;65final NamedGroup namedGroup;6667DHECredentials(DHPublicKey popPublicKey, NamedGroup namedGroup) {68this.popPublicKey = popPublicKey;69this.namedGroup = namedGroup;70}7172static DHECredentials valueOf(NamedGroup ng,73byte[] encodedPublic) throws IOException, GeneralSecurityException {7475if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) {76throw new RuntimeException(77"Credentials decoding: Not FFDHE named group");78}7980if (encodedPublic == null || encodedPublic.length == 0) {81return null;82}8384DHParameterSpec params = (DHParameterSpec)ng.getParameterSpec();85if (params == null) {86return null;87}8889KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");90DHPublicKeySpec spec = new DHPublicKeySpec(91new BigInteger(1, encodedPublic),92params.getP(), params.getG());93DHPublicKey publicKey =94(DHPublicKey)kf.generatePublic(spec);9596return new DHECredentials(publicKey, ng);97}98}99100static final class DHEPossession implements SSLPossession {101final PrivateKey privateKey;102final DHPublicKey publicKey;103final NamedGroup namedGroup;104105DHEPossession(NamedGroup namedGroup, SecureRandom random) {106try {107KeyPairGenerator kpg =108JsseJce.getKeyPairGenerator("DiffieHellman");109DHParameterSpec params =110(DHParameterSpec)namedGroup.getParameterSpec();111kpg.initialize(params, random);112KeyPair kp = generateDHKeyPair(kpg);113if (kp == null) {114throw new RuntimeException("Could not generate DH keypair");115}116privateKey = kp.getPrivate();117publicKey = (DHPublicKey)kp.getPublic();118} catch (GeneralSecurityException gse) {119throw new RuntimeException(120"Could not generate DH keypair", gse);121}122123this.namedGroup = namedGroup;124}125126DHEPossession(int keyLength, SecureRandom random) {127DHParameterSpec params =128PredefinedDHParameterSpecs.definedParams.get(keyLength);129try {130KeyPairGenerator kpg =131JsseJce.getKeyPairGenerator("DiffieHellman");132if (params != null) {133kpg.initialize(params, random);134} else {135kpg.initialize(keyLength, random);136}137138KeyPair kp = generateDHKeyPair(kpg);139if (kp == null) {140throw new RuntimeException(141"Could not generate DH keypair of " +142keyLength + " bits");143}144privateKey = kp.getPrivate();145publicKey = (DHPublicKey)kp.getPublic();146} catch (GeneralSecurityException gse) {147throw new RuntimeException(148"Could not generate DH keypair", gse);149}150151this.namedGroup = NamedGroup.valueOf(publicKey.getParams());152}153154DHEPossession(DHECredentials credentials, SecureRandom random) {155try {156KeyPairGenerator kpg =157JsseJce.getKeyPairGenerator("DiffieHellman");158kpg.initialize(credentials.popPublicKey.getParams(), random);159KeyPair kp = generateDHKeyPair(kpg);160if (kp == null) {161throw new RuntimeException("Could not generate DH keypair");162}163privateKey = kp.getPrivate();164publicKey = (DHPublicKey)kp.getPublic();165} catch (GeneralSecurityException gse) {166throw new RuntimeException(167"Could not generate DH keypair", gse);168}169170this.namedGroup = credentials.namedGroup;171}172173// Generate and validate DHPublicKeySpec174private KeyPair generateDHKeyPair(175KeyPairGenerator kpg) throws GeneralSecurityException {176boolean doExtraValiadtion =177(!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName()));178boolean isRecovering = false;179for (int i = 0; i <= 2; i++) { // Try to recover from failure.180KeyPair kp = kpg.generateKeyPair();181// validate the Diffie-Hellman public key182if (doExtraValiadtion) {183DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());184try {185KeyUtil.validate(spec);186} catch (InvalidKeyException ivke) {187if (isRecovering) {188throw ivke;189}190// otherwise, ignore the exception and try again191isRecovering = true;192continue;193}194}195196return kp;197}198199return null;200}201202private static DHPublicKeySpec getDHPublicKeySpec(PublicKey key) {203if (key instanceof DHPublicKey) {204DHPublicKey dhKey = (DHPublicKey)key;205DHParameterSpec params = dhKey.getParams();206return new DHPublicKeySpec(dhKey.getY(),207params.getP(), params.getG());208}209try {210KeyFactory factory = JsseJce.getKeyFactory("DiffieHellman");211return factory.getKeySpec(key, DHPublicKeySpec.class);212} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {213// unlikely214throw new RuntimeException("Unable to get DHPublicKeySpec", e);215}216}217218@Override219public byte[] encode() {220// Note: the DH public value is encoded as a big-endian integer221// and padded to the left with zeros to the size of p in bytes.222byte[] encoded = Utilities.toByteArray(publicKey.getY());223int pSize = (KeyUtil.getKeySize(publicKey) + 7) >>> 3;224if (pSize > 0 && encoded.length < pSize) {225byte[] buffer = new byte[pSize];226System.arraycopy(encoded, 0,227buffer, pSize - encoded.length, encoded.length);228encoded = buffer;229}230231return encoded;232}233}234235private static final class236DHEPossessionGenerator implements SSLPossessionGenerator {237// Flag to use smart ephemeral DH key which size matches the238// corresponding authentication key239private static final boolean useSmartEphemeralDHKeys;240241// Flag to use legacy ephemeral DH key which size is 512 bits for242// exportable cipher suites, and 768 bits for others243private static final boolean useLegacyEphemeralDHKeys;244245// The customized ephemeral DH key size for non-exportable246// cipher suites.247private static final int customizedDHKeySize;248249// Is it for exportable cipher suite?250private final boolean exportable;251252static {253String property = GetPropertyAction.privilegedGetProperty(254"jdk.tls.ephemeralDHKeySize");255if (property == null || property.isEmpty()) {256useLegacyEphemeralDHKeys = false;257useSmartEphemeralDHKeys = false;258customizedDHKeySize = -1;259} else if ("matched".equals(property)) {260useLegacyEphemeralDHKeys = false;261useSmartEphemeralDHKeys = true;262customizedDHKeySize = -1;263} else if ("legacy".equals(property)) {264useLegacyEphemeralDHKeys = true;265useSmartEphemeralDHKeys = false;266customizedDHKeySize = -1;267} else {268useLegacyEphemeralDHKeys = false;269useSmartEphemeralDHKeys = false;270271try {272// DH parameter generation can be extremely slow, best to273// use one of the supported pre-computed DH parameters274// (see DHCrypt class).275customizedDHKeySize = Integer.parseUnsignedInt(property);276if (customizedDHKeySize < 1024 ||277customizedDHKeySize > 8192 ||278(customizedDHKeySize & 0x3f) != 0) {279throw new IllegalArgumentException(280"Unsupported customized DH key size: " +281customizedDHKeySize + ". " +282"The key size must be multiple of 64, " +283"and range from 1024 to 8192 (inclusive)");284}285} catch (NumberFormatException nfe) {286throw new IllegalArgumentException(287"Invalid system property jdk.tls.ephemeralDHKeySize");288}289}290}291292// Prevent instantiation of this class.293private DHEPossessionGenerator(boolean exportable) {294this.exportable = exportable;295}296297// Used for ServerKeyExchange, TLS 1.2 and prior versions.298@Override299public SSLPossession createPossession(HandshakeContext context) {300NamedGroup preferableNamedGroup = null;301if (!useLegacyEphemeralDHKeys &&302(context.clientRequestedNamedGroups != null) &&303(!context.clientRequestedNamedGroups.isEmpty())) {304preferableNamedGroup =305SupportedGroups.getPreferredGroup(306context.negotiatedProtocol,307context.algorithmConstraints,308NamedGroupType.NAMED_GROUP_FFDHE,309context.clientRequestedNamedGroups);310if (preferableNamedGroup != null) {311return new DHEPossession(preferableNamedGroup,312context.sslContext.getSecureRandom());313}314}315316/*317* 768 bits ephemeral DH private keys were used to be used in318* ServerKeyExchange except that exportable ciphers max out at 512319* bits modulus values. We still adhere to this behavior in legacy320* mode (system property "jdk.tls.ephemeralDHKeySize" is defined321* as "legacy").322*323* Old JDK (JDK 7 and previous) releases don't support DH keys324* bigger than 1024 bits. We have to consider the compatibility325* requirement. 1024 bits DH key is always used for non-exportable326* cipher suites in default mode (system property327* "jdk.tls.ephemeralDHKeySize" is not defined).328*329* However, if applications want more stronger strength, setting330* system property "jdk.tls.ephemeralDHKeySize" to "matched"331* is a workaround to use ephemeral DH key which size matches the332* corresponding authentication key. For example, if the public key333* size of an authentication certificate is 2048 bits, then the334* ephemeral DH key size should be 2048 bits accordingly unless335* the cipher suite is exportable. This key sizing scheme keeps336* the cryptographic strength consistent between authentication337* keys and key-exchange keys.338*339* Applications may also want to customize the ephemeral DH key340* size to a fixed length for non-exportable cipher suites. This341* can be approached by setting system property342* "jdk.tls.ephemeralDHKeySize" to a valid positive integer between343* 1024 and 8192 bits, inclusive.344*345* Note that the minimum acceptable key size is 1024 bits except346* exportable cipher suites or legacy mode.347*348* Note that per RFC 2246, the key size limit of DH is 512 bits for349* exportable cipher suites. Because of the weakness, exportable350* cipher suites are deprecated since TLS v1.1 and they are not351* enabled by default in Oracle provider. The legacy behavior is352* reserved and 512 bits DH key is always used for exportable353* cipher suites.354*/355int keySize = exportable ? 512 : 1024; // default mode356if (!exportable) {357if (useLegacyEphemeralDHKeys) { // legacy mode358keySize = 768;359} else if (useSmartEphemeralDHKeys) { // matched mode360PrivateKey key = null;361ServerHandshakeContext shc =362(ServerHandshakeContext)context;363if (shc.interimAuthn instanceof X509Possession) {364key = ((X509Possession)shc.interimAuthn).popPrivateKey;365}366367if (key != null) {368int ks = KeyUtil.getKeySize(key);369370// DH parameter generation can be extremely slow, make371// sure to use one of the supported pre-computed DH372// parameters.373//374// Old deployed applications may not be ready to375// support DH key sizes bigger than 2048 bits. Please376// DON'T use value other than 1024 and 2048 at present.377// May improve the underlying providers and key size378// limit in the future when the compatibility and379// interoperability impact is limited.380keySize = ks <= 1024 ? 1024 : 2048;381} // Otherwise, anonymous cipher suites, 1024-bit is used.382} else if (customizedDHKeySize > 0) { // customized mode383keySize = customizedDHKeySize;384}385}386387return new DHEPossession(388keySize, context.sslContext.getSecureRandom());389}390}391392private static final393class DHEKAGenerator implements SSLKeyAgreementGenerator {394static private DHEKAGenerator instance = new DHEKAGenerator();395396// Prevent instantiation of this class.397private DHEKAGenerator() {398// blank399}400401@Override402public SSLKeyDerivation createKeyDerivation(403HandshakeContext context) throws IOException {404DHEPossession dhePossession = null;405DHECredentials dheCredentials = null;406for (SSLPossession poss : context.handshakePossessions) {407if (!(poss instanceof DHEPossession)) {408continue;409}410411DHEPossession dhep = (DHEPossession)poss;412for (SSLCredentials cred : context.handshakeCredentials) {413if (!(cred instanceof DHECredentials)) {414continue;415}416DHECredentials dhec = (DHECredentials)cred;417if (dhep.namedGroup != null && dhec.namedGroup != null) {418if (dhep.namedGroup.equals(dhec.namedGroup)) {419dheCredentials = (DHECredentials)cred;420break;421}422} else {423DHParameterSpec pps = dhep.publicKey.getParams();424DHParameterSpec cps = dhec.popPublicKey.getParams();425if (pps.getP().equals(cps.getP()) &&426pps.getG().equals(cps.getG())) {427dheCredentials = (DHECredentials)cred;428break;429}430}431}432433if (dheCredentials != null) {434dhePossession = (DHEPossession)poss;435break;436}437}438439if (dhePossession == null || dheCredentials == null) {440throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,441"No sufficient DHE key agreement parameters negotiated");442}443444return new DHEKAKeyDerivation(context,445dhePossession.privateKey, dheCredentials.popPublicKey);446}447448private static final449class DHEKAKeyDerivation implements SSLKeyDerivation {450private final HandshakeContext context;451private final PrivateKey localPrivateKey;452private final PublicKey peerPublicKey;453454DHEKAKeyDerivation(HandshakeContext context,455PrivateKey localPrivateKey,456PublicKey peerPublicKey) {457this.context = context;458this.localPrivateKey = localPrivateKey;459this.peerPublicKey = peerPublicKey;460}461462@Override463public SecretKey deriveKey(String algorithm,464AlgorithmParameterSpec params) throws IOException {465if (!context.negotiatedProtocol.useTLS13PlusSpec()) {466return t12DeriveKey(algorithm, params);467} else {468return t13DeriveKey(algorithm, params);469}470}471472private SecretKey t12DeriveKey(String algorithm,473AlgorithmParameterSpec params) throws IOException {474try {475KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");476ka.init(localPrivateKey);477ka.doPhase(peerPublicKey, true);478SecretKey preMasterSecret =479ka.generateSecret("TlsPremasterSecret");480SSLMasterKeyDerivation mskd =481SSLMasterKeyDerivation.valueOf(482context.negotiatedProtocol);483if (mskd == null) {484// unlikely485throw new SSLHandshakeException(486"No expected master key derivation for protocol: " +487context.negotiatedProtocol.name);488}489SSLKeyDerivation kd = mskd.createKeyDerivation(490context, preMasterSecret);491return kd.deriveKey("MasterSecret", params);492} catch (GeneralSecurityException gse) {493throw (SSLHandshakeException) new SSLHandshakeException(494"Could not generate secret").initCause(gse);495}496}497498private SecretKey t13DeriveKey(String algorithm,499AlgorithmParameterSpec params) throws IOException {500try {501KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");502ka.init(localPrivateKey);503ka.doPhase(peerPublicKey, true);504SecretKey sharedSecret =505ka.generateSecret("TlsPremasterSecret");506507HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg;508SSLKeyDerivation kd = context.handshakeKeyDerivation;509HKDF hkdf = new HKDF(hashAlg.name);510if (kd == null) { // No PSK is in use.511// If PSK is not in use Early Secret will still be512// HKDF-Extract(0, 0).513byte[] zeros = new byte[hashAlg.hashLength];514SecretKeySpec ikm =515new SecretKeySpec(zeros, "TlsPreSharedSecret");516SecretKey earlySecret =517hkdf.extract(zeros, ikm, "TlsEarlySecret");518kd = new SSLSecretDerivation(context, earlySecret);519}520521// derive salt secret522SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);523524// derive handshake secret525return hkdf.extract(saltSecret, sharedSecret, algorithm);526} catch (GeneralSecurityException gse) {527throw (SSLHandshakeException) new SSLHandshakeException(528"Could not generate secret").initCause(gse);529}530}531}532}533}534535536