1 /* 2 * Copyright (c) 1997, 2012, 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 27 package sun.security.ssl; 28 29 import java.io.IOException; 30 import java.io.PrintStream; 31 import java.math.BigInteger; 32 import javax.net.ssl.SSLHandshakeException; 33 34 /* 35 * Message used by clients to send their Diffie-Hellman public 36 * keys to servers. 37 * 38 * @author David Brownell 39 */ 40 final class DHClientKeyExchange extends HandshakeMessage { 41 42 @Override 43 int messageType() { 44 return ht_client_key_exchange; 45 } 46 47 /* 48 * This value may be empty if it was included in the 49 * client's certificate ... 50 */ 51 private byte[] dh_Yc; // 1 to 2^16 -1 bytes 52 53 BigInteger getClientPublicKey() { 54 return dh_Yc == null ? null : new BigInteger(1, dh_Yc); 55 } 56 57 /* 58 * Either pass the client's public key explicitly (because it's 59 * using DHE or DH_anon), or implicitly (the public key was in the 60 * certificate). 61 */ 62 DHClientKeyExchange(BigInteger publicKey) { 63 dh_Yc = toByteArray(publicKey); 64 } 65 66 DHClientKeyExchange() { 67 dh_Yc = null; 68 } 69 70 /* 71 * Get the client's public key either explicitly or implicitly. 72 * (It's ugly to have an empty record be sent in the latter case, 73 * but that's what the protocol spec requires.) 74 */ 75 DHClientKeyExchange(HandshakeInStream input) throws IOException { 76 if (input.available() >= 2) { 77 dh_Yc = input.getBytes16(); 78 } else { 79 // currently, we don't support cipher suites that requires 80 // implicit public key of client. 81 throw new SSLHandshakeException( 82 "Unsupported implicit client DiffieHellman public key"); 83 } 84 } 85 86 @Override 87 int messageLength() { 88 if (dh_Yc == null) { 89 return 0; 90 } else { 91 return dh_Yc.length + 2; 92 } 93 } 94 95 @Override 96 void send(HandshakeOutStream s) throws IOException { 97 if (dh_Yc != null && dh_Yc.length != 0) { 98 s.putBytes16(dh_Yc); 99 } 100 } 101 102 @Override 103 void print(PrintStream s) throws IOException { 104 s.println("*** ClientKeyExchange, DH"); 105 106 if (debug != null && Debug.isOn("verbose")) { 107 Debug.println(s, "DH Public key", dh_Yc); 108 } 109 } 110 } | 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.ssl.SupportedGroupsExtension.NamedGroup; 46 import sun.security.util.HexDumpEncoder; 47 48 /** 49 * Pack of the "ClientKeyExchange" handshake message. 50 */ 51 final class DHClientKeyExchange { 52 static final DHClientKeyExchangeConsumer dhHandshakeConsumer = 53 new DHClientKeyExchangeConsumer(); 54 static final DHClientKeyExchangeProducer dhHandshakeProducer = 55 new DHClientKeyExchangeProducer(); 56 57 /** 58 * The DiffieHellman ClientKeyExchange handshake message. 59 * 60 * If the client has sent a certificate which contains a suitable 61 * DiffieHellman key (for fixed_dh client authentication), then the 62 * client public value is implicit and does not need to be sent again. 63 * In this case, the client key exchange message will be sent, but it 64 * MUST be empty. 65 * 66 * Currently, we don't support cipher suite that requires implicit public 67 * key of client. 68 */ 69 private static final 70 class DHClientKeyExchangeMessage extends HandshakeMessage { 71 private byte[] y; // 1 to 2^16 - 1 bytes 72 73 DHClientKeyExchangeMessage( 74 HandshakeContext handshakeContext) throws IOException { 75 super(handshakeContext); 76 // This happens in client side only. 77 ClientHandshakeContext chc = 78 (ClientHandshakeContext)handshakeContext; 79 80 DHEPossession dhePossession = null; 81 for (SSLPossession possession : chc.handshakePossessions) { 82 if (possession instanceof DHEPossession) { 83 dhePossession = (DHEPossession)possession; 84 break; 85 } 86 } 87 88 if (dhePossession == null) { 89 // unlikely 90 chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 91 "No DHE credentials negotiated for client key exchange"); 92 } 93 94 DHPublicKey publicKey = dhePossession.publicKey; 95 DHParameterSpec params = publicKey.getParams(); 96 this.y = Utilities.toByteArray(publicKey.getY()); 97 } 98 99 DHClientKeyExchangeMessage(HandshakeContext handshakeContext, 100 ByteBuffer m) throws IOException { 101 super(handshakeContext); 102 // This happens in server side only. 103 ServerHandshakeContext shc = 104 (ServerHandshakeContext)handshakeContext; 105 106 if (m.remaining() < 3) { 107 shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 108 "Invalid DH ClientKeyExchange message: insufficient data"); 109 } 110 111 this.y = Record.getBytes16(m); 112 113 if (m.hasRemaining()) { 114 shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 115 "Invalid DH ClientKeyExchange message: unknown extra data"); 116 } 117 } 118 119 @Override 120 public SSLHandshake handshakeType() { 121 return SSLHandshake.CLIENT_KEY_EXCHANGE; 122 } 123 124 @Override 125 public int messageLength() { 126 return y.length + 2; // 2: length filed 127 } 128 129 @Override 130 public void send(HandshakeOutStream hos) throws IOException { 131 hos.putBytes16(y); 132 } 133 134 @Override 135 public String toString() { 136 MessageFormat messageFormat = new MessageFormat( 137 "\"DH ClientKeyExchange\": '{'\n" + 138 " \"parameters\": '{'\n" + 139 " \"dh_Yc\": '{'\n" + 140 "{0}\n" + 141 " '}',\n" + 142 " '}'\n" + 143 "'}'", 144 Locale.ENGLISH); 145 146 HexDumpEncoder hexEncoder = new HexDumpEncoder(); 147 Object[] messageFields = { 148 Utilities.indent( 149 hexEncoder.encodeBuffer(y), " "), 150 }; 151 return messageFormat.format(messageFields); 152 } 153 } 154 155 /** 156 * The DiffieHellman "ClientKeyExchange" handshake message producer. 157 */ 158 private static final 159 class DHClientKeyExchangeProducer implements HandshakeProducer { 160 // Prevent instantiation of this class. 161 private DHClientKeyExchangeProducer() { 162 // blank 163 } 164 165 @Override 166 public byte[] produce(ConnectionContext context, 167 HandshakeMessage message) throws IOException { 168 // The producing happens in client side only. 169 ClientHandshakeContext chc = (ClientHandshakeContext)context; 170 171 DHECredentials dheCredentials = null; 172 for (SSLCredentials cd : chc.handshakeCredentials) { 173 if (cd instanceof DHECredentials) { 174 dheCredentials = (DHECredentials)cd; 175 break; 176 } 177 } 178 179 if (dheCredentials == null) { 180 chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 181 "No DHE credentials negotiated for client key exchange"); 182 } 183 184 185 DHEPossession dhePossession = new DHEPossession( 186 dheCredentials, chc.sslContext.getSecureRandom()); 187 chc.handshakePossessions.add(dhePossession); 188 DHClientKeyExchangeMessage ckem = 189 new DHClientKeyExchangeMessage(chc); 190 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 191 SSLLogger.fine( 192 "Produced DH ClientKeyExchange handshake message", ckem); 193 } 194 195 // Output the handshake message. 196 ckem.write(chc.handshakeOutput); 197 chc.handshakeOutput.flush(); 198 199 // update the states 200 SSLKeyExchange ke = 201 SSLKeyExchange.valueOf(chc.negotiatedCipherSuite.keyExchange); 202 if (ke == null) { 203 // unlikely 204 chc.conContext.fatal(Alert.INTERNAL_ERROR, 205 "Not supported key exchange type"); 206 } else { 207 SSLKeyDerivation masterKD = ke.createKeyDerivation(chc); 208 SecretKey masterSecret = masterKD.deriveKey("TODO", null); 209 chc.handshakeSession.setMasterSecret(masterSecret); 210 211 SSLTrafficKeyDerivation kd = 212 SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); 213 if (kd == null) { 214 // unlikely 215 chc.conContext.fatal(Alert.INTERNAL_ERROR, 216 "Not supported key derivation: " + 217 chc.negotiatedProtocol); 218 } else { 219 chc.handshakeKeyDerivation = 220 kd.createKeyDerivation(chc, masterSecret); 221 } 222 } 223 224 // The handshake message has been delivered. 225 return null; 226 } 227 } 228 229 /** 230 * The DiffieHellman "ClientKeyExchange" handshake message consumer. 231 */ 232 private static final 233 class DHClientKeyExchangeConsumer implements SSLConsumer { 234 // Prevent instantiation of this class. 235 private DHClientKeyExchangeConsumer() { 236 // blank 237 } 238 239 @Override 240 public void consume(ConnectionContext context, 241 ByteBuffer message) throws IOException { 242 // The consuming happens in server side only. 243 ServerHandshakeContext shc = (ServerHandshakeContext)context; 244 245 DHEPossession dhePossession = null; 246 for (SSLPossession possession : shc.handshakePossessions) { 247 if (possession instanceof DHEPossession) { 248 dhePossession = (DHEPossession)possession; 249 break; 250 } 251 } 252 253 if (dhePossession == null) { 254 // unlikely 255 shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 256 "No expected DHE possessions for client key exchange"); 257 } 258 259 SSLKeyExchange ke = SSLKeyExchange.valueOf( 260 shc.negotiatedCipherSuite.keyExchange); 261 if (ke == null) { 262 // unlikely 263 shc.conContext.fatal(Alert.INTERNAL_ERROR, 264 "Not supported key exchange type"); 265 } 266 267 DHClientKeyExchangeMessage ckem = 268 new DHClientKeyExchangeMessage(shc, message); 269 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 270 SSLLogger.fine( 271 "Consuming DH ClientKeyExchange handshake message", ckem); 272 } 273 274 // create the credentials 275 try { 276 DHParameterSpec params = dhePossession.publicKey.getParams(); 277 DHPublicKeySpec spec = new DHPublicKeySpec( 278 new BigInteger(1, ckem.y), 279 params.getP(), params.getG()); 280 KeyFactory kf = JsseJce.getKeyFactory("DH"); 281 DHPublicKey peerPublicKey = 282 (DHPublicKey)kf.generatePublic(spec); 283 284 // check constraints of peer DHPublicKey 285 if (!shc.algorithmConstraints.permits( 286 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), 287 peerPublicKey)) { 288 throw new SSLHandshakeException( 289 "DHPublicKey does not comply to algorithm constraints"); 290 } 291 292 NamedGroup namedGroup = NamedGroup.valueOf(params); 293 shc.handshakeCredentials.add( 294 new DHECredentials(peerPublicKey, namedGroup)); 295 } catch (GeneralSecurityException | java.io.IOException e) { 296 throw (SSLHandshakeException)(new SSLHandshakeException( 297 "Could not generate DHPublicKey").initCause(e)); 298 } 299 300 // update the states 301 SSLKeyDerivation masterKD = ke.createKeyDerivation(shc); 302 SecretKey masterSecret = masterKD.deriveKey("TODO", null); 303 shc.handshakeSession.setMasterSecret(masterSecret); 304 305 SSLTrafficKeyDerivation kd = 306 SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); 307 if (kd == null) { 308 // unlikely 309 shc.conContext.fatal(Alert.INTERNAL_ERROR, 310 "Not supported key derivation: " + shc.negotiatedProtocol); 311 } else { 312 shc.handshakeKeyDerivation = 313 kd.createKeyDerivation(shc, masterSecret); 314 } 315 } 316 } 317 } |