Path: blob/master/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.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.math.BigInteger;29import java.nio.ByteBuffer;30import java.security.CryptoPrimitive;31import java.security.GeneralSecurityException;32import java.security.InvalidAlgorithmParameterException;33import java.security.InvalidKeyException;34import java.security.Key;35import java.security.KeyFactory;36import java.security.NoSuchAlgorithmException;37import java.security.PrivateKey;38import java.security.PublicKey;39import java.security.Signature;40import java.security.SignatureException;41import java.text.MessageFormat;42import java.util.EnumSet;43import java.util.Locale;44import java.util.Map;45import javax.crypto.interfaces.DHPublicKey;46import javax.crypto.spec.DHParameterSpec;47import javax.crypto.spec.DHPublicKeySpec;48import sun.security.ssl.DHKeyExchange.DHECredentials;49import sun.security.ssl.DHKeyExchange.DHEPossession;50import sun.security.ssl.SSLHandshake.HandshakeMessage;51import sun.security.ssl.X509Authentication.X509Credentials;52import sun.security.ssl.X509Authentication.X509Possession;53import sun.security.util.HexDumpEncoder;54import sun.security.util.KeyUtil;5556/**57* Pack of the ServerKeyExchange handshake message.58*/59final class DHServerKeyExchange {60static final SSLConsumer dhHandshakeConsumer =61new DHServerKeyExchangeConsumer();62static final HandshakeProducer dhHandshakeProducer =63new DHServerKeyExchangeProducer();6465/**66* The DiffieHellman ServerKeyExchange handshake message.67*/68private static final69class DHServerKeyExchangeMessage extends HandshakeMessage {70// public key encapsulated in this message71private final byte[] p; // 1 to 2^16 - 1 bytes72private final byte[] g; // 1 to 2^16 - 1 bytes73private final byte[] y; // 1 to 2^16 - 1 bytes7475// the signature algorithm used by this ServerKeyExchange message76private final boolean useExplicitSigAlgorithm;77private final SignatureScheme signatureScheme;7879// signature bytes, or null if anonymous80private final byte[] paramsSignature;8182DHServerKeyExchangeMessage(83HandshakeContext handshakeContext) throws IOException {84super(handshakeContext);8586// This happens in server side only.87ServerHandshakeContext shc =88(ServerHandshakeContext)handshakeContext;8990DHEPossession dhePossession = null;91X509Possession x509Possession = null;92for (SSLPossession possession : shc.handshakePossessions) {93if (possession instanceof DHEPossession) {94dhePossession = (DHEPossession)possession;95if (x509Possession != null) {96break;97}98} else if (possession instanceof X509Possession) {99x509Possession = (X509Possession)possession;100if (dhePossession != null) {101break;102}103}104}105106if (dhePossession == null) {107// unlikely108throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,109"No DHE credentials negotiated for server key exchange");110}111DHPublicKey publicKey = dhePossession.publicKey;112DHParameterSpec params = publicKey.getParams();113this.p = Utilities.toByteArray(params.getP());114this.g = Utilities.toByteArray(params.getG());115this.y = Utilities.toByteArray(publicKey.getY());116117if (x509Possession == null) {118// anonymous, no authentication, no signature119paramsSignature = null;120signatureScheme = null;121useExplicitSigAlgorithm = false;122} else {123useExplicitSigAlgorithm =124shc.negotiatedProtocol.useTLS12PlusSpec();125Signature signer;126if (useExplicitSigAlgorithm) {127Map.Entry<SignatureScheme, Signature> schemeAndSigner =128SignatureScheme.getSignerOfPreferableAlgorithm(129shc.algorithmConstraints,130shc.peerRequestedSignatureSchemes,131x509Possession,132shc.negotiatedProtocol);133if (schemeAndSigner == null) {134// Unlikely, the credentials generator should have135// selected the preferable signature algorithm properly.136throw shc.conContext.fatal(Alert.INTERNAL_ERROR,137"No supported signature algorithm for " +138x509Possession.popPrivateKey.getAlgorithm() +139" key");140} else {141signatureScheme = schemeAndSigner.getKey();142signer = schemeAndSigner.getValue();143}144} else {145signatureScheme = null;146try {147signer = getSignature(148x509Possession.popPrivateKey.getAlgorithm(),149x509Possession.popPrivateKey);150} catch (NoSuchAlgorithmException | InvalidKeyException e) {151throw shc.conContext.fatal(Alert.INTERNAL_ERROR,152"Unsupported signature algorithm: " +153x509Possession.popPrivateKey.getAlgorithm(), e);154}155}156157byte[] signature;158try {159updateSignature(signer, shc.clientHelloRandom.randomBytes,160shc.serverHelloRandom.randomBytes);161signature = signer.sign();162} catch (SignatureException ex) {163throw shc.conContext.fatal(Alert.INTERNAL_ERROR,164"Failed to sign dhe parameters: " +165x509Possession.popPrivateKey.getAlgorithm(), ex);166}167paramsSignature = signature;168}169}170171DHServerKeyExchangeMessage(HandshakeContext handshakeContext,172ByteBuffer m) throws IOException {173super(handshakeContext);174175// This happens in client side only.176ClientHandshakeContext chc =177(ClientHandshakeContext)handshakeContext;178179this.p = Record.getBytes16(m);180this.g = Record.getBytes16(m);181this.y = Record.getBytes16(m);182183try {184KeyUtil.validate(new DHPublicKeySpec(185new BigInteger(1, y),186new BigInteger(1, p),187new BigInteger(1, p)));188} catch (InvalidKeyException ike) {189throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,190"Invalid DH ServerKeyExchange: invalid parameters", ike);191}192193X509Credentials x509Credentials = null;194for (SSLCredentials cd : chc.handshakeCredentials) {195if (cd instanceof X509Credentials) {196x509Credentials = (X509Credentials)cd;197break;198}199}200201if (x509Credentials == null) {202// anonymous, no authentication, no signature203if (m.hasRemaining()) {204throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,205"Invalid DH ServerKeyExchange: unknown extra data");206}207208this.signatureScheme = null;209this.paramsSignature = null;210this.useExplicitSigAlgorithm = false;211212return;213}214215this.useExplicitSigAlgorithm =216chc.negotiatedProtocol.useTLS12PlusSpec();217if (useExplicitSigAlgorithm) {218int ssid = Record.getInt16(m);219signatureScheme = SignatureScheme.valueOf(ssid);220if (signatureScheme == null) {221throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,222"Invalid signature algorithm (" + ssid +223") used in DH ServerKeyExchange handshake message");224}225226if (!chc.localSupportedSignAlgs.contains(signatureScheme)) {227throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,228"Unsupported signature algorithm (" +229signatureScheme.name +230") used in DH ServerKeyExchange handshake message");231}232} else {233this.signatureScheme = null;234}235236// read and verify the signature237this.paramsSignature = Record.getBytes16(m);238Signature signer;239if (useExplicitSigAlgorithm) {240try {241signer = signatureScheme.getVerifier(242x509Credentials.popPublicKey);243} catch (NoSuchAlgorithmException | InvalidKeyException |244InvalidAlgorithmParameterException nsae) {245throw chc.conContext.fatal(Alert.INTERNAL_ERROR,246"Unsupported signature algorithm: " +247signatureScheme.name, nsae);248}249} else {250try {251signer = getSignature(252x509Credentials.popPublicKey.getAlgorithm(),253x509Credentials.popPublicKey);254} catch (NoSuchAlgorithmException | InvalidKeyException e) {255throw chc.conContext.fatal(Alert.INTERNAL_ERROR,256"Unsupported signature algorithm: " +257x509Credentials.popPublicKey.getAlgorithm(), e);258}259}260261try {262updateSignature(signer,263chc.clientHelloRandom.randomBytes,264chc.serverHelloRandom.randomBytes);265266if (!signer.verify(paramsSignature)) {267throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,268"Invalid signature on DH ServerKeyExchange message");269}270} catch (SignatureException ex) {271throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,272"Cannot verify DH ServerKeyExchange signature", ex);273}274}275276@Override277public SSLHandshake handshakeType() {278return SSLHandshake.SERVER_KEY_EXCHANGE;279}280281@Override282public int messageLength() {283int sigLen = 0;284if (paramsSignature != null) {285sigLen = 2 + paramsSignature.length;286if (useExplicitSigAlgorithm) {287sigLen += SignatureScheme.sizeInRecord();288}289}290291return 6 + p.length + g.length + y.length + sigLen;292// 6: overhead for p, g, y values293}294295@Override296public void send(HandshakeOutStream hos) throws IOException {297hos.putBytes16(p);298hos.putBytes16(g);299hos.putBytes16(y);300301if (paramsSignature != null) {302if (useExplicitSigAlgorithm) {303hos.putInt16(signatureScheme.id);304}305306hos.putBytes16(paramsSignature);307}308}309310@Override311public String toString() {312if (paramsSignature == null) { // anonymous313MessageFormat messageFormat = new MessageFormat(314"\"DH ServerKeyExchange\": '{'\n" +315" \"parameters\": '{'\n" +316" \"dh_p\": '{'\n" +317"{0}\n" +318" '}',\n" +319" \"dh_g\": '{'\n" +320"{1}\n" +321" '}',\n" +322" \"dh_Ys\": '{'\n" +323"{2}\n" +324" '}',\n" +325" '}'\n" +326"'}'",327Locale.ENGLISH);328329HexDumpEncoder hexEncoder = new HexDumpEncoder();330Object[] messageFields = {331Utilities.indent(332hexEncoder.encodeBuffer(p), " "),333Utilities.indent(334hexEncoder.encodeBuffer(g), " "),335Utilities.indent(336hexEncoder.encodeBuffer(y), " "),337};338339return messageFormat.format(messageFields);340}341342if (useExplicitSigAlgorithm) {343MessageFormat messageFormat = new MessageFormat(344"\"DH ServerKeyExchange\": '{'\n" +345" \"parameters\": '{'\n" +346" \"dh_p\": '{'\n" +347"{0}\n" +348" '}',\n" +349" \"dh_g\": '{'\n" +350"{1}\n" +351" '}',\n" +352" \"dh_Ys\": '{'\n" +353"{2}\n" +354" '}',\n" +355" '}',\n" +356" \"digital signature\": '{'\n" +357" \"signature algorithm\": \"{3}\"\n" +358" \"signature\": '{'\n" +359"{4}\n" +360" '}',\n" +361" '}'\n" +362"'}'",363Locale.ENGLISH);364365HexDumpEncoder hexEncoder = new HexDumpEncoder();366Object[] messageFields = {367Utilities.indent(368hexEncoder.encodeBuffer(p), " "),369Utilities.indent(370hexEncoder.encodeBuffer(g), " "),371Utilities.indent(372hexEncoder.encodeBuffer(y), " "),373signatureScheme.name,374Utilities.indent(375hexEncoder.encodeBuffer(paramsSignature), " ")376};377378return messageFormat.format(messageFields);379} else {380MessageFormat messageFormat = new MessageFormat(381"\"DH ServerKeyExchange\": '{'\n" +382" \"parameters\": '{'\n" +383" \"dh_p\": '{'\n" +384"{0}\n" +385" '}',\n" +386" \"dh_g\": '{'\n" +387"{1}\n" +388" '}',\n" +389" \"dh_Ys\": '{'\n" +390"{2}\n" +391" '}',\n" +392" '}',\n" +393" \"signature\": '{'\n" +394"{3}\n" +395" '}'\n" +396"'}'",397Locale.ENGLISH);398399HexDumpEncoder hexEncoder = new HexDumpEncoder();400Object[] messageFields = {401Utilities.indent(402hexEncoder.encodeBuffer(p), " "),403Utilities.indent(404hexEncoder.encodeBuffer(g), " "),405Utilities.indent(406hexEncoder.encodeBuffer(y), " "),407Utilities.indent(408hexEncoder.encodeBuffer(paramsSignature), " ")409};410411return messageFormat.format(messageFields);412}413}414415private static Signature getSignature(String keyAlgorithm,416Key key) throws NoSuchAlgorithmException, InvalidKeyException {417Signature signer;418switch (keyAlgorithm) {419case "DSA":420signer = Signature.getInstance(JsseJce.SIGNATURE_DSA);421break;422case "RSA":423signer = RSASignature.getInstance();424break;425default:426throw new NoSuchAlgorithmException(427"neither an RSA or a DSA key : " + keyAlgorithm);428}429430if (signer != null) {431if (key instanceof PublicKey) {432signer.initVerify((PublicKey)(key));433} else {434signer.initSign((PrivateKey)key);435}436}437438return signer;439}440441/*442* Update sig with nonces and Diffie-Hellman public key.443*/444private void updateSignature(Signature sig, byte[] clntNonce,445byte[] svrNonce) throws SignatureException {446int tmp;447448sig.update(clntNonce);449sig.update(svrNonce);450451sig.update((byte)(p.length >> 8));452sig.update((byte)(p.length & 0x0ff));453sig.update(p);454455sig.update((byte)(g.length >> 8));456sig.update((byte)(g.length & 0x0ff));457sig.update(g);458459sig.update((byte)(y.length >> 8));460sig.update((byte)(y.length & 0x0ff));461sig.update(y);462}463}464465/**466* The DiffieHellman "ServerKeyExchange" handshake message producer.467*/468static final class DHServerKeyExchangeProducer469implements HandshakeProducer {470// Prevent instantiation of this class.471private DHServerKeyExchangeProducer() {472// blank473}474475@Override476public byte[] produce(ConnectionContext context,477HandshakeMessage message) throws IOException {478// The producing happens in server side only.479ServerHandshakeContext shc = (ServerHandshakeContext)context;480DHServerKeyExchangeMessage skem =481new DHServerKeyExchangeMessage(shc);482if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {483SSLLogger.fine(484"Produced DH ServerKeyExchange handshake message", skem);485}486487// Output the handshake message.488skem.write(shc.handshakeOutput);489shc.handshakeOutput.flush();490491// The handshake message has been delivered.492return null;493}494}495496/**497* The DiffieHellman "ServerKeyExchange" handshake message consumer.498*/499static final class DHServerKeyExchangeConsumer implements SSLConsumer {500// Prevent instantiation of this class.501private DHServerKeyExchangeConsumer() {502// blank503}504505@Override506public void consume(ConnectionContext context,507ByteBuffer message) throws IOException {508// The consuming happens in client side only.509ClientHandshakeContext chc = (ClientHandshakeContext)context;510511DHServerKeyExchangeMessage skem =512new DHServerKeyExchangeMessage(chc, message);513if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {514SSLLogger.fine(515"Consuming DH ServerKeyExchange handshake message", skem);516}517518//519// validate520//521// check constraints of EC PublicKey522DHPublicKey publicKey;523try {524KeyFactory kf = KeyFactory.getInstance("DiffieHellman");525DHPublicKeySpec spec = new DHPublicKeySpec(526new BigInteger(1, skem.y),527new BigInteger(1, skem.p),528new BigInteger(1, skem.g));529publicKey = (DHPublicKey)kf.generatePublic(spec);530} catch (GeneralSecurityException gse) {531throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,532"Could not generate DHPublicKey", gse);533}534535if (!chc.algorithmConstraints.permits(536EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) {537throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,538"DH ServerKeyExchange does not comply to " +539"algorithm constraints");540}541542//543// update544//545NamedGroup namedGroup = NamedGroup.valueOf(publicKey.getParams());546chc.handshakeCredentials.add(547new DHECredentials(publicKey, namedGroup));548549//550// produce551//552// Need no new handshake message producers here.553}554}555}556557558559