Path: blob/master/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java
67771 views
/*1* Copyright (c) 2003, 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.nio.ByteBuffer;29import java.security.CryptoPrimitive;30import java.security.GeneralSecurityException;31import java.security.PublicKey;32import java.security.interfaces.ECPublicKey;33import java.security.interfaces.XECPublicKey;34import java.security.spec.AlgorithmParameterSpec;35import java.security.spec.ECParameterSpec;36import java.security.spec.NamedParameterSpec;37import java.text.MessageFormat;38import java.util.EnumSet;39import java.util.Locale;40import javax.crypto.SecretKey;41import sun.security.ssl.SSLHandshake.HandshakeMessage;42import sun.security.ssl.X509Authentication.X509Credentials;43import sun.security.ssl.X509Authentication.X509Possession;44import sun.security.util.HexDumpEncoder;4546/**47* Pack of the "ClientKeyExchange" handshake message.48*49* This file is used by both the ECDH/ECDHE/XDH code since much of the50* code is the same between the EC named groups (i.e.51* x25519/x448/secp*r1), even though the APIs are very different (i.e.52* ECPublicKey/XECPublicKey, KeyExchange.getInstance("EC"/"XDH"), etc.).53*/54final class ECDHClientKeyExchange {55static final SSLConsumer ecdhHandshakeConsumer =56new ECDHClientKeyExchangeConsumer();57static final HandshakeProducer ecdhHandshakeProducer =58new ECDHClientKeyExchangeProducer();5960static final SSLConsumer ecdheHandshakeConsumer =61new ECDHEClientKeyExchangeConsumer();62static final HandshakeProducer ecdheHandshakeProducer =63new ECDHEClientKeyExchangeProducer();6465/**66* The ECDH/ECDHE/XDH ClientKeyExchange handshake message.67*/68private static final69class ECDHClientKeyExchangeMessage extends HandshakeMessage {70private final byte[] encodedPoint;7172ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,73byte[] encodedPublicKey) {74super(handshakeContext);7576this.encodedPoint = encodedPublicKey;77}7879ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,80ByteBuffer m) throws IOException {81super(handshakeContext);82if (m.remaining() != 0) { // explicit PublicValueEncoding83this.encodedPoint = Record.getBytes8(m);84} else {85this.encodedPoint = new byte[0];86}87}8889@Override90public SSLHandshake handshakeType() {91return SSLHandshake.CLIENT_KEY_EXCHANGE;92}9394@Override95public int messageLength() {96if (encodedPoint == null || encodedPoint.length == 0) {97return 0;98} else {99return 1 + encodedPoint.length;100}101}102103@Override104public void send(HandshakeOutStream hos) throws IOException {105if (encodedPoint != null && encodedPoint.length != 0) {106hos.putBytes8(encodedPoint);107}108}109110@Override111public String toString() {112MessageFormat messageFormat = new MessageFormat(113"\"ECDH ClientKeyExchange\": '{'\n" +114" \"ecdh public\": '{'\n" +115"{0}\n" +116" '}',\n" +117"'}'",118Locale.ENGLISH);119if (encodedPoint == null || encodedPoint.length == 0) {120Object[] messageFields = {121" <implicit>"122};123return messageFormat.format(messageFields);124} else {125HexDumpEncoder hexEncoder = new HexDumpEncoder();126Object[] messageFields = {127Utilities.indent(128hexEncoder.encodeBuffer(encodedPoint), " "),129};130return messageFormat.format(messageFields);131}132}133}134135/**136* The ECDH "ClientKeyExchange" handshake message producer.137*/138private static final139class ECDHClientKeyExchangeProducer implements HandshakeProducer {140// Prevent instantiation of this class.141private ECDHClientKeyExchangeProducer() {142// blank143}144145@Override146public byte[] produce(ConnectionContext context,147HandshakeMessage message) throws IOException {148// The producing happens in client side only.149ClientHandshakeContext chc = (ClientHandshakeContext)context;150151X509Credentials x509Credentials = null;152for (SSLCredentials credential : chc.handshakeCredentials) {153if (credential instanceof X509Credentials) {154x509Credentials = (X509Credentials)credential;155break;156}157}158159if (x509Credentials == null) {160throw chc.conContext.fatal(Alert.INTERNAL_ERROR,161"No server certificate for ECDH client key exchange");162}163164PublicKey publicKey = x509Credentials.popPublicKey;165166NamedGroup namedGroup = null;167String algorithm = publicKey.getAlgorithm();168169// Determine which NamedGroup we'll be using, then use170// the creator functions.171if (algorithm.equals("EC")) {172ECParameterSpec params = ((ECPublicKey)publicKey).getParams();173namedGroup = NamedGroup.valueOf(params);174} else if (algorithm.equals("XDH")) {175AlgorithmParameterSpec params =176((XECPublicKey)publicKey).getParams();177if (params instanceof NamedParameterSpec) {178String name = ((NamedParameterSpec)params).getName();179namedGroup = NamedGroup.nameOf(name);180}181} else {182throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,183"Not EC/XDH server certificate for " +184"ECDH client key exchange");185}186187if (namedGroup == null) {188throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,189"Unsupported EC/XDH server cert for " +190"ECDH client key exchange");191}192193SSLPossession sslPossession = namedGroup.createPossession(194chc.sslContext.getSecureRandom());195196chc.handshakePossessions.add(sslPossession);197ECDHClientKeyExchangeMessage cke =198new ECDHClientKeyExchangeMessage(199chc, sslPossession.encode());200if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {201SSLLogger.fine(202"Produced ECDH ClientKeyExchange handshake message", cke);203}204205// Output the handshake message.206cke.write(chc.handshakeOutput);207chc.handshakeOutput.flush();208209// update the states210SSLKeyExchange ke = SSLKeyExchange.valueOf(211chc.negotiatedCipherSuite.keyExchange,212chc.negotiatedProtocol);213if (ke == null) {214// unlikely215throw chc.conContext.fatal(Alert.INTERNAL_ERROR,216"Not supported key exchange type");217} else {218SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);219SecretKey masterSecret =220masterKD.deriveKey("MasterSecret", null);221chc.handshakeSession.setMasterSecret(masterSecret);222223SSLTrafficKeyDerivation kd =224SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);225if (kd == null) {226// unlikely227throw chc.conContext.fatal(Alert.INTERNAL_ERROR,228"Not supported key derivation: " +229chc.negotiatedProtocol);230} else {231chc.handshakeKeyDerivation =232kd.createKeyDerivation(chc, masterSecret);233}234}235236// The handshake message has been delivered.237return null;238}239}240241/**242* The ECDH "ClientKeyExchange" handshake message consumer.243*/244private static final245class ECDHClientKeyExchangeConsumer implements SSLConsumer {246// Prevent instantiation of this class.247private ECDHClientKeyExchangeConsumer() {248// blank249}250251@Override252public void consume(ConnectionContext context,253ByteBuffer message) throws IOException {254// The consuming happens in server side only.255ServerHandshakeContext shc = (ServerHandshakeContext)context;256257X509Possession x509Possession = null;258for (SSLPossession possession : shc.handshakePossessions) {259if (possession instanceof X509Possession) {260x509Possession = (X509Possession)possession;261break;262}263}264265if (x509Possession == null) {266// unlikely, have been checked during cipher suite negotiation.267throw shc.conContext.fatal(Alert.INTERNAL_ERROR,268"No expected EC server cert for ECDH client key exchange");269}270271// Determine which NamedGroup we'll be using, then use272// the creator functions.273NamedGroup namedGroup = null;274275// Iteratively determine the X509Possession type's ParameterSpec.276ECParameterSpec ecParams = x509Possession.getECParameterSpec();277NamedParameterSpec namedParams = null;278if (ecParams != null) {279namedGroup = NamedGroup.valueOf(ecParams);280}281282// Wasn't EC, try XEC.283if (ecParams == null) {284namedParams = x509Possession.getXECParameterSpec();285namedGroup = NamedGroup.nameOf(namedParams.getName());286}287288// Can't figure this out, bail.289if ((ecParams == null) && (namedParams == null)) {290// unlikely, have been checked during cipher suite negotiation.291throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,292"Not EC/XDH server cert for ECDH client key exchange");293}294295// unlikely, have been checked during cipher suite negotiation.296if (namedGroup == null) {297throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,298"Unknown named group in server cert for " +299"ECDH client key exchange");300}301302SSLKeyExchange ke = SSLKeyExchange.valueOf(303shc.negotiatedCipherSuite.keyExchange,304shc.negotiatedProtocol);305if (ke == null) {306// unlikely307throw shc.conContext.fatal(Alert.INTERNAL_ERROR,308"Not supported key exchange type");309}310311// parse either handshake message containing either EC/XEC.312ECDHClientKeyExchangeMessage cke =313new ECDHClientKeyExchangeMessage(shc, message);314if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {315SSLLogger.fine(316"Consuming ECDH ClientKeyExchange handshake message", cke);317}318319// create the credentials320try {321SSLCredentials sslCredentials =322namedGroup.decodeCredentials(cke.encodedPoint);323if (shc.algorithmConstraints != null &&324sslCredentials instanceof325NamedGroupCredentials namedGroupCredentials) {326if (!shc.algorithmConstraints.permits(327EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),328namedGroupCredentials.getPublicKey())) {329shc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,330"ClientKeyExchange for " + namedGroup +331" does not comply with algorithm constraints");332}333}334335shc.handshakeCredentials.add(sslCredentials);336} catch (GeneralSecurityException e) {337throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,338"Cannot decode ECDH PublicKey: " + namedGroup);339}340341// update the states342SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);343SecretKey masterSecret =344masterKD.deriveKey("MasterSecret", null);345shc.handshakeSession.setMasterSecret(masterSecret);346347SSLTrafficKeyDerivation kd =348SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);349if (kd == null) {350// unlikely351throw shc.conContext.fatal(Alert.INTERNAL_ERROR,352"Not supported key derivation: " + shc.negotiatedProtocol);353} else {354shc.handshakeKeyDerivation =355kd.createKeyDerivation(shc, masterSecret);356}357}358}359360/**361* The ECDHE "ClientKeyExchange" handshake message producer.362*/363private static final364class ECDHEClientKeyExchangeProducer implements HandshakeProducer {365// Prevent instantiation of this class.366private ECDHEClientKeyExchangeProducer() {367// blank368}369370@Override371public byte[] produce(ConnectionContext context,372HandshakeMessage message) throws IOException {373// The producing happens in client side only.374ClientHandshakeContext chc = (ClientHandshakeContext)context;375376SSLCredentials sslCredentials = null;377NamedGroup ng = null;378379// Find a good EC/XEC credential to use, determine the380// NamedGroup to use for creating Possessions/Credentials/Keys.381for (SSLCredentials cd : chc.handshakeCredentials) {382if (cd instanceof NamedGroupCredentials) {383NamedGroupCredentials creds = (NamedGroupCredentials)cd;384ng = creds.getNamedGroup();385sslCredentials = cd;386break;387}388}389390if (sslCredentials == null) {391throw chc.conContext.fatal(Alert.INTERNAL_ERROR,392"No ECDHE credentials negotiated for client key exchange");393}394395SSLPossession sslPossession = ng.createPossession(396chc.sslContext.getSecureRandom());397398chc.handshakePossessions.add(sslPossession);399400// Write the EC/XEC message.401ECDHClientKeyExchangeMessage cke =402new ECDHClientKeyExchangeMessage(403chc, sslPossession.encode());404405if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {406SSLLogger.fine(407"Produced ECDHE ClientKeyExchange handshake message", cke);408}409410// Output the handshake message.411cke.write(chc.handshakeOutput);412chc.handshakeOutput.flush();413414// update the states415SSLKeyExchange ke = SSLKeyExchange.valueOf(416chc.negotiatedCipherSuite.keyExchange,417chc.negotiatedProtocol);418if (ke == null) {419// unlikely420throw chc.conContext.fatal(Alert.INTERNAL_ERROR,421"Not supported key exchange type");422} else {423SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);424SecretKey masterSecret =425masterKD.deriveKey("MasterSecret", null);426chc.handshakeSession.setMasterSecret(masterSecret);427428SSLTrafficKeyDerivation kd =429SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);430if (kd == null) {431// unlikely432throw chc.conContext.fatal(Alert.INTERNAL_ERROR,433"Not supported key derivation: " +434chc.negotiatedProtocol);435} else {436chc.handshakeKeyDerivation =437kd.createKeyDerivation(chc, masterSecret);438}439}440441// The handshake message has been delivered.442return null;443}444}445446/**447* The ECDHE "ClientKeyExchange" handshake message consumer.448*/449private static final450class ECDHEClientKeyExchangeConsumer implements SSLConsumer {451// Prevent instantiation of this class.452private ECDHEClientKeyExchangeConsumer() {453// blank454}455456@Override457public void consume(ConnectionContext context,458ByteBuffer message) throws IOException {459// The consuming happens in server side only.460ServerHandshakeContext shc = (ServerHandshakeContext)context;461462SSLPossession sslPossession = null;463NamedGroup namedGroup = null;464465// Find a good EC/XEC credential to use, determine the466// NamedGroup to use for creating Possessions/Credentials/Keys.467for (SSLPossession possession : shc.handshakePossessions) {468if (possession instanceof NamedGroupPossession) {469NamedGroupPossession poss =470(NamedGroupPossession)possession;471namedGroup = poss.getNamedGroup();472sslPossession = poss;473break;474}475}476477if (sslPossession == null) {478// unlikely479throw shc.conContext.fatal(Alert.INTERNAL_ERROR,480"No expected ECDHE possessions for client key exchange");481}482483if (namedGroup == null) {484// unlikely, have been checked during cipher suite negotiation485throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,486"Unsupported EC server cert for ECDHE client key exchange");487}488489SSLKeyExchange ke = SSLKeyExchange.valueOf(490shc.negotiatedCipherSuite.keyExchange,491shc.negotiatedProtocol);492if (ke == null) {493// unlikely494throw shc.conContext.fatal(Alert.INTERNAL_ERROR,495"Not supported key exchange type");496}497498// parse the EC/XEC handshake message499ECDHClientKeyExchangeMessage cke =500new ECDHClientKeyExchangeMessage(shc, message);501if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {502SSLLogger.fine(503"Consuming ECDHE ClientKeyExchange handshake message", cke);504}505506// create the credentials507try {508SSLCredentials sslCredentials =509namedGroup.decodeCredentials(cke.encodedPoint);510if (shc.algorithmConstraints != null &&511sslCredentials instanceof512NamedGroupCredentials namedGroupCredentials) {513if (!shc.algorithmConstraints.permits(514EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),515namedGroupCredentials.getPublicKey())) {516shc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,517"ClientKeyExchange for " + namedGroup +518" does not comply with algorithm constraints");519}520}521522shc.handshakeCredentials.add(sslCredentials);523} catch (GeneralSecurityException e) {524throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,525"Cannot decode named group: " + namedGroup);526}527528// update the states529SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);530SecretKey masterSecret =531masterKD.deriveKey("MasterSecret", null);532shc.handshakeSession.setMasterSecret(masterSecret);533534SSLTrafficKeyDerivation kd =535SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);536if (kd == null) {537// unlikely538throw shc.conContext.fatal(Alert.INTERNAL_ERROR,539"Not supported key derivation: " + shc.negotiatedProtocol);540} else {541shc.handshakeKeyDerivation =542kd.createKeyDerivation(shc, masterSecret);543}544}545}546}547548549