Path: blob/master/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java
41159 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.GeneralSecurityException;30import java.security.PublicKey;31import java.security.interfaces.ECPublicKey;32import java.security.interfaces.XECPublicKey;33import java.security.spec.AlgorithmParameterSpec;34import java.security.spec.ECParameterSpec;35import java.security.spec.NamedParameterSpec;36import java.text.MessageFormat;37import java.util.Locale;38import javax.crypto.SecretKey;39import sun.security.ssl.SSLHandshake.HandshakeMessage;40import sun.security.ssl.X509Authentication.X509Credentials;41import sun.security.ssl.X509Authentication.X509Possession;42import sun.security.util.HexDumpEncoder;4344/**45* Pack of the "ClientKeyExchange" handshake message.46*47* This file is used by both the ECDH/ECDHE/XDH code since much of the48* code is the same between the EC named groups (i.e.49* x25519/x448/secp*r1), even though the APIs are very different (i.e.50* ECPublicKey/XECPublicKey, KeyExchange.getInstance("EC"/"XDH"), etc.).51*/52final class ECDHClientKeyExchange {53static final SSLConsumer ecdhHandshakeConsumer =54new ECDHClientKeyExchangeConsumer();55static final HandshakeProducer ecdhHandshakeProducer =56new ECDHClientKeyExchangeProducer();5758static final SSLConsumer ecdheHandshakeConsumer =59new ECDHEClientKeyExchangeConsumer();60static final HandshakeProducer ecdheHandshakeProducer =61new ECDHEClientKeyExchangeProducer();6263/**64* The ECDH/ECDHE/XDH ClientKeyExchange handshake message.65*/66private static final67class ECDHClientKeyExchangeMessage extends HandshakeMessage {68private final byte[] encodedPoint;6970ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,71byte[] encodedPublicKey) {72super(handshakeContext);7374this.encodedPoint = encodedPublicKey;75}7677ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,78ByteBuffer m) throws IOException {79super(handshakeContext);80if (m.remaining() != 0) { // explicit PublicValueEncoding81this.encodedPoint = Record.getBytes8(m);82} else {83this.encodedPoint = new byte[0];84}85}8687@Override88public SSLHandshake handshakeType() {89return SSLHandshake.CLIENT_KEY_EXCHANGE;90}9192@Override93public int messageLength() {94if (encodedPoint == null || encodedPoint.length == 0) {95return 0;96} else {97return 1 + encodedPoint.length;98}99}100101@Override102public void send(HandshakeOutStream hos) throws IOException {103if (encodedPoint != null && encodedPoint.length != 0) {104hos.putBytes8(encodedPoint);105}106}107108@Override109public String toString() {110MessageFormat messageFormat = new MessageFormat(111"\"ECDH ClientKeyExchange\": '{'\n" +112" \"ecdh public\": '{'\n" +113"{0}\n" +114" '}',\n" +115"'}'",116Locale.ENGLISH);117if (encodedPoint == null || encodedPoint.length == 0) {118Object[] messageFields = {119" <implicit>"120};121return messageFormat.format(messageFields);122} else {123HexDumpEncoder hexEncoder = new HexDumpEncoder();124Object[] messageFields = {125Utilities.indent(126hexEncoder.encodeBuffer(encodedPoint), " "),127};128return messageFormat.format(messageFields);129}130}131}132133/**134* The ECDH "ClientKeyExchange" handshake message producer.135*/136private static final137class ECDHClientKeyExchangeProducer implements HandshakeProducer {138// Prevent instantiation of this class.139private ECDHClientKeyExchangeProducer() {140// blank141}142143@Override144public byte[] produce(ConnectionContext context,145HandshakeMessage message) throws IOException {146// The producing happens in client side only.147ClientHandshakeContext chc = (ClientHandshakeContext)context;148149X509Credentials x509Credentials = null;150for (SSLCredentials credential : chc.handshakeCredentials) {151if (credential instanceof X509Credentials) {152x509Credentials = (X509Credentials)credential;153break;154}155}156157if (x509Credentials == null) {158throw chc.conContext.fatal(Alert.INTERNAL_ERROR,159"No server certificate for ECDH client key exchange");160}161162PublicKey publicKey = x509Credentials.popPublicKey;163164NamedGroup namedGroup = null;165String algorithm = publicKey.getAlgorithm();166167// Determine which NamedGroup we'll be using, then use168// the creator functions.169if (algorithm.equals("EC")) {170ECParameterSpec params = ((ECPublicKey)publicKey).getParams();171namedGroup = NamedGroup.valueOf(params);172} else if (algorithm.equals("XDH")) {173AlgorithmParameterSpec params =174((XECPublicKey)publicKey).getParams();175if (params instanceof NamedParameterSpec) {176String name = ((NamedParameterSpec)params).getName();177namedGroup = NamedGroup.nameOf(name);178}179} else {180throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,181"Not EC/XDH server certificate for " +182"ECDH client key exchange");183}184185if (namedGroup == null) {186throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,187"Unsupported EC/XDH server cert for " +188"ECDH client key exchange");189}190191SSLPossession sslPossession = namedGroup.createPossession(192chc.sslContext.getSecureRandom());193194chc.handshakePossessions.add(sslPossession);195ECDHClientKeyExchangeMessage cke =196new ECDHClientKeyExchangeMessage(197chc, sslPossession.encode());198if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {199SSLLogger.fine(200"Produced ECDH ClientKeyExchange handshake message", cke);201}202203// Output the handshake message.204cke.write(chc.handshakeOutput);205chc.handshakeOutput.flush();206207// update the states208SSLKeyExchange ke = SSLKeyExchange.valueOf(209chc.negotiatedCipherSuite.keyExchange,210chc.negotiatedProtocol);211if (ke == null) {212// unlikely213throw chc.conContext.fatal(Alert.INTERNAL_ERROR,214"Not supported key exchange type");215} else {216SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);217SecretKey masterSecret =218masterKD.deriveKey("MasterSecret", null);219chc.handshakeSession.setMasterSecret(masterSecret);220221SSLTrafficKeyDerivation kd =222SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);223if (kd == null) {224// unlikely225throw chc.conContext.fatal(Alert.INTERNAL_ERROR,226"Not supported key derivation: " +227chc.negotiatedProtocol);228} else {229chc.handshakeKeyDerivation =230kd.createKeyDerivation(chc, masterSecret);231}232}233234// The handshake message has been delivered.235return null;236}237}238239/**240* The ECDH "ClientKeyExchange" handshake message consumer.241*/242private static final243class ECDHClientKeyExchangeConsumer implements SSLConsumer {244// Prevent instantiation of this class.245private ECDHClientKeyExchangeConsumer() {246// blank247}248249@Override250public void consume(ConnectionContext context,251ByteBuffer message) throws IOException {252// The consuming happens in server side only.253ServerHandshakeContext shc = (ServerHandshakeContext)context;254255X509Possession x509Possession = null;256for (SSLPossession possession : shc.handshakePossessions) {257if (possession instanceof X509Possession) {258x509Possession = (X509Possession)possession;259break;260}261}262263if (x509Possession == null) {264// unlikely, have been checked during cipher suite negotiation.265throw shc.conContext.fatal(Alert.INTERNAL_ERROR,266"No expected EC server cert for ECDH client key exchange");267}268269// Determine which NamedGroup we'll be using, then use270// the creator functions.271NamedGroup namedGroup = null;272273// Iteratively determine the X509Possession type's ParameterSpec.274ECParameterSpec ecParams = x509Possession.getECParameterSpec();275NamedParameterSpec namedParams = null;276if (ecParams != null) {277namedGroup = NamedGroup.valueOf(ecParams);278}279280// Wasn't EC, try XEC.281if (ecParams == null) {282namedParams = x509Possession.getXECParameterSpec();283namedGroup = NamedGroup.nameOf(namedParams.getName());284}285286// Can't figure this out, bail.287if ((ecParams == null) && (namedParams == null)) {288// unlikely, have been checked during cipher suite negotiation.289throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,290"Not EC/XDH server cert for ECDH client key exchange");291}292293// unlikely, have been checked during cipher suite negotiation.294if (namedGroup == null) {295throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,296"Unknown named group in server cert for " +297"ECDH client key exchange");298}299300SSLKeyExchange ke = SSLKeyExchange.valueOf(301shc.negotiatedCipherSuite.keyExchange,302shc.negotiatedProtocol);303if (ke == null) {304// unlikely305throw shc.conContext.fatal(Alert.INTERNAL_ERROR,306"Not supported key exchange type");307}308309// parse either handshake message containing either EC/XEC.310ECDHClientKeyExchangeMessage cke =311new ECDHClientKeyExchangeMessage(shc, message);312if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {313SSLLogger.fine(314"Consuming ECDH ClientKeyExchange handshake message", cke);315}316317// create the credentials318try {319NamedGroup ng = namedGroup; // "effectively final" the lambda320// AlgorithmConstraints are checked internally.321SSLCredentials sslCredentials = namedGroup.decodeCredentials(322cke.encodedPoint, shc.algorithmConstraints,323s -> shc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,324"ClientKeyExchange " + ng + ": " + s));325326shc.handshakeCredentials.add(sslCredentials);327} catch (GeneralSecurityException e) {328throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,329"Cannot decode ECDH PublicKey: " + namedGroup);330}331332// update the states333SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);334SecretKey masterSecret =335masterKD.deriveKey("MasterSecret", null);336shc.handshakeSession.setMasterSecret(masterSecret);337338SSLTrafficKeyDerivation kd =339SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);340if (kd == null) {341// unlikely342throw shc.conContext.fatal(Alert.INTERNAL_ERROR,343"Not supported key derivation: " + shc.negotiatedProtocol);344} else {345shc.handshakeKeyDerivation =346kd.createKeyDerivation(shc, masterSecret);347}348}349}350351/**352* The ECDHE "ClientKeyExchange" handshake message producer.353*/354private static final355class ECDHEClientKeyExchangeProducer implements HandshakeProducer {356// Prevent instantiation of this class.357private ECDHEClientKeyExchangeProducer() {358// blank359}360361@Override362public byte[] produce(ConnectionContext context,363HandshakeMessage message) throws IOException {364// The producing happens in client side only.365ClientHandshakeContext chc = (ClientHandshakeContext)context;366367SSLCredentials sslCredentials = null;368NamedGroup ng = null;369370// Find a good EC/XEC credential to use, determine the371// NamedGroup to use for creating Possessions/Credentials/Keys.372for (SSLCredentials cd : chc.handshakeCredentials) {373if (cd instanceof NamedGroupCredentials) {374NamedGroupCredentials creds = (NamedGroupCredentials)cd;375ng = creds.getNamedGroup();376sslCredentials = cd;377break;378}379}380381if (sslCredentials == null) {382throw chc.conContext.fatal(Alert.INTERNAL_ERROR,383"No ECDHE credentials negotiated for client key exchange");384}385386SSLPossession sslPossession = ng.createPossession(387chc.sslContext.getSecureRandom());388389chc.handshakePossessions.add(sslPossession);390391// Write the EC/XEC message.392ECDHClientKeyExchangeMessage cke =393new ECDHClientKeyExchangeMessage(394chc, sslPossession.encode());395396if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {397SSLLogger.fine(398"Produced ECDHE ClientKeyExchange handshake message", cke);399}400401// Output the handshake message.402cke.write(chc.handshakeOutput);403chc.handshakeOutput.flush();404405// update the states406SSLKeyExchange ke = SSLKeyExchange.valueOf(407chc.negotiatedCipherSuite.keyExchange,408chc.negotiatedProtocol);409if (ke == null) {410// unlikely411throw chc.conContext.fatal(Alert.INTERNAL_ERROR,412"Not supported key exchange type");413} else {414SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);415SecretKey masterSecret =416masterKD.deriveKey("MasterSecret", null);417chc.handshakeSession.setMasterSecret(masterSecret);418419SSLTrafficKeyDerivation kd =420SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);421if (kd == null) {422// unlikely423throw chc.conContext.fatal(Alert.INTERNAL_ERROR,424"Not supported key derivation: " +425chc.negotiatedProtocol);426} else {427chc.handshakeKeyDerivation =428kd.createKeyDerivation(chc, masterSecret);429}430}431432// The handshake message has been delivered.433return null;434}435}436437/**438* The ECDHE "ClientKeyExchange" handshake message consumer.439*/440private static final441class ECDHEClientKeyExchangeConsumer implements SSLConsumer {442// Prevent instantiation of this class.443private ECDHEClientKeyExchangeConsumer() {444// blank445}446447@Override448public void consume(ConnectionContext context,449ByteBuffer message) throws IOException {450// The consuming happens in server side only.451ServerHandshakeContext shc = (ServerHandshakeContext)context;452453SSLPossession sslPossession = null;454NamedGroup namedGroup = null;455456// Find a good EC/XEC credential to use, determine the457// NamedGroup to use for creating Possessions/Credentials/Keys.458for (SSLPossession possession : shc.handshakePossessions) {459if (possession instanceof NamedGroupPossession) {460NamedGroupPossession poss =461(NamedGroupPossession)possession;462namedGroup = poss.getNamedGroup();463sslPossession = poss;464break;465}466}467468if (sslPossession == null) {469// unlikely470throw shc.conContext.fatal(Alert.INTERNAL_ERROR,471"No expected ECDHE possessions for client key exchange");472}473474if (namedGroup == null) {475// unlikely, have been checked during cipher suite negotiation476throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,477"Unsupported EC server cert for ECDHE client key exchange");478}479480SSLKeyExchange ke = SSLKeyExchange.valueOf(481shc.negotiatedCipherSuite.keyExchange,482shc.negotiatedProtocol);483if (ke == null) {484// unlikely485throw shc.conContext.fatal(Alert.INTERNAL_ERROR,486"Not supported key exchange type");487}488489// parse the EC/XEC handshake message490ECDHClientKeyExchangeMessage cke =491new ECDHClientKeyExchangeMessage(shc, message);492if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {493SSLLogger.fine(494"Consuming ECDHE ClientKeyExchange handshake message", cke);495}496497// create the credentials498try {499NamedGroup ng = namedGroup; // "effectively final" the lambda500// AlgorithmConstraints are checked internally.501SSLCredentials sslCredentials = namedGroup.decodeCredentials(502cke.encodedPoint, shc.algorithmConstraints,503s -> shc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,504"ClientKeyExchange " + ng + ": " + s));505506shc.handshakeCredentials.add(sslCredentials);507} catch (GeneralSecurityException e) {508throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,509"Cannot decode named group: " + namedGroup);510}511512// update the states513SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);514SecretKey masterSecret =515masterKD.deriveKey("MasterSecret", null);516shc.handshakeSession.setMasterSecret(masterSecret);517518SSLTrafficKeyDerivation kd =519SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);520if (kd == null) {521// unlikely522throw shc.conContext.fatal(Alert.INTERNAL_ERROR,523"Not supported key derivation: " + shc.negotiatedProtocol);524} else {525shc.handshakeKeyDerivation =526kd.createKeyDerivation(shc, masterSecret);527}528}529}530}531532533