1 /* 2 * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.ssl; 27 28 import java.io.IOException; 29 import java.math.BigInteger; 30 import java.nio.ByteBuffer; 31 import java.security.CryptoPrimitive; 32 import java.security.GeneralSecurityException; 33 import java.security.KeyFactory; 34 import java.text.MessageFormat; 35 import java.util.EnumSet; 36 import java.util.Locale; 37 import javax.crypto.SecretKey; 38 import javax.crypto.interfaces.DHPublicKey; 39 import javax.crypto.spec.DHParameterSpec; 40 import javax.crypto.spec.DHPublicKeySpec; 41 import javax.net.ssl.SSLHandshakeException; 42 import sun.security.ssl.DHKeyExchange.DHECredentials; 43 import sun.security.ssl.DHKeyExchange.DHEPossession; 44 import sun.security.ssl.SSLHandshake.HandshakeMessage; 45 import sun.security.util.HexDumpEncoder; 46 47 /** 48 * Pack of the "ClientKeyExchange" handshake message. 49 */ 50 final class DHClientKeyExchange { 51 static final DHClientKeyExchangeConsumer dhHandshakeConsumer = 52 new DHClientKeyExchangeConsumer(); 53 static final DHClientKeyExchangeProducer dhHandshakeProducer = 54 new DHClientKeyExchangeProducer(); 55 56 /** 57 * The DiffieHellman ClientKeyExchange handshake message. 58 * 59 * If the client has sent a certificate which contains a suitable 60 * DiffieHellman key (for fixed_dh client authentication), then the 61 * client public value is implicit and does not need to be sent again. 62 * In this case, the client key exchange message will be sent, but it 63 * MUST be empty. 64 * 65 * Currently, we don't support cipher suite that requires implicit public 66 * key of client. 67 */ 68 private static final 69 class DHClientKeyExchangeMessage extends HandshakeMessage { 70 private byte[] y; // 1 to 2^16 - 1 bytes 71 DHClientKeyExchangeMessage( HandshakeContext handshakeContext)72 DHClientKeyExchangeMessage( 73 HandshakeContext handshakeContext) throws IOException { 74 super(handshakeContext); 75 // This happens in client side only. 76 ClientHandshakeContext chc = 77 (ClientHandshakeContext)handshakeContext; 78 79 DHEPossession dhePossession = null; 80 for (SSLPossession possession : chc.handshakePossessions) { 81 if (possession instanceof DHEPossession) { 82 dhePossession = (DHEPossession)possession; 83 break; 84 } 85 } 86 87 if (dhePossession == null) { 88 // unlikely 89 throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 90 "No DHE credentials negotiated for client key exchange"); 91 } 92 93 DHPublicKey publicKey = dhePossession.publicKey; 94 DHParameterSpec params = publicKey.getParams(); 95 this.y = Utilities.toByteArray(publicKey.getY()); 96 } 97 DHClientKeyExchangeMessage(HandshakeContext handshakeContext, ByteBuffer m)98 DHClientKeyExchangeMessage(HandshakeContext handshakeContext, 99 ByteBuffer m) throws IOException { 100 super(handshakeContext); 101 // This happens in server side only. 102 ServerHandshakeContext shc = 103 (ServerHandshakeContext)handshakeContext; 104 105 if (m.remaining() < 3) { 106 throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 107 "Invalid DH ClientKeyExchange message: insufficient data"); 108 } 109 110 this.y = Record.getBytes16(m); 111 112 if (m.hasRemaining()) { 113 throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 114 "Invalid DH ClientKeyExchange message: unknown extra data"); 115 } 116 } 117 118 @Override handshakeType()119 public SSLHandshake handshakeType() { 120 return SSLHandshake.CLIENT_KEY_EXCHANGE; 121 } 122 123 @Override messageLength()124 public int messageLength() { 125 return y.length + 2; // 2: length filed 126 } 127 128 @Override send(HandshakeOutStream hos)129 public void send(HandshakeOutStream hos) throws IOException { 130 hos.putBytes16(y); 131 } 132 133 @Override toString()134 public String toString() { 135 MessageFormat messageFormat = new MessageFormat( 136 "\"DH ClientKeyExchange\": '{'\n" + 137 " \"parameters\": '{'\n" + 138 " \"dh_Yc\": '{'\n" + 139 "{0}\n" + 140 " '}',\n" + 141 " '}'\n" + 142 "'}'", 143 Locale.ENGLISH); 144 145 HexDumpEncoder hexEncoder = new HexDumpEncoder(); 146 Object[] messageFields = { 147 Utilities.indent( 148 hexEncoder.encodeBuffer(y), " "), 149 }; 150 return messageFormat.format(messageFields); 151 } 152 } 153 154 /** 155 * The DiffieHellman "ClientKeyExchange" handshake message producer. 156 */ 157 private static final 158 class DHClientKeyExchangeProducer implements HandshakeProducer { 159 // Prevent instantiation of this class. DHClientKeyExchangeProducer()160 private DHClientKeyExchangeProducer() { 161 // blank 162 } 163 164 @Override produce(ConnectionContext context, HandshakeMessage message)165 public byte[] produce(ConnectionContext context, 166 HandshakeMessage message) throws IOException { 167 // The producing happens in client side only. 168 ClientHandshakeContext chc = (ClientHandshakeContext)context; 169 170 DHECredentials dheCredentials = null; 171 for (SSLCredentials cd : chc.handshakeCredentials) { 172 if (cd instanceof DHECredentials) { 173 dheCredentials = (DHECredentials)cd; 174 break; 175 } 176 } 177 178 if (dheCredentials == null) { 179 throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 180 "No DHE credentials negotiated for client key exchange"); 181 } 182 183 184 DHEPossession dhePossession = new DHEPossession( 185 dheCredentials, chc.sslContext.getSecureRandom()); 186 chc.handshakePossessions.add(dhePossession); 187 DHClientKeyExchangeMessage ckem = 188 new DHClientKeyExchangeMessage(chc); 189 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 190 SSLLogger.fine( 191 "Produced DH ClientKeyExchange handshake message", ckem); 192 } 193 194 // Output the handshake message. 195 ckem.write(chc.handshakeOutput); 196 chc.handshakeOutput.flush(); 197 198 // update the states 199 SSLKeyExchange ke = SSLKeyExchange.valueOf( 200 chc.negotiatedCipherSuite.keyExchange, 201 chc.negotiatedProtocol); 202 if (ke == null) { 203 // unlikely 204 throw chc.conContext.fatal(Alert.INTERNAL_ERROR, 205 "Not supported key exchange type"); 206 } else { 207 SSLKeyDerivation masterKD = ke.createKeyDerivation(chc); 208 SecretKey masterSecret = 209 masterKD.deriveKey("MasterSecret", null); 210 chc.handshakeSession.setMasterSecret(masterSecret); 211 212 SSLTrafficKeyDerivation kd = 213 SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); 214 if (kd == null) { 215 // unlikely 216 throw chc.conContext.fatal(Alert.INTERNAL_ERROR, 217 "Not supported key derivation: " + 218 chc.negotiatedProtocol); 219 } else { 220 chc.handshakeKeyDerivation = 221 kd.createKeyDerivation(chc, masterSecret); 222 } 223 } 224 225 // The handshake message has been delivered. 226 return null; 227 } 228 } 229 230 /** 231 * The DiffieHellman "ClientKeyExchange" handshake message consumer. 232 */ 233 private static final 234 class DHClientKeyExchangeConsumer implements SSLConsumer { 235 // Prevent instantiation of this class. DHClientKeyExchangeConsumer()236 private DHClientKeyExchangeConsumer() { 237 // blank 238 } 239 240 @Override consume(ConnectionContext context, ByteBuffer message)241 public void consume(ConnectionContext context, 242 ByteBuffer message) throws IOException { 243 // The consuming happens in server side only. 244 ServerHandshakeContext shc = (ServerHandshakeContext)context; 245 246 DHEPossession dhePossession = null; 247 for (SSLPossession possession : shc.handshakePossessions) { 248 if (possession instanceof DHEPossession) { 249 dhePossession = (DHEPossession)possession; 250 break; 251 } 252 } 253 254 if (dhePossession == null) { 255 // unlikely 256 throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 257 "No expected DHE possessions for client key exchange"); 258 } 259 260 SSLKeyExchange ke = SSLKeyExchange.valueOf( 261 shc.negotiatedCipherSuite.keyExchange, 262 shc.negotiatedProtocol); 263 if (ke == null) { 264 // unlikely 265 throw shc.conContext.fatal(Alert.INTERNAL_ERROR, 266 "Not supported key exchange type"); 267 } 268 269 DHClientKeyExchangeMessage ckem = 270 new DHClientKeyExchangeMessage(shc, message); 271 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 272 SSLLogger.fine( 273 "Consuming DH ClientKeyExchange handshake message", ckem); 274 } 275 276 // create the credentials 277 try { 278 DHParameterSpec params = dhePossession.publicKey.getParams(); 279 DHPublicKeySpec spec = new DHPublicKeySpec( 280 new BigInteger(1, ckem.y), 281 params.getP(), params.getG()); 282 KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman"); 283 DHPublicKey peerPublicKey = 284 (DHPublicKey)kf.generatePublic(spec); 285 286 // check constraints of peer DHPublicKey 287 if (!shc.algorithmConstraints.permits( 288 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), 289 peerPublicKey)) { 290 throw new SSLHandshakeException( 291 "DHPublicKey does not comply to algorithm constraints"); 292 } 293 294 NamedGroup namedGroup = NamedGroup.valueOf(params); 295 shc.handshakeCredentials.add( 296 new DHECredentials(peerPublicKey, namedGroup)); 297 } catch (GeneralSecurityException | java.io.IOException e) { 298 throw (SSLHandshakeException)(new SSLHandshakeException( 299 "Could not generate DHPublicKey").initCause(e)); 300 } 301 302 // update the states 303 SSLKeyDerivation masterKD = ke.createKeyDerivation(shc); 304 SecretKey masterSecret = 305 masterKD.deriveKey("MasterSecret", null); 306 shc.handshakeSession.setMasterSecret(masterSecret); 307 308 SSLTrafficKeyDerivation kd = 309 SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); 310 if (kd == null) { 311 // unlikely 312 throw shc.conContext.fatal(Alert.INTERNAL_ERROR, 313 "Not supported key derivation: " + shc.negotiatedProtocol); 314 } else { 315 shc.handshakeKeyDerivation = 316 kd.createKeyDerivation(shc, masterSecret); 317 } 318 } 319 } 320 } 321