Path: blob/master/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java
41159 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.GeneralSecurityException;30import java.security.InvalidAlgorithmParameterException;31import java.security.InvalidKeyException;32import java.security.Key;33import java.security.NoSuchAlgorithmException;34import java.security.PrivateKey;35import java.security.PublicKey;36import java.security.Signature;37import java.security.SignatureException;38import java.text.MessageFormat;39import java.util.Locale;40import java.util.Map;41import sun.security.ssl.SSLHandshake.HandshakeMessage;42import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;43import sun.security.ssl.X509Authentication.X509Credentials;44import sun.security.ssl.X509Authentication.X509Possession;45import sun.security.util.HexDumpEncoder;4647/**48* Pack of the ServerKeyExchange handshake message.49*/50final class ECDHServerKeyExchange {51static final SSLConsumer ecdheHandshakeConsumer =52new ECDHServerKeyExchangeConsumer();53static final HandshakeProducer ecdheHandshakeProducer =54new ECDHServerKeyExchangeProducer();5556/**57* The ECDH ServerKeyExchange handshake message.58*/59private static final60class ECDHServerKeyExchangeMessage extends HandshakeMessage {61private static final byte CURVE_NAMED_CURVE = (byte)0x03;6263// id of the named curve64private final NamedGroup namedGroup;6566// encoded public point67private final byte[] publicPoint;6869// signature bytes, or null if anonymous70private final byte[] paramsSignature;7172private final boolean useExplicitSigAlgorithm;7374// the signature algorithm used by this ServerKeyExchange message75private final SignatureScheme signatureScheme;7677// the parsed credential object78private SSLCredentials sslCredentials;7980ECDHServerKeyExchangeMessage(81HandshakeContext handshakeContext) throws IOException {82super(handshakeContext);8384// This happens in server side only.85ServerHandshakeContext shc =86(ServerHandshakeContext)handshakeContext;8788// Find the Possessions needed89NamedGroupPossession namedGroupPossession = null;90X509Possession x509Possession = null;91for (SSLPossession possession : shc.handshakePossessions) {92if (possession instanceof NamedGroupPossession) {93namedGroupPossession = (NamedGroupPossession)possession;94if (x509Possession != null) {95break;96}97} else if (possession instanceof X509Possession) {98x509Possession = (X509Possession)possession;99if (namedGroupPossession != null) {100break;101}102}103}104105if (namedGroupPossession == null) {106// unlikely107throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,108"No ECDHE credentials negotiated for server key exchange");109}110111// Find the NamedGroup used for the ephemeral keys.112namedGroup = namedGroupPossession.getNamedGroup();113if ((namedGroup == null) || (!namedGroup.isAvailable)) {114// unlikely115throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,116"Missing or improper named group: " + namedGroup);117}118119publicPoint = namedGroup.encodePossessionPublicKey(120namedGroupPossession);121if (publicPoint == null) {122// unlikely123throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,124"Missing public point for named group: " + namedGroup);125}126127if (x509Possession == null) {128// anonymous, no authentication, no signature129paramsSignature = null;130signatureScheme = null;131useExplicitSigAlgorithm = false;132} else {133useExplicitSigAlgorithm =134shc.negotiatedProtocol.useTLS12PlusSpec();135Signature signer;136if (useExplicitSigAlgorithm) {137Map.Entry<SignatureScheme, Signature> schemeAndSigner =138SignatureScheme.getSignerOfPreferableAlgorithm(139shc.algorithmConstraints,140shc.peerRequestedSignatureSchemes,141x509Possession,142shc.negotiatedProtocol);143if (schemeAndSigner == null) {144// Unlikely, the credentials generator should have145// selected the preferable signature algorithm properly.146throw shc.conContext.fatal(Alert.INTERNAL_ERROR,147"No supported signature algorithm for " +148x509Possession.popPrivateKey.getAlgorithm() +149" key");150} else {151signatureScheme = schemeAndSigner.getKey();152signer = schemeAndSigner.getValue();153}154} else {155signatureScheme = null;156try {157signer = getSignature(158x509Possession.popPrivateKey.getAlgorithm(),159x509Possession.popPrivateKey);160} catch (NoSuchAlgorithmException | InvalidKeyException e) {161throw shc.conContext.fatal(Alert.INTERNAL_ERROR,162"Unsupported signature algorithm: " +163x509Possession.popPrivateKey.getAlgorithm(), e);164}165}166167byte[] signature;168try {169updateSignature(signer, shc.clientHelloRandom.randomBytes,170shc.serverHelloRandom.randomBytes,171namedGroup.id, publicPoint);172signature = signer.sign();173} catch (SignatureException ex) {174throw shc.conContext.fatal(Alert.INTERNAL_ERROR,175"Failed to sign ecdhe parameters: " +176x509Possession.popPrivateKey.getAlgorithm(), ex);177}178paramsSignature = signature;179}180}181182ECDHServerKeyExchangeMessage(HandshakeContext handshakeContext,183ByteBuffer m) throws IOException {184super(handshakeContext);185186// This happens in client side only.187ClientHandshakeContext chc =188(ClientHandshakeContext)handshakeContext;189190byte curveType = (byte)Record.getInt8(m);191if (curveType != CURVE_NAMED_CURVE) {192// Unlikely as only the named curves should be negotiated.193throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,194"Unsupported ECCurveType: " + curveType);195}196197int namedGroupId = Record.getInt16(m);198this.namedGroup = NamedGroup.valueOf(namedGroupId);199if (namedGroup == null) {200throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,201"Unknown named group ID: " + namedGroupId);202}203204if (!SupportedGroups.isSupported(namedGroup)) {205throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,206"Unsupported named group: " + namedGroup);207}208209publicPoint = Record.getBytes8(m);210if (publicPoint.length == 0) {211throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,212"Insufficient Point data: " + namedGroup);213}214215try {216sslCredentials = namedGroup.decodeCredentials(217publicPoint, handshakeContext.algorithmConstraints,218s -> chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,219"ServerKeyExchange " + namedGroup + ": " + (s)));220} catch (GeneralSecurityException ex) {221throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,222"Cannot decode named group: " +223NamedGroup.nameOf(namedGroupId));224}225226X509Credentials x509Credentials = null;227for (SSLCredentials cd : chc.handshakeCredentials) {228if (cd instanceof X509Credentials) {229x509Credentials = (X509Credentials)cd;230break;231}232}233234if (x509Credentials == null) {235// anonymous, no authentication, no signature236if (m.hasRemaining()) {237throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,238"Invalid DH ServerKeyExchange: unknown extra data");239}240this.signatureScheme = null;241this.paramsSignature = null;242this.useExplicitSigAlgorithm = false;243244return;245}246247this.useExplicitSigAlgorithm =248chc.negotiatedProtocol.useTLS12PlusSpec();249if (useExplicitSigAlgorithm) {250int ssid = Record.getInt16(m);251signatureScheme = SignatureScheme.valueOf(ssid);252if (signatureScheme == null) {253throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,254"Invalid signature algorithm (" + ssid +255") used in ECDH ServerKeyExchange handshake message");256}257258if (!chc.localSupportedSignAlgs.contains(signatureScheme)) {259throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,260"Unsupported signature algorithm (" +261signatureScheme.name +262") used in ECDH ServerKeyExchange handshake message");263}264} else {265signatureScheme = null;266}267268// read and verify the signature269paramsSignature = Record.getBytes16(m);270Signature signer;271if (useExplicitSigAlgorithm) {272try {273signer = signatureScheme.getVerifier(274x509Credentials.popPublicKey);275} catch (NoSuchAlgorithmException | InvalidKeyException |276InvalidAlgorithmParameterException nsae) {277throw chc.conContext.fatal(Alert.INTERNAL_ERROR,278"Unsupported signature algorithm: " +279signatureScheme.name, nsae);280}281} else {282try {283signer = getSignature(284x509Credentials.popPublicKey.getAlgorithm(),285x509Credentials.popPublicKey);286} catch (NoSuchAlgorithmException | InvalidKeyException e) {287throw chc.conContext.fatal(Alert.INTERNAL_ERROR,288"Unsupported signature algorithm: " +289x509Credentials.popPublicKey.getAlgorithm(), e);290}291}292293try {294updateSignature(signer,295chc.clientHelloRandom.randomBytes,296chc.serverHelloRandom.randomBytes,297namedGroup.id, publicPoint);298299if (!signer.verify(paramsSignature)) {300throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,301"Invalid ECDH ServerKeyExchange signature");302}303} catch (SignatureException ex) {304throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,305"Cannot verify ECDH ServerKeyExchange signature", ex);306}307}308309@Override310public SSLHandshake handshakeType() {311return SSLHandshake.SERVER_KEY_EXCHANGE;312}313314@Override315public int messageLength() {316int sigLen = 0;317if (paramsSignature != null) {318sigLen = 2 + paramsSignature.length;319if (useExplicitSigAlgorithm) {320sigLen += SignatureScheme.sizeInRecord();321}322}323324return 4 + publicPoint.length + sigLen;325}326327@Override328public void send(HandshakeOutStream hos) throws IOException {329hos.putInt8(CURVE_NAMED_CURVE);330hos.putInt16(namedGroup.id);331hos.putBytes8(publicPoint);332if (paramsSignature != null) {333if (useExplicitSigAlgorithm) {334hos.putInt16(signatureScheme.id);335}336337hos.putBytes16(paramsSignature);338}339}340341@Override342public String toString() {343if (useExplicitSigAlgorithm) {344MessageFormat messageFormat = new MessageFormat(345"\"ECDH ServerKeyExchange\": '{'\n" +346" \"parameters\": '{'\n" +347" \"named group\": \"{0}\"\n" +348" \"ecdh public\": '{'\n" +349"{1}\n" +350" '}',\n" +351" '}',\n" +352" \"digital signature\": '{'\n" +353" \"signature algorithm\": \"{2}\"\n" +354" \"signature\": '{'\n" +355"{3}\n" +356" '}',\n" +357" '}'\n" +358"'}'",359Locale.ENGLISH);360361HexDumpEncoder hexEncoder = new HexDumpEncoder();362Object[] messageFields = {363namedGroup.name,364Utilities.indent(365hexEncoder.encodeBuffer(publicPoint), " "),366signatureScheme.name,367Utilities.indent(368hexEncoder.encodeBuffer(paramsSignature), " ")369};370return messageFormat.format(messageFields);371} else if (paramsSignature != null) {372MessageFormat messageFormat = new MessageFormat(373"\"ECDH ServerKeyExchange\": '{'\n" +374" \"parameters\": '{'\n" +375" \"named group\": \"{0}\"\n" +376" \"ecdh public\": '{'\n" +377"{1}\n" +378" '}',\n" +379" '}',\n" +380" \"signature\": '{'\n" +381"{2}\n" +382" '}'\n" +383"'}'",384Locale.ENGLISH);385386HexDumpEncoder hexEncoder = new HexDumpEncoder();387Object[] messageFields = {388namedGroup.name,389Utilities.indent(390hexEncoder.encodeBuffer(publicPoint), " "),391Utilities.indent(392hexEncoder.encodeBuffer(paramsSignature), " ")393};394395return messageFormat.format(messageFields);396} else { // anonymous397MessageFormat messageFormat = new MessageFormat(398"\"ECDH ServerKeyExchange\": '{'\n" +399" \"parameters\": '{'\n" +400" \"named group\": \"{0}\"\n" +401" \"ecdh public\": '{'\n" +402"{1}\n" +403" '}',\n" +404" '}'\n" +405"'}'",406Locale.ENGLISH);407408HexDumpEncoder hexEncoder = new HexDumpEncoder();409Object[] messageFields = {410namedGroup.name,411Utilities.indent(412hexEncoder.encodeBuffer(publicPoint), " "),413};414415return messageFormat.format(messageFields);416}417}418419private static Signature getSignature(String keyAlgorithm,420Key key) throws NoSuchAlgorithmException, InvalidKeyException {421Signature signer;422switch (keyAlgorithm) {423case "EC":424signer = Signature.getInstance(JsseJce.SIGNATURE_ECDSA);425break;426case "EdDSA":427signer = Signature.getInstance(JsseJce.SIGNATURE_EDDSA);428break;429case "RSA":430signer = RSASignature.getInstance();431break;432default:433throw new NoSuchAlgorithmException(434"neither an RSA or a EC key : " + keyAlgorithm);435}436437if (signer != null) {438if (key instanceof PublicKey) {439signer.initVerify((PublicKey)(key));440} else {441signer.initSign((PrivateKey)key);442}443}444445return signer;446}447448private static void updateSignature(Signature sig,449byte[] clntNonce, byte[] svrNonce, int namedGroupId,450byte[] publicPoint) throws SignatureException {451sig.update(clntNonce);452sig.update(svrNonce);453454sig.update(CURVE_NAMED_CURVE);455sig.update((byte)((namedGroupId >> 8) & 0xFF));456sig.update((byte)(namedGroupId & 0xFF));457sig.update((byte)publicPoint.length);458sig.update(publicPoint);459}460}461462/**463* The ECDH "ServerKeyExchange" handshake message producer.464*/465private static final466class ECDHServerKeyExchangeProducer implements HandshakeProducer {467// Prevent instantiation of this class.468private ECDHServerKeyExchangeProducer() {469// blank470}471472@Override473public byte[] produce(ConnectionContext context,474HandshakeMessage message) throws IOException {475// The producing happens in server side only.476ServerHandshakeContext shc = (ServerHandshakeContext)context;477ECDHServerKeyExchangeMessage skem =478new ECDHServerKeyExchangeMessage(shc);479if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {480SSLLogger.fine(481"Produced ECDH ServerKeyExchange handshake message", skem);482}483484// Output the handshake message.485skem.write(shc.handshakeOutput);486shc.handshakeOutput.flush();487488// The handshake message has been delivered.489return null;490}491}492493/**494* The ECDH "ServerKeyExchange" handshake message consumer.495*/496private static final497class ECDHServerKeyExchangeConsumer implements SSLConsumer {498// Prevent instantiation of this class.499private ECDHServerKeyExchangeConsumer() {500// blank501}502503@Override504public void consume(ConnectionContext context,505ByteBuffer message) throws IOException {506// The consuming happens in client side only.507ClientHandshakeContext chc = (ClientHandshakeContext)context;508509// AlgorithmConstraints are checked during decoding510ECDHServerKeyExchangeMessage skem =511new ECDHServerKeyExchangeMessage(chc, message);512if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {513SSLLogger.fine(514"Consuming ECDH ServerKeyExchange handshake message", skem);515}516517//518// update519//520chc.handshakeCredentials.add(skem.sslCredentials);521522//523// produce524//525// Need no new handshake message producers here.526}527}528}529530531532