Path: blob/master/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.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.math.BigInteger;29import java.nio.ByteBuffer;30import java.security.CryptoPrimitive;31import java.security.GeneralSecurityException;32import java.security.KeyFactory;33import java.text.MessageFormat;34import java.util.EnumSet;35import java.util.Locale;36import javax.crypto.SecretKey;37import javax.crypto.interfaces.DHPublicKey;38import javax.crypto.spec.DHParameterSpec;39import javax.crypto.spec.DHPublicKeySpec;40import javax.net.ssl.SSLHandshakeException;41import sun.security.ssl.DHKeyExchange.DHECredentials;42import sun.security.ssl.DHKeyExchange.DHEPossession;43import sun.security.ssl.SSLHandshake.HandshakeMessage;44import sun.security.util.HexDumpEncoder;4546/**47* Pack of the "ClientKeyExchange" handshake message.48*/49final class DHClientKeyExchange {50static final DHClientKeyExchangeConsumer dhHandshakeConsumer =51new DHClientKeyExchangeConsumer();52static final DHClientKeyExchangeProducer dhHandshakeProducer =53new DHClientKeyExchangeProducer();5455/**56* The DiffieHellman ClientKeyExchange handshake message.57*58* If the client has sent a certificate which contains a suitable59* DiffieHellman key (for fixed_dh client authentication), then the60* client public value is implicit and does not need to be sent again.61* In this case, the client key exchange message will be sent, but it62* MUST be empty.63*64* Currently, we don't support cipher suite that requires implicit public65* key of client.66*/67private static final68class DHClientKeyExchangeMessage extends HandshakeMessage {69private final byte[] y; // 1 to 2^16 - 1 bytes7071DHClientKeyExchangeMessage(72HandshakeContext handshakeContext) throws IOException {73super(handshakeContext);74// This happens in client side only.75ClientHandshakeContext chc =76(ClientHandshakeContext)handshakeContext;7778DHEPossession dhePossession = null;79for (SSLPossession possession : chc.handshakePossessions) {80if (possession instanceof DHEPossession) {81dhePossession = (DHEPossession)possession;82break;83}84}8586if (dhePossession == null) {87// unlikely88throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,89"No DHE credentials negotiated for client key exchange");90}9192DHPublicKey publicKey = dhePossession.publicKey;93DHParameterSpec params = publicKey.getParams();94this.y = Utilities.toByteArray(publicKey.getY());95}9697DHClientKeyExchangeMessage(HandshakeContext handshakeContext,98ByteBuffer m) throws IOException {99super(handshakeContext);100// This happens in server side only.101ServerHandshakeContext shc =102(ServerHandshakeContext)handshakeContext;103104if (m.remaining() < 3) {105throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,106"Invalid DH ClientKeyExchange message: insufficient data");107}108109this.y = Record.getBytes16(m);110111if (m.hasRemaining()) {112throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,113"Invalid DH ClientKeyExchange message: unknown extra data");114}115}116117@Override118public SSLHandshake handshakeType() {119return SSLHandshake.CLIENT_KEY_EXCHANGE;120}121122@Override123public int messageLength() {124return y.length + 2; // 2: length filed125}126127@Override128public void send(HandshakeOutStream hos) throws IOException {129hos.putBytes16(y);130}131132@Override133public String toString() {134MessageFormat messageFormat = new MessageFormat(135"\"DH ClientKeyExchange\": '{'\n" +136" \"parameters\": '{'\n" +137" \"dh_Yc\": '{'\n" +138"{0}\n" +139" '}',\n" +140" '}'\n" +141"'}'",142Locale.ENGLISH);143144HexDumpEncoder hexEncoder = new HexDumpEncoder();145Object[] messageFields = {146Utilities.indent(147hexEncoder.encodeBuffer(y), " "),148};149return messageFormat.format(messageFields);150}151}152153/**154* The DiffieHellman "ClientKeyExchange" handshake message producer.155*/156private static final157class DHClientKeyExchangeProducer implements HandshakeProducer {158// Prevent instantiation of this class.159private DHClientKeyExchangeProducer() {160// blank161}162163@Override164public byte[] produce(ConnectionContext context,165HandshakeMessage message) throws IOException {166// The producing happens in client side only.167ClientHandshakeContext chc = (ClientHandshakeContext)context;168169DHECredentials dheCredentials = null;170for (SSLCredentials cd : chc.handshakeCredentials) {171if (cd instanceof DHECredentials) {172dheCredentials = (DHECredentials)cd;173break;174}175}176177if (dheCredentials == null) {178throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,179"No DHE credentials negotiated for client key exchange");180}181182183DHEPossession dhePossession = new DHEPossession(184dheCredentials, chc.sslContext.getSecureRandom());185chc.handshakePossessions.add(dhePossession);186DHClientKeyExchangeMessage ckem =187new DHClientKeyExchangeMessage(chc);188if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {189SSLLogger.fine(190"Produced DH ClientKeyExchange handshake message", ckem);191}192193// Output the handshake message.194ckem.write(chc.handshakeOutput);195chc.handshakeOutput.flush();196197// update the states198SSLKeyExchange ke = SSLKeyExchange.valueOf(199chc.negotiatedCipherSuite.keyExchange,200chc.negotiatedProtocol);201if (ke == null) {202// unlikely203throw chc.conContext.fatal(Alert.INTERNAL_ERROR,204"Not supported key exchange type");205} else {206SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);207SecretKey masterSecret =208masterKD.deriveKey("MasterSecret", null);209chc.handshakeSession.setMasterSecret(masterSecret);210211SSLTrafficKeyDerivation kd =212SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);213if (kd == null) {214// unlikely215throw chc.conContext.fatal(Alert.INTERNAL_ERROR,216"Not supported key derivation: " +217chc.negotiatedProtocol);218} else {219chc.handshakeKeyDerivation =220kd.createKeyDerivation(chc, masterSecret);221}222}223224// The handshake message has been delivered.225return null;226}227}228229/**230* The DiffieHellman "ClientKeyExchange" handshake message consumer.231*/232private static final233class DHClientKeyExchangeConsumer implements SSLConsumer {234// Prevent instantiation of this class.235private DHClientKeyExchangeConsumer() {236// blank237}238239@Override240public void consume(ConnectionContext context,241ByteBuffer message) throws IOException {242// The consuming happens in server side only.243ServerHandshakeContext shc = (ServerHandshakeContext)context;244245DHEPossession dhePossession = null;246for (SSLPossession possession : shc.handshakePossessions) {247if (possession instanceof DHEPossession) {248dhePossession = (DHEPossession)possession;249break;250}251}252253if (dhePossession == null) {254// unlikely255throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,256"No expected DHE possessions for client key exchange");257}258259SSLKeyExchange ke = SSLKeyExchange.valueOf(260shc.negotiatedCipherSuite.keyExchange,261shc.negotiatedProtocol);262if (ke == null) {263// unlikely264throw shc.conContext.fatal(Alert.INTERNAL_ERROR,265"Not supported key exchange type");266}267268DHClientKeyExchangeMessage ckem =269new DHClientKeyExchangeMessage(shc, message);270if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {271SSLLogger.fine(272"Consuming DH ClientKeyExchange handshake message", ckem);273}274275// create the credentials276try {277DHParameterSpec params = dhePossession.publicKey.getParams();278DHPublicKeySpec spec = new DHPublicKeySpec(279new BigInteger(1, ckem.y),280params.getP(), params.getG());281KeyFactory kf = KeyFactory.getInstance("DiffieHellman");282DHPublicKey peerPublicKey =283(DHPublicKey)kf.generatePublic(spec);284285// check constraints of peer DHPublicKey286if (!shc.algorithmConstraints.permits(287EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),288peerPublicKey)) {289throw new SSLHandshakeException(290"DHPublicKey does not comply to algorithm constraints");291}292293NamedGroup namedGroup = NamedGroup.valueOf(params);294shc.handshakeCredentials.add(295new DHECredentials(peerPublicKey, namedGroup));296} catch (GeneralSecurityException | java.io.IOException e) {297throw (SSLHandshakeException)(new SSLHandshakeException(298"Could not generate DHPublicKey").initCause(e));299}300301// update the states302SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);303SecretKey masterSecret =304masterKD.deriveKey("MasterSecret", null);305shc.handshakeSession.setMasterSecret(masterSecret);306307SSLTrafficKeyDerivation kd =308SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);309if (kd == null) {310// unlikely311throw shc.conContext.fatal(Alert.INTERNAL_ERROR,312"Not supported key derivation: " + shc.negotiatedProtocol);313} else {314shc.handshakeKeyDerivation =315kd.createKeyDerivation(shc, masterSecret);316}317}318}319}320321322