Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/ssl/ECDHKeyExchange.java
38830 views
/*1* Copyright (c) 2018, 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.security.AlgorithmConstraints;29import java.security.CryptoPrimitive;30import java.security.GeneralSecurityException;31import java.security.KeyFactory;32import java.security.KeyPair;33import java.security.KeyPairGenerator;34import java.security.PrivateKey;35import java.security.PublicKey;36import java.security.SecureRandom;37import java.security.interfaces.ECPublicKey;38import java.security.spec.AlgorithmParameterSpec;39import java.security.spec.ECGenParameterSpec;40import java.security.spec.ECParameterSpec;41import java.security.spec.ECPoint;42import java.security.spec.ECPublicKeySpec;43import java.util.EnumSet;44import javax.crypto.KeyAgreement;45import javax.crypto.SecretKey;46import javax.crypto.spec.SecretKeySpec;47import javax.net.ssl.SSLHandshakeException;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.X509Credentials;53import sun.security.ssl.X509Authentication.X509Possession;54import sun.security.util.ECUtil;5556final class ECDHKeyExchange {57static final SSLPossessionGenerator poGenerator =58new ECDHEPossessionGenerator();59static final SSLKeyAgreementGenerator ecdheKAGenerator =60new ECDHEKAGenerator();61static final SSLKeyAgreementGenerator ecdhKAGenerator =62new ECDHKAGenerator();6364static final class ECDHECredentials implements SSLCredentials {65final ECPublicKey popPublicKey;66final NamedGroup namedGroup;6768ECDHECredentials(ECPublicKey popPublicKey, NamedGroup namedGroup) {69this.popPublicKey = popPublicKey;70this.namedGroup = namedGroup;71}7273static ECDHECredentials valueOf(NamedGroup namedGroup,74byte[] encodedPoint) throws IOException, GeneralSecurityException {7576if (namedGroup.type != NamedGroupType.NAMED_GROUP_ECDHE) {77throw new RuntimeException(78"Credentials decoding: Not ECDHE named group");79}8081if (encodedPoint == null || encodedPoint.length == 0) {82return null;83}8485ECParameterSpec parameters =86JsseJce.getECParameterSpec(namedGroup.oid);87if (parameters == null) {88return null;89}9091ECPoint point = JsseJce.decodePoint(92encodedPoint, parameters.getCurve());93KeyFactory factory = JsseJce.getKeyFactory("EC");94ECPublicKey publicKey = (ECPublicKey)factory.generatePublic(95new ECPublicKeySpec(point, parameters));96return new ECDHECredentials(publicKey, namedGroup);97}98}99100static final class ECDHEPossession implements SSLPossession {101final PrivateKey privateKey;102final ECPublicKey publicKey;103final NamedGroup namedGroup;104105ECDHEPossession(NamedGroup namedGroup, SecureRandom random) {106try {107KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("EC");108ECGenParameterSpec params =109(ECGenParameterSpec)namedGroup.getParameterSpec();110kpg.initialize(params, random);111KeyPair kp = kpg.generateKeyPair();112privateKey = kp.getPrivate();113publicKey = (ECPublicKey)kp.getPublic();114} catch (GeneralSecurityException e) {115throw new RuntimeException(116"Could not generate ECDH keypair", e);117}118119this.namedGroup = namedGroup;120}121122ECDHEPossession(ECDHECredentials credentials, SecureRandom random) {123ECParameterSpec params = credentials.popPublicKey.getParams();124try {125KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("EC");126kpg.initialize(params, random);127KeyPair kp = kpg.generateKeyPair();128privateKey = kp.getPrivate();129publicKey = (ECPublicKey)kp.getPublic();130} catch (GeneralSecurityException e) {131throw new RuntimeException(132"Could not generate ECDH keypair", e);133}134135this.namedGroup = credentials.namedGroup;136}137138@Override139public byte[] encode() {140return ECUtil.encodePoint(141publicKey.getW(), publicKey.getParams().getCurve());142}143144// called by ClientHandshaker with either the server's static or145// ephemeral public key146SecretKey getAgreedSecret(147PublicKey peerPublicKey) throws SSLHandshakeException {148149try {150KeyAgreement ka = JsseJce.getKeyAgreement("ECDH");151ka.init(privateKey);152ka.doPhase(peerPublicKey, true);153return ka.generateSecret("TlsPremasterSecret");154} catch (GeneralSecurityException e) {155throw (SSLHandshakeException) new SSLHandshakeException(156"Could not generate secret").initCause(e);157}158}159160// called by ServerHandshaker161SecretKey getAgreedSecret(162byte[] encodedPoint) throws SSLHandshakeException {163try {164ECParameterSpec params = publicKey.getParams();165ECPoint point =166JsseJce.decodePoint(encodedPoint, params.getCurve());167KeyFactory kf = JsseJce.getKeyFactory("EC");168ECPublicKeySpec spec = new ECPublicKeySpec(point, params);169PublicKey peerPublicKey = kf.generatePublic(spec);170return getAgreedSecret(peerPublicKey);171} catch (GeneralSecurityException | java.io.IOException e) {172throw (SSLHandshakeException) new SSLHandshakeException(173"Could not generate secret").initCause(e);174}175}176177// Check constraints of the specified EC public key.178void checkConstraints(AlgorithmConstraints constraints,179byte[] encodedPoint) throws SSLHandshakeException {180try {181182ECParameterSpec params = publicKey.getParams();183ECPoint point =184JsseJce.decodePoint(encodedPoint, params.getCurve());185ECPublicKeySpec spec = new ECPublicKeySpec(point, params);186187KeyFactory kf = JsseJce.getKeyFactory("EC");188ECPublicKey pubKey = (ECPublicKey)kf.generatePublic(spec);189190// check constraints of ECPublicKey191if (!constraints.permits(192EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), pubKey)) {193throw new SSLHandshakeException(194"ECPublicKey does not comply to algorithm constraints");195}196} catch (GeneralSecurityException | java.io.IOException e) {197throw (SSLHandshakeException) new SSLHandshakeException(198"Could not generate ECPublicKey").initCause(e);199}200}201}202203private static final204class ECDHEPossessionGenerator implements SSLPossessionGenerator {205// Prevent instantiation of this class.206private ECDHEPossessionGenerator() {207// blank208}209210@Override211public SSLPossession createPossession(HandshakeContext context) {212NamedGroup preferableNamedGroup = null;213if ((context.clientRequestedNamedGroups != null) &&214(!context.clientRequestedNamedGroups.isEmpty())) {215preferableNamedGroup = SupportedGroups.getPreferredGroup(216context.negotiatedProtocol,217context.algorithmConstraints,218NamedGroupType.NAMED_GROUP_ECDHE,219context.clientRequestedNamedGroups);220} else {221preferableNamedGroup = SupportedGroups.getPreferredGroup(222context.negotiatedProtocol,223context.algorithmConstraints,224NamedGroupType.NAMED_GROUP_ECDHE);225}226227if (preferableNamedGroup != null) {228return new ECDHEPossession(preferableNamedGroup,229context.sslContext.getSecureRandom());230}231232// no match found, cannot use this cipher suite.233//234return null;235}236}237238private static final239class ECDHKAGenerator implements SSLKeyAgreementGenerator {240// Prevent instantiation of this class.241private ECDHKAGenerator() {242// blank243}244245@Override246public SSLKeyDerivation createKeyDerivation(247HandshakeContext context) throws IOException {248if (context instanceof ServerHandshakeContext) {249return createServerKeyDerivation(250(ServerHandshakeContext)context);251} else {252return createClientKeyDerivation(253(ClientHandshakeContext)context);254}255}256257private SSLKeyDerivation createServerKeyDerivation(258ServerHandshakeContext shc) throws IOException {259X509Possession x509Possession = null;260ECDHECredentials ecdheCredentials = null;261for (SSLPossession poss : shc.handshakePossessions) {262if (!(poss instanceof X509Possession)) {263continue;264}265266ECParameterSpec params =267((X509Possession)poss).getECParameterSpec();268if (params == null) {269continue;270}271272NamedGroup ng = NamedGroup.valueOf(params);273if (ng == null) {274// unlikely, have been checked during cipher suite negotiation.275throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,276"Unsupported EC server cert for ECDH key exchange");277}278279for (SSLCredentials cred : shc.handshakeCredentials) {280if (!(cred instanceof ECDHECredentials)) {281continue;282}283if (ng.equals(((ECDHECredentials)cred).namedGroup)) {284ecdheCredentials = (ECDHECredentials)cred;285break;286}287}288289if (ecdheCredentials != null) {290x509Possession = (X509Possession)poss;291break;292}293}294295if (x509Possession == null || ecdheCredentials == null) {296throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,297"No sufficient ECDHE key agreement parameters negotiated");298}299300return new ECDHEKAKeyDerivation(shc,301x509Possession.popPrivateKey, ecdheCredentials.popPublicKey);302}303304private SSLKeyDerivation createClientKeyDerivation(305ClientHandshakeContext chc) throws IOException {306ECDHEPossession ecdhePossession = null;307X509Credentials x509Credentials = null;308for (SSLPossession poss : chc.handshakePossessions) {309if (!(poss instanceof ECDHEPossession)) {310continue;311}312313NamedGroup ng = ((ECDHEPossession)poss).namedGroup;314for (SSLCredentials cred : chc.handshakeCredentials) {315if (!(cred instanceof X509Credentials)) {316continue;317}318319PublicKey publicKey = ((X509Credentials)cred).popPublicKey;320if (!publicKey.getAlgorithm().equals("EC")) {321continue;322}323ECParameterSpec params =324((ECPublicKey)publicKey).getParams();325NamedGroup namedGroup = NamedGroup.valueOf(params);326if (namedGroup == null) {327// unlikely, should have been checked previously328throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,329"Unsupported EC server cert for ECDH key exchange");330}331332if (ng.equals(namedGroup)) {333x509Credentials = (X509Credentials)cred;334break;335}336}337338if (x509Credentials != null) {339ecdhePossession = (ECDHEPossession)poss;340break;341}342}343344if (ecdhePossession == null || x509Credentials == null) {345throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,346"No sufficient ECDH key agreement parameters negotiated");347}348349return new ECDHEKAKeyDerivation(chc,350ecdhePossession.privateKey, x509Credentials.popPublicKey);351}352}353354private static final355class ECDHEKAGenerator implements SSLKeyAgreementGenerator {356// Prevent instantiation of this class.357private ECDHEKAGenerator() {358// blank359}360361@Override362public SSLKeyDerivation createKeyDerivation(363HandshakeContext context) throws IOException {364ECDHEPossession ecdhePossession = null;365ECDHECredentials ecdheCredentials = null;366for (SSLPossession poss : context.handshakePossessions) {367if (!(poss instanceof ECDHEPossession)) {368continue;369}370371NamedGroup ng = ((ECDHEPossession)poss).namedGroup;372for (SSLCredentials cred : context.handshakeCredentials) {373if (!(cred instanceof ECDHECredentials)) {374continue;375}376if (ng.equals(((ECDHECredentials)cred).namedGroup)) {377ecdheCredentials = (ECDHECredentials)cred;378break;379}380}381382if (ecdheCredentials != null) {383ecdhePossession = (ECDHEPossession)poss;384break;385}386}387388if (ecdhePossession == null || ecdheCredentials == null) {389throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,390"No sufficient ECDHE key agreement parameters negotiated");391}392393return new ECDHEKAKeyDerivation(context,394ecdhePossession.privateKey, ecdheCredentials.popPublicKey);395}396}397398private static final399class ECDHEKAKeyDerivation implements SSLKeyDerivation {400private final HandshakeContext context;401private final PrivateKey localPrivateKey;402private final PublicKey peerPublicKey;403404ECDHEKAKeyDerivation(HandshakeContext context,405PrivateKey localPrivateKey,406PublicKey peerPublicKey) {407this.context = context;408this.localPrivateKey = localPrivateKey;409this.peerPublicKey = peerPublicKey;410}411412@Override413public SecretKey deriveKey(String algorithm,414AlgorithmParameterSpec params) throws IOException {415if (!context.negotiatedProtocol.useTLS13PlusSpec()) {416return t12DeriveKey(algorithm, params);417} else {418return t13DeriveKey(algorithm, params);419}420}421422private SecretKey t12DeriveKey(String algorithm,423AlgorithmParameterSpec params) throws IOException {424try {425KeyAgreement ka = JsseJce.getKeyAgreement("ECDH");426ka.init(localPrivateKey);427ka.doPhase(peerPublicKey, true);428SecretKey preMasterSecret =429ka.generateSecret("TlsPremasterSecret");430431SSLMasterKeyDerivation mskd =432SSLMasterKeyDerivation.valueOf(433context.negotiatedProtocol);434if (mskd == null) {435// unlikely436throw new SSLHandshakeException(437"No expected master key derivation for protocol: " +438context.negotiatedProtocol.name);439}440SSLKeyDerivation kd = mskd.createKeyDerivation(441context, preMasterSecret);442return kd.deriveKey("MasterSecret", params);443} catch (GeneralSecurityException gse) {444throw (SSLHandshakeException) new SSLHandshakeException(445"Could not generate secret").initCause(gse);446}447}448449private SecretKey t13DeriveKey(String algorithm,450AlgorithmParameterSpec params) throws IOException {451try {452KeyAgreement ka = JsseJce.getKeyAgreement("ECDH");453ka.init(localPrivateKey);454ka.doPhase(peerPublicKey, true);455SecretKey sharedSecret =456ka.generateSecret("TlsPremasterSecret");457458HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg;459SSLKeyDerivation kd = context.handshakeKeyDerivation;460HKDF hkdf = new HKDF(hashAlg.name);461if (kd == null) { // No PSK is in use.462// If PSK is not in use Early Secret will still be463// HKDF-Extract(0, 0).464byte[] zeros = new byte[hashAlg.hashLength];465SecretKeySpec ikm =466new SecretKeySpec(zeros, "TlsPreSharedSecret");467SecretKey earlySecret =468hkdf.extract(zeros, ikm, "TlsEarlySecret");469kd = new SSLSecretDerivation(context, earlySecret);470}471472// derive salt secret473SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);474475// derive handshake secret476return hkdf.extract(saltSecret, sharedSecret, algorithm);477} catch (GeneralSecurityException gse) {478throw (SSLHandshakeException) new SSLHandshakeException(479"Could not generate secret").initCause(gse);480}481}482}483}484485486