Path: blob/master/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java
67770 views
/*1* Copyright (c) 2015, 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.InvalidAlgorithmParameterException;32import java.security.InvalidKeyException;33import java.security.Key;34import java.security.NoSuchAlgorithmException;35import java.security.PrivateKey;36import java.security.PublicKey;37import java.security.Signature;38import java.security.SignatureException;39import java.text.MessageFormat;40import java.util.EnumSet;41import java.util.Locale;42import java.util.Map;43import sun.security.ssl.SSLHandshake.HandshakeMessage;44import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;45import sun.security.ssl.X509Authentication.X509Credentials;46import sun.security.ssl.X509Authentication.X509Possession;47import sun.security.util.HexDumpEncoder;4849/**50* Pack of the ServerKeyExchange handshake message.51*/52final class ECDHServerKeyExchange {53static final SSLConsumer ecdheHandshakeConsumer =54new ECDHServerKeyExchangeConsumer();55static final HandshakeProducer ecdheHandshakeProducer =56new ECDHServerKeyExchangeProducer();5758/**59* The ECDH ServerKeyExchange handshake message.60*/61private static final62class ECDHServerKeyExchangeMessage extends HandshakeMessage {63private static final byte CURVE_NAMED_CURVE = (byte)0x03;6465// id of the named curve66private final NamedGroup namedGroup;6768// encoded public point69private final byte[] publicPoint;7071// signature bytes, or null if anonymous72private final byte[] paramsSignature;7374private final boolean useExplicitSigAlgorithm;7576// the signature algorithm used by this ServerKeyExchange message77private final SignatureScheme signatureScheme;7879// the parsed credential object80private SSLCredentials sslCredentials;8182ECDHServerKeyExchangeMessage(83HandshakeContext handshakeContext) throws IOException {84super(handshakeContext);8586// This happens in server side only.87ServerHandshakeContext shc =88(ServerHandshakeContext)handshakeContext;8990// Find the Possessions needed91NamedGroupPossession namedGroupPossession = null;92X509Possession x509Possession = null;93for (SSLPossession possession : shc.handshakePossessions) {94if (possession instanceof NamedGroupPossession) {95namedGroupPossession = (NamedGroupPossession)possession;96if (x509Possession != null) {97break;98}99} else if (possession instanceof X509Possession) {100x509Possession = (X509Possession)possession;101if (namedGroupPossession != null) {102break;103}104}105}106107if (namedGroupPossession == null) {108// unlikely109throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,110"No ECDHE credentials negotiated for server key exchange");111}112113// Find the NamedGroup used for the ephemeral keys.114namedGroup = namedGroupPossession.getNamedGroup();115if ((namedGroup == null) || (!namedGroup.isAvailable)) {116// unlikely117throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,118"Missing or improper named group: " + namedGroup);119}120121publicPoint = namedGroup.encodePossessionPublicKey(122namedGroupPossession);123if (publicPoint == null) {124// unlikely125throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,126"Missing public point for named group: " + namedGroup);127}128129if (x509Possession == null) {130// anonymous, no authentication, no signature131paramsSignature = null;132signatureScheme = null;133useExplicitSigAlgorithm = false;134} else {135useExplicitSigAlgorithm =136shc.negotiatedProtocol.useTLS12PlusSpec();137Signature signer;138if (useExplicitSigAlgorithm) {139Map.Entry<SignatureScheme, Signature> schemeAndSigner =140SignatureScheme.getSignerOfPreferableAlgorithm(141shc.algorithmConstraints,142shc.peerRequestedSignatureSchemes,143x509Possession,144shc.negotiatedProtocol);145if (schemeAndSigner == null) {146// Unlikely, the credentials generator should have147// selected the preferable signature algorithm properly.148throw shc.conContext.fatal(Alert.INTERNAL_ERROR,149"No supported signature algorithm for " +150x509Possession.popPrivateKey.getAlgorithm() +151" key");152} else {153signatureScheme = schemeAndSigner.getKey();154signer = schemeAndSigner.getValue();155}156} else {157signatureScheme = null;158try {159signer = getSignature(160x509Possession.popPrivateKey.getAlgorithm(),161x509Possession.popPrivateKey);162} catch (NoSuchAlgorithmException | InvalidKeyException e) {163throw shc.conContext.fatal(Alert.INTERNAL_ERROR,164"Unsupported signature algorithm: " +165x509Possession.popPrivateKey.getAlgorithm(), e);166}167}168169byte[] signature;170try {171updateSignature(signer, shc.clientHelloRandom.randomBytes,172shc.serverHelloRandom.randomBytes,173namedGroup.id, publicPoint);174signature = signer.sign();175} catch (SignatureException ex) {176throw shc.conContext.fatal(Alert.INTERNAL_ERROR,177"Failed to sign ecdhe parameters: " +178x509Possession.popPrivateKey.getAlgorithm(), ex);179}180paramsSignature = signature;181}182}183184ECDHServerKeyExchangeMessage(HandshakeContext handshakeContext,185ByteBuffer m) throws IOException {186super(handshakeContext);187188// This happens in client side only.189ClientHandshakeContext chc =190(ClientHandshakeContext)handshakeContext;191192byte curveType = (byte)Record.getInt8(m);193if (curveType != CURVE_NAMED_CURVE) {194// Unlikely as only the named curves should be negotiated.195throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,196"Unsupported ECCurveType: " + curveType);197}198199int namedGroupId = Record.getInt16(m);200this.namedGroup = NamedGroup.valueOf(namedGroupId);201if (namedGroup == null) {202throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,203"Unknown named group ID: " + namedGroupId);204}205206if (!SupportedGroups.isSupported(namedGroup)) {207throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,208"Unsupported named group: " + namedGroup);209}210211publicPoint = Record.getBytes8(m);212if (publicPoint.length == 0) {213throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,214"Insufficient Point data: " + namedGroup);215}216217try {218sslCredentials =219namedGroup.decodeCredentials(publicPoint);220if (handshakeContext.algorithmConstraints != null &&221sslCredentials instanceof222NamedGroupCredentials namedGroupCredentials) {223if (!handshakeContext.algorithmConstraints.permits(224EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),225namedGroupCredentials.getPublicKey())) {226chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,227"ServerKeyExchange for " + namedGroup +228" does not comply with algorithm constraints");229}230}231} catch (GeneralSecurityException ex) {232throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,233"Cannot decode named group: " +234NamedGroup.nameOf(namedGroupId));235}236237X509Credentials x509Credentials = null;238for (SSLCredentials cd : chc.handshakeCredentials) {239if (cd instanceof X509Credentials) {240x509Credentials = (X509Credentials)cd;241break;242}243}244245if (x509Credentials == null) {246// anonymous, no authentication, no signature247if (m.hasRemaining()) {248throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,249"Invalid DH ServerKeyExchange: unknown extra data");250}251this.signatureScheme = null;252this.paramsSignature = null;253this.useExplicitSigAlgorithm = false;254255return;256}257258this.useExplicitSigAlgorithm =259chc.negotiatedProtocol.useTLS12PlusSpec();260if (useExplicitSigAlgorithm) {261int ssid = Record.getInt16(m);262signatureScheme = SignatureScheme.valueOf(ssid);263if (signatureScheme == null) {264throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,265"Invalid signature algorithm (" + ssid +266") used in ECDH ServerKeyExchange handshake message");267}268269if (!chc.localSupportedSignAlgs.contains(signatureScheme)) {270throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,271"Unsupported signature algorithm (" +272signatureScheme.name +273") used in ECDH ServerKeyExchange handshake message");274}275} else {276signatureScheme = null;277}278279// read and verify the signature280paramsSignature = Record.getBytes16(m);281Signature signer;282if (useExplicitSigAlgorithm) {283try {284signer = signatureScheme.getVerifier(285x509Credentials.popPublicKey);286} catch (NoSuchAlgorithmException | InvalidKeyException |287InvalidAlgorithmParameterException nsae) {288throw chc.conContext.fatal(Alert.INTERNAL_ERROR,289"Unsupported signature algorithm: " +290signatureScheme.name, nsae);291}292} else {293try {294signer = getSignature(295x509Credentials.popPublicKey.getAlgorithm(),296x509Credentials.popPublicKey);297} catch (NoSuchAlgorithmException | InvalidKeyException e) {298throw chc.conContext.fatal(Alert.INTERNAL_ERROR,299"Unsupported signature algorithm: " +300x509Credentials.popPublicKey.getAlgorithm(), e);301}302}303304try {305updateSignature(signer,306chc.clientHelloRandom.randomBytes,307chc.serverHelloRandom.randomBytes,308namedGroup.id, publicPoint);309310if (!signer.verify(paramsSignature)) {311throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,312"Invalid ECDH ServerKeyExchange signature");313}314} catch (SignatureException ex) {315throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,316"Cannot verify ECDH ServerKeyExchange signature", ex);317}318}319320@Override321public SSLHandshake handshakeType() {322return SSLHandshake.SERVER_KEY_EXCHANGE;323}324325@Override326public int messageLength() {327int sigLen = 0;328if (paramsSignature != null) {329sigLen = 2 + paramsSignature.length;330if (useExplicitSigAlgorithm) {331sigLen += SignatureScheme.sizeInRecord();332}333}334335return 4 + publicPoint.length + sigLen;336}337338@Override339public void send(HandshakeOutStream hos) throws IOException {340hos.putInt8(CURVE_NAMED_CURVE);341hos.putInt16(namedGroup.id);342hos.putBytes8(publicPoint);343if (paramsSignature != null) {344if (useExplicitSigAlgorithm) {345hos.putInt16(signatureScheme.id);346}347348hos.putBytes16(paramsSignature);349}350}351352@Override353public String toString() {354if (useExplicitSigAlgorithm) {355MessageFormat messageFormat = new MessageFormat(356"\"ECDH ServerKeyExchange\": '{'\n" +357" \"parameters\": '{'\n" +358" \"named group\": \"{0}\"\n" +359" \"ecdh public\": '{'\n" +360"{1}\n" +361" '}',\n" +362" '}',\n" +363" \"digital signature\": '{'\n" +364" \"signature algorithm\": \"{2}\"\n" +365" \"signature\": '{'\n" +366"{3}\n" +367" '}',\n" +368" '}'\n" +369"'}'",370Locale.ENGLISH);371372HexDumpEncoder hexEncoder = new HexDumpEncoder();373Object[] messageFields = {374namedGroup.name,375Utilities.indent(376hexEncoder.encodeBuffer(publicPoint), " "),377signatureScheme.name,378Utilities.indent(379hexEncoder.encodeBuffer(paramsSignature), " ")380};381return messageFormat.format(messageFields);382} else if (paramsSignature != null) {383MessageFormat messageFormat = new MessageFormat(384"\"ECDH ServerKeyExchange\": '{'\n" +385" \"parameters\": '{'\n" +386" \"named group\": \"{0}\"\n" +387" \"ecdh public\": '{'\n" +388"{1}\n" +389" '}',\n" +390" '}',\n" +391" \"signature\": '{'\n" +392"{2}\n" +393" '}'\n" +394"'}'",395Locale.ENGLISH);396397HexDumpEncoder hexEncoder = new HexDumpEncoder();398Object[] messageFields = {399namedGroup.name,400Utilities.indent(401hexEncoder.encodeBuffer(publicPoint), " "),402Utilities.indent(403hexEncoder.encodeBuffer(paramsSignature), " ")404};405406return messageFormat.format(messageFields);407} else { // anonymous408MessageFormat messageFormat = new MessageFormat(409"\"ECDH ServerKeyExchange\": '{'\n" +410" \"parameters\": '{'\n" +411" \"named group\": \"{0}\"\n" +412" \"ecdh public\": '{'\n" +413"{1}\n" +414" '}',\n" +415" '}'\n" +416"'}'",417Locale.ENGLISH);418419HexDumpEncoder hexEncoder = new HexDumpEncoder();420Object[] messageFields = {421namedGroup.name,422Utilities.indent(423hexEncoder.encodeBuffer(publicPoint), " "),424};425426return messageFormat.format(messageFields);427}428}429430private static Signature getSignature(String keyAlgorithm,431Key key) throws NoSuchAlgorithmException, InvalidKeyException {432Signature signer;433switch (keyAlgorithm) {434case "EC":435signer = Signature.getInstance(JsseJce.SIGNATURE_ECDSA);436break;437case "EdDSA":438signer = Signature.getInstance(JsseJce.SIGNATURE_EDDSA);439break;440case "RSA":441signer = RSASignature.getInstance();442break;443default:444throw new NoSuchAlgorithmException(445"neither an RSA or a EC key : " + keyAlgorithm);446}447448if (signer != null) {449if (key instanceof PublicKey) {450signer.initVerify((PublicKey)(key));451} else {452signer.initSign((PrivateKey)key);453}454}455456return signer;457}458459private static void updateSignature(Signature sig,460byte[] clntNonce, byte[] svrNonce, int namedGroupId,461byte[] publicPoint) throws SignatureException {462sig.update(clntNonce);463sig.update(svrNonce);464465sig.update(CURVE_NAMED_CURVE);466sig.update((byte)((namedGroupId >> 8) & 0xFF));467sig.update((byte)(namedGroupId & 0xFF));468sig.update((byte)publicPoint.length);469sig.update(publicPoint);470}471}472473/**474* The ECDH "ServerKeyExchange" handshake message producer.475*/476private static final477class ECDHServerKeyExchangeProducer implements HandshakeProducer {478// Prevent instantiation of this class.479private ECDHServerKeyExchangeProducer() {480// blank481}482483@Override484public byte[] produce(ConnectionContext context,485HandshakeMessage message) throws IOException {486// The producing happens in server side only.487ServerHandshakeContext shc = (ServerHandshakeContext)context;488ECDHServerKeyExchangeMessage skem =489new ECDHServerKeyExchangeMessage(shc);490if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {491SSLLogger.fine(492"Produced ECDH ServerKeyExchange handshake message", skem);493}494495// Output the handshake message.496skem.write(shc.handshakeOutput);497shc.handshakeOutput.flush();498499// The handshake message has been delivered.500return null;501}502}503504/**505* The ECDH "ServerKeyExchange" handshake message consumer.506*/507private static final508class ECDHServerKeyExchangeConsumer implements SSLConsumer {509// Prevent instantiation of this class.510private ECDHServerKeyExchangeConsumer() {511// blank512}513514@Override515public void consume(ConnectionContext context,516ByteBuffer message) throws IOException {517// The consuming happens in client side only.518ClientHandshakeContext chc = (ClientHandshakeContext)context;519520// AlgorithmConstraints are checked during decoding521ECDHServerKeyExchangeMessage skem =522new ECDHServerKeyExchangeMessage(chc, message);523if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {524SSLLogger.fine(525"Consuming ECDH ServerKeyExchange handshake message", skem);526}527528//529// update530//531chc.handshakeCredentials.add(skem.sslCredentials);532533//534// produce535//536// Need no new handshake message producers here.537}538}539}540541542543