Path: blob/master/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java
41159 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.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.ECParameterSpec;39import java.security.spec.ECPoint;40import java.security.spec.ECPublicKeySpec;41import java.util.EnumSet;42import javax.crypto.KeyAgreement;43import javax.crypto.SecretKey;44import javax.net.ssl.SSLHandshakeException;45import sun.security.ssl.NamedGroup.NamedGroupSpec;46import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;47import sun.security.ssl.X509Authentication.X509Credentials;48import sun.security.ssl.X509Authentication.X509Possession;49import sun.security.ssl.XDHKeyExchange.XDHECredentials;50import sun.security.ssl.XDHKeyExchange.XDHEPossession;51import sun.security.util.ECUtil;5253final class ECDHKeyExchange {54static final SSLPossessionGenerator poGenerator =55new ECDHEPossessionGenerator();56static final SSLKeyAgreementGenerator ecdhKAGenerator =57new ECDHKAGenerator();5859// TLSv1.360static final SSLKeyAgreementGenerator ecdheKAGenerator =61new ECDHEKAGenerator();6263// TLSv1-1.2, the KA gets more difficult with EC/XEC keys64static final SSLKeyAgreementGenerator ecdheXdhKAGenerator =65new ECDHEXDHKAGenerator();6667static final class ECDHECredentials implements NamedGroupCredentials {68final ECPublicKey popPublicKey;69final NamedGroup namedGroup;7071ECDHECredentials(ECPublicKey popPublicKey, NamedGroup namedGroup) {72this.popPublicKey = popPublicKey;73this.namedGroup = namedGroup;74}7576@Override77public PublicKey getPublicKey() {78return popPublicKey;79}8081@Override82public NamedGroup getNamedGroup() {83return namedGroup;84}8586static ECDHECredentials valueOf(NamedGroup namedGroup,87byte[] encodedPoint) throws IOException, GeneralSecurityException {8889if (namedGroup.spec != NamedGroupSpec.NAMED_GROUP_ECDHE) {90throw new RuntimeException(91"Credentials decoding: Not ECDHE named group");92}9394if (encodedPoint == null || encodedPoint.length == 0) {95return null;96}9798ECParameterSpec parameters =99(ECParameterSpec)namedGroup.keAlgParamSpec;100ECPoint point = ECUtil.decodePoint(101encodedPoint, parameters.getCurve());102KeyFactory factory = KeyFactory.getInstance("EC");103ECPublicKey publicKey = (ECPublicKey)factory.generatePublic(104new ECPublicKeySpec(point, parameters));105return new ECDHECredentials(publicKey, namedGroup);106}107}108109static final class ECDHEPossession implements NamedGroupPossession {110final PrivateKey privateKey;111final ECPublicKey publicKey;112final NamedGroup namedGroup;113114ECDHEPossession(NamedGroup namedGroup, SecureRandom random) {115try {116KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");117kpg.initialize(namedGroup.keAlgParamSpec, random);118KeyPair kp = kpg.generateKeyPair();119privateKey = kp.getPrivate();120publicKey = (ECPublicKey)kp.getPublic();121} catch (GeneralSecurityException e) {122throw new RuntimeException(123"Could not generate ECDH keypair", e);124}125126this.namedGroup = namedGroup;127}128129ECDHEPossession(ECDHECredentials credentials, SecureRandom random) {130ECParameterSpec params = credentials.popPublicKey.getParams();131try {132KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");133kpg.initialize(params, random);134KeyPair kp = kpg.generateKeyPair();135privateKey = kp.getPrivate();136publicKey = (ECPublicKey)kp.getPublic();137} catch (GeneralSecurityException e) {138throw new RuntimeException(139"Could not generate ECDH keypair", e);140}141142this.namedGroup = credentials.namedGroup;143}144145@Override146public byte[] encode() {147return ECUtil.encodePoint(148publicKey.getW(), publicKey.getParams().getCurve());149}150151// called by ClientHandshaker with either the server's static or152// ephemeral public key153SecretKey getAgreedSecret(154PublicKey peerPublicKey) throws SSLHandshakeException {155156try {157KeyAgreement ka = KeyAgreement.getInstance("ECDH");158ka.init(privateKey);159ka.doPhase(peerPublicKey, true);160return ka.generateSecret("TlsPremasterSecret");161} catch (GeneralSecurityException e) {162throw (SSLHandshakeException) new SSLHandshakeException(163"Could not generate secret").initCause(e);164}165}166167// called by ServerHandshaker168SecretKey getAgreedSecret(169byte[] encodedPoint) throws SSLHandshakeException {170try {171ECParameterSpec params = publicKey.getParams();172ECPoint point =173ECUtil.decodePoint(encodedPoint, params.getCurve());174KeyFactory kf = KeyFactory.getInstance("EC");175ECPublicKeySpec spec = new ECPublicKeySpec(point, params);176PublicKey peerPublicKey = kf.generatePublic(spec);177return getAgreedSecret(peerPublicKey);178} catch (GeneralSecurityException | java.io.IOException e) {179throw (SSLHandshakeException) new SSLHandshakeException(180"Could not generate secret").initCause(e);181}182}183184// Check constraints of the specified EC public key.185void checkConstraints(AlgorithmConstraints constraints,186byte[] encodedPoint) throws SSLHandshakeException {187try {188189ECParameterSpec params = publicKey.getParams();190ECPoint point =191ECUtil.decodePoint(encodedPoint, params.getCurve());192ECPublicKeySpec spec = new ECPublicKeySpec(point, params);193194KeyFactory kf = KeyFactory.getInstance("EC");195ECPublicKey pubKey = (ECPublicKey)kf.generatePublic(spec);196197// check constraints of ECPublicKey198if (!constraints.permits(199EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), pubKey)) {200throw new SSLHandshakeException(201"ECPublicKey does not comply to algorithm constraints");202}203} catch (GeneralSecurityException | java.io.IOException e) {204throw (SSLHandshakeException) new SSLHandshakeException(205"Could not generate ECPublicKey").initCause(e);206}207}208209@Override210public PublicKey getPublicKey() {211return publicKey;212}213214@Override215public NamedGroup getNamedGroup() {216return namedGroup;217}218219@Override220public PrivateKey getPrivateKey() {221return privateKey;222}223}224225private static final226class ECDHEPossessionGenerator implements SSLPossessionGenerator {227// Prevent instantiation of this class.228private ECDHEPossessionGenerator() {229// blank230}231232@Override233public SSLPossession createPossession(HandshakeContext context) {234235NamedGroup preferableNamedGroup;236237// Find most preferred EC or XEC groups238if ((context.clientRequestedNamedGroups != null) &&239(!context.clientRequestedNamedGroups.isEmpty())) {240preferableNamedGroup = SupportedGroups.getPreferredGroup(241context.negotiatedProtocol,242context.algorithmConstraints,243new NamedGroupSpec[] {244NamedGroupSpec.NAMED_GROUP_ECDHE,245NamedGroupSpec.NAMED_GROUP_XDH },246context.clientRequestedNamedGroups);247} else {248preferableNamedGroup = SupportedGroups.getPreferredGroup(249context.negotiatedProtocol,250context.algorithmConstraints,251new NamedGroupSpec[] {252NamedGroupSpec.NAMED_GROUP_ECDHE,253NamedGroupSpec.NAMED_GROUP_XDH });254}255256if (preferableNamedGroup != null) {257return preferableNamedGroup.createPossession(258context.sslContext.getSecureRandom());259}260261// no match found, cannot use this cipher suite.262//263return null;264}265}266267private static final268class ECDHKAGenerator implements SSLKeyAgreementGenerator {269// Prevent instantiation of this class.270private ECDHKAGenerator() {271// blank272}273274@Override275public SSLKeyDerivation createKeyDerivation(276HandshakeContext context) throws IOException {277if (context instanceof ServerHandshakeContext) {278return createServerKeyDerivation(279(ServerHandshakeContext)context);280} else {281return createClientKeyDerivation(282(ClientHandshakeContext)context);283}284}285286private SSLKeyDerivation createServerKeyDerivation(287ServerHandshakeContext shc) throws IOException {288X509Possession x509Possession = null;289ECDHECredentials ecdheCredentials = null;290for (SSLPossession poss : shc.handshakePossessions) {291if (!(poss instanceof X509Possession)) {292continue;293}294295ECParameterSpec params =296((X509Possession)poss).getECParameterSpec();297if (params == null) {298continue;299}300301NamedGroup ng = NamedGroup.valueOf(params);302if (ng == null) {303// unlikely, have been checked during cipher suite304// negotiation.305throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,306"Unsupported EC server cert for ECDH key exchange");307}308309for (SSLCredentials cred : shc.handshakeCredentials) {310if (!(cred instanceof ECDHECredentials)) {311continue;312}313if (ng.equals(((ECDHECredentials)cred).namedGroup)) {314ecdheCredentials = (ECDHECredentials)cred;315break;316}317}318319if (ecdheCredentials != null) {320x509Possession = (X509Possession)poss;321break;322}323}324325if (x509Possession == null || ecdheCredentials == null) {326throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,327"No sufficient ECDHE key agreement parameters negotiated");328}329330return new KAKeyDerivation("ECDH", shc,331x509Possession.popPrivateKey, ecdheCredentials.popPublicKey);332}333334private SSLKeyDerivation createClientKeyDerivation(335ClientHandshakeContext chc) throws IOException {336ECDHEPossession ecdhePossession = null;337X509Credentials x509Credentials = null;338for (SSLPossession poss : chc.handshakePossessions) {339if (!(poss instanceof ECDHEPossession)) {340continue;341}342343NamedGroup ng = ((ECDHEPossession)poss).namedGroup;344for (SSLCredentials cred : chc.handshakeCredentials) {345if (!(cred instanceof X509Credentials)) {346continue;347}348349PublicKey publicKey = ((X509Credentials)cred).popPublicKey;350if (!publicKey.getAlgorithm().equals("EC")) {351continue;352}353ECParameterSpec params =354((ECPublicKey)publicKey).getParams();355NamedGroup namedGroup = NamedGroup.valueOf(params);356if (namedGroup == null) {357// unlikely, should have been checked previously358throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,359"Unsupported EC server cert for ECDH key exchange");360}361362if (ng.equals(namedGroup)) {363x509Credentials = (X509Credentials)cred;364break;365}366}367368if (x509Credentials != null) {369ecdhePossession = (ECDHEPossession)poss;370break;371}372}373374if (ecdhePossession == null || x509Credentials == null) {375throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,376"No sufficient ECDH key agreement parameters negotiated");377}378379return new KAKeyDerivation("ECDH", chc,380ecdhePossession.privateKey, x509Credentials.popPublicKey);381}382}383384private static final385class ECDHEKAGenerator implements SSLKeyAgreementGenerator {386// Prevent instantiation of this class.387private ECDHEKAGenerator() {388// blank389}390391@Override392public SSLKeyDerivation createKeyDerivation(393HandshakeContext context) throws IOException {394ECDHEPossession ecdhePossession = null;395ECDHECredentials ecdheCredentials = null;396for (SSLPossession poss : context.handshakePossessions) {397if (!(poss instanceof ECDHEPossession)) {398continue;399}400401NamedGroup ng = ((ECDHEPossession)poss).namedGroup;402for (SSLCredentials cred : context.handshakeCredentials) {403if (!(cred instanceof ECDHECredentials)) {404continue;405}406if (ng.equals(((ECDHECredentials)cred).namedGroup)) {407ecdheCredentials = (ECDHECredentials)cred;408break;409}410}411412if (ecdheCredentials != null) {413ecdhePossession = (ECDHEPossession)poss;414break;415}416}417418if (ecdhePossession == null || ecdheCredentials == null) {419throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,420"No sufficient ECDHE key agreement parameters negotiated");421}422423return new KAKeyDerivation("ECDH", context,424ecdhePossession.privateKey, ecdheCredentials.popPublicKey);425}426}427428/*429* A Generator for TLSv1-1.2 to create a ECDHE or a XDH KeyDerivation430* object depending on the negotiated group.431*/432private static final433class ECDHEXDHKAGenerator implements SSLKeyAgreementGenerator {434// Prevent instantiation of this class.435private ECDHEXDHKAGenerator() {436// blank437}438439@Override440public SSLKeyDerivation createKeyDerivation(441HandshakeContext context) throws IOException {442443NamedGroupPossession namedGroupPossession = null;444NamedGroupCredentials namedGroupCredentials = null;445NamedGroup namedGroup = null;446447// Find a possession/credential combo using the same named group448search:449for (SSLPossession poss : context.handshakePossessions) {450for (SSLCredentials cred : context.handshakeCredentials) {451if (((poss instanceof ECDHEPossession) &&452(cred instanceof ECDHECredentials)) ||453(((poss instanceof XDHEPossession) &&454(cred instanceof XDHECredentials)))) {455NamedGroupPossession p = (NamedGroupPossession)poss;456NamedGroupCredentials c = (NamedGroupCredentials)cred;457if (p.getNamedGroup() != c.getNamedGroup()) {458continue;459} else {460namedGroup = p.getNamedGroup();461}462namedGroupPossession = p;463namedGroupCredentials = c;464break search;465}466}467}468469if (namedGroupPossession == null || namedGroupCredentials == null) {470throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,471"No sufficient ECDHE/XDH key agreement " +472"parameters negotiated");473}474475String alg;476switch (namedGroup.spec) {477case NAMED_GROUP_ECDHE:478alg = "ECDH";479break;480case NAMED_GROUP_XDH:481alg = "XDH";482break;483default:484throw new RuntimeException("Unexpected named group type");485}486487return new KAKeyDerivation(alg, context,488namedGroupPossession.getPrivateKey(),489namedGroupCredentials.getPublicKey());490}491}492}493494495