1 /* 2 * Copyright (c) 2006, 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 package sun.security.ssl; 27 28 import java.io.IOException; 29 import java.io.PrintStream; 30 31 import java.security.PublicKey; 32 import java.security.interfaces.ECPublicKey; 33 import java.security.spec.*; 34 35 /** 36 * ClientKeyExchange message for all ECDH based key exchange methods. It 37 * contains the client's ephemeral public value. 38 * 39 * @since 1.6 40 * @author Andreas Sterbenz 41 */ 42 final class ECDHClientKeyExchange extends HandshakeMessage { 43 44 @Override 45 int messageType() { 46 return ht_client_key_exchange; 47 } 48 49 private byte[] encodedPoint; 50 51 byte[] getEncodedPoint() { 52 return encodedPoint; 53 } 54 55 // Called by the client with its ephemeral public key. 56 ECDHClientKeyExchange(PublicKey publicKey) { 57 ECPublicKey ecKey = (ECPublicKey)publicKey; 58 ECPoint point = ecKey.getW(); 59 ECParameterSpec params = ecKey.getParams(); 60 encodedPoint = JsseJce.encodePoint(point, params.getCurve()); 61 } 62 63 ECDHClientKeyExchange(HandshakeInStream input) throws IOException { 64 encodedPoint = input.getBytes8(); 65 } 66 67 @Override 68 int messageLength() { 69 return encodedPoint.length + 1; 70 } 71 72 @Override 73 void send(HandshakeOutStream s) throws IOException { 74 s.putBytes8(encodedPoint); 75 } 76 77 @Override 78 void print(PrintStream s) throws IOException { 79 s.println("*** ECDHClientKeyExchange"); 80 81 if (debug != null && Debug.isOn("verbose")) { 82 Debug.println(s, "ECDH Public value", encodedPoint); 83 } 84 } 85 } | 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.nio.ByteBuffer; 30 import java.security.AlgorithmConstraints; 31 import java.security.CryptoPrimitive; 32 import java.security.GeneralSecurityException; 33 import java.security.KeyFactory; 34 import java.security.PrivateKey; 35 import java.security.PublicKey; 36 import java.security.interfaces.ECPrivateKey; 37 import java.security.interfaces.ECPublicKey; 38 import java.security.spec.ECParameterSpec; 39 import java.security.spec.ECPoint; 40 import java.security.spec.ECPublicKeySpec; 41 import java.text.MessageFormat; 42 import java.util.EnumSet; 43 import java.util.Locale; 44 import javax.crypto.SecretKey; 45 import javax.net.ssl.SSLHandshakeException; 46 import sun.security.ssl.ECDHKeyExchange.ECDHECredentials; 47 import sun.security.ssl.ECDHKeyExchange.ECDHEPossession; 48 import sun.security.ssl.SSLHandshake.HandshakeMessage; 49 import sun.security.ssl.SupportedGroupsExtension.NamedGroup; 50 import sun.security.ssl.X509Authentication.X509Credentials; 51 import sun.security.ssl.X509Authentication.X509Possession; 52 import sun.security.util.HexDumpEncoder; 53 54 /** 55 * Pack of the "ClientKeyExchange" handshake message. 56 */ 57 final class ECDHClientKeyExchange { 58 static final SSLConsumer ecdhHandshakeConsumer = 59 new ECDHClientKeyExchangeConsumer(); 60 static final HandshakeProducer ecdhHandshakeProducer = 61 new ECDHClientKeyExchangeProducer(); 62 63 static final SSLConsumer ecdheHandshakeConsumer = 64 new ECDHEClientKeyExchangeConsumer(); 65 static final HandshakeProducer ecdheHandshakeProducer = 66 new ECDHEClientKeyExchangeProducer(); 67 68 /** 69 * The ECDH/ECDHE ClientKeyExchange handshake message. 70 */ 71 private static final 72 class ECDHClientKeyExchangeMessage extends HandshakeMessage { 73 private final byte[] encodedPoint; 74 75 ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext, 76 ECPublicKey publicKey) { 77 super(handshakeContext); 78 79 ECPoint point = publicKey.getW(); 80 ECParameterSpec params = publicKey.getParams(); 81 encodedPoint = JsseJce.encodePoint(point, params.getCurve()); 82 } 83 84 ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext, 85 ByteBuffer m) throws IOException { 86 super(handshakeContext); 87 if (m.remaining() != 0) { // explicit PublicValueEncoding 88 this.encodedPoint = Record.getBytes8(m); 89 } else { 90 this.encodedPoint = new byte[0]; 91 } 92 } 93 94 // Check constraints of the specified EC public key. 95 static void checkConstraints(AlgorithmConstraints constraints, 96 ECPublicKey publicKey, 97 byte[] encodedPoint) throws SSLHandshakeException { 98 99 try { 100 ECParameterSpec params = publicKey.getParams(); 101 ECPoint point = 102 JsseJce.decodePoint(encodedPoint, params.getCurve()); 103 ECPublicKeySpec spec = new ECPublicKeySpec(point, params); 104 105 KeyFactory kf = JsseJce.getKeyFactory("EC"); 106 ECPublicKey peerPublicKey = 107 (ECPublicKey)kf.generatePublic(spec); 108 109 // check constraints of ECPublicKey 110 if (!constraints.permits( 111 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), 112 peerPublicKey)) { 113 throw new SSLHandshakeException( 114 "ECPublicKey does not comply to algorithm constraints"); 115 } 116 } catch (GeneralSecurityException | java.io.IOException e) { 117 throw (SSLHandshakeException) new SSLHandshakeException( 118 "Could not generate ECPublicKey").initCause(e); 119 } 120 } 121 122 @Override 123 public SSLHandshake handshakeType() { 124 return SSLHandshake.CLIENT_KEY_EXCHANGE; 125 } 126 127 @Override 128 public int messageLength() { 129 if (encodedPoint == null || encodedPoint.length == 0) { 130 return 0; 131 } else { 132 return 1 + encodedPoint.length; 133 } 134 } 135 136 @Override 137 public void send(HandshakeOutStream hos) throws IOException { 138 if (encodedPoint != null && encodedPoint.length != 0) { 139 hos.putBytes8(encodedPoint); 140 } 141 } 142 143 @Override 144 public String toString() { 145 MessageFormat messageFormat = new MessageFormat( 146 "\"ECDH ClientKeyExchange\": '{'\n" + 147 " \"ecdh public\": '{'\n" + 148 "{0}\n" + 149 " '}',\n" + 150 "'}'", 151 Locale.ENGLISH); 152 if (encodedPoint == null || encodedPoint.length == 0) { 153 Object[] messageFields = { 154 " <implicit>" 155 }; 156 return messageFormat.format(messageFields); 157 } else { 158 HexDumpEncoder hexEncoder = new HexDumpEncoder(); 159 Object[] messageFields = { 160 Utilities.indent( 161 hexEncoder.encodeBuffer(encodedPoint), " "), 162 }; 163 return messageFormat.format(messageFields); 164 } 165 } 166 } 167 168 /** 169 * The ECDH "ClientKeyExchange" handshake message producer. 170 */ 171 private static final 172 class ECDHClientKeyExchangeProducer implements HandshakeProducer { 173 // Prevent instantiation of this class. 174 private ECDHClientKeyExchangeProducer() { 175 // blank 176 } 177 178 @Override 179 public byte[] produce(ConnectionContext context, 180 HandshakeMessage message) throws IOException { 181 // The producing happens in client side only. 182 ClientHandshakeContext chc = (ClientHandshakeContext)context; 183 184 X509Credentials x509Credentials = null; 185 for (SSLCredentials credential : chc.handshakeCredentials) { 186 if (credential instanceof X509Credentials) { 187 x509Credentials = (X509Credentials)credential; 188 break; 189 } 190 } 191 192 if (x509Credentials == null) { 193 chc.conContext.fatal(Alert.INTERNAL_ERROR, 194 "No server certificate for ECDH client key exchange"); 195 } 196 197 PublicKey publicKey = x509Credentials.popPublicKey; 198 if (!publicKey.getAlgorithm().equals("EC")) { 199 chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, 200 "Not EC server certificate for ECDH client key exchange"); 201 } 202 203 ECParameterSpec params = ((ECPublicKey)publicKey).getParams(); 204 NamedGroup namedGroup = NamedGroup.valueOf(params); 205 if (namedGroup == null) { 206 chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, 207 "Unsupported EC server cert for ECDH client key exchange"); 208 } 209 210 ECDHEPossession ecdhePossession = new ECDHEPossession( 211 namedGroup, chc.sslContext.getSecureRandom()); 212 chc.handshakePossessions.add(ecdhePossession); 213 ECDHClientKeyExchangeMessage cke = 214 new ECDHClientKeyExchangeMessage( 215 chc, ecdhePossession.publicKey); 216 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 217 SSLLogger.fine( 218 "Produced ECDH ClientKeyExchange handshake message", cke); 219 } 220 221 // Output the handshake message. 222 cke.write(chc.handshakeOutput); 223 chc.handshakeOutput.flush(); 224 225 // update the states 226 SSLKeyExchange ke = 227 SSLKeyExchange.valueOf(chc.negotiatedCipherSuite.keyExchange); 228 if (ke == null) { 229 // unlikely 230 chc.conContext.fatal(Alert.INTERNAL_ERROR, 231 "Not supported key exchange type"); 232 } else { 233 SSLKeyDerivation masterKD = ke.createKeyDerivation(chc); 234 SecretKey masterSecret = masterKD.deriveKey("TODO", null); 235 chc.handshakeSession.setMasterSecret(masterSecret); 236 237 SSLTrafficKeyDerivation kd = 238 SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); 239 if (kd == null) { 240 // unlikely 241 chc.conContext.fatal(Alert.INTERNAL_ERROR, 242 "Not supported key derivation: " + 243 chc.negotiatedProtocol); 244 } else { 245 chc.handshakeKeyDerivation = 246 kd.createKeyDerivation(chc, masterSecret); 247 } 248 } 249 250 // The handshake message has been delivered. 251 return null; 252 } 253 } 254 255 /** 256 * The ECDH "ClientKeyExchange" handshake message consumer. 257 */ 258 private static final 259 class ECDHClientKeyExchangeConsumer implements SSLConsumer { 260 // Prevent instantiation of this class. 261 private ECDHClientKeyExchangeConsumer() { 262 // blank 263 } 264 265 @Override 266 public void consume(ConnectionContext context, 267 ByteBuffer message) throws IOException { 268 // The consuming happens in server side only. 269 ServerHandshakeContext shc = (ServerHandshakeContext)context; 270 271 X509Possession x509Possession = null; 272 for (SSLPossession possession : shc.handshakePossessions) { 273 if (possession instanceof X509Possession) { 274 x509Possession = (X509Possession)possession; 275 break; 276 } 277 } 278 279 if (x509Possession == null) { 280 // unlikely, have been checked during cipher suite negotiation. 281 shc.conContext.fatal(Alert.INTERNAL_ERROR, 282 "No expected EC server cert for ECDH client key exchange"); 283 return; // make the compiler happy 284 } 285 286 PrivateKey privateKey = x509Possession.popPrivateKey; 287 if (!privateKey.getAlgorithm().equals("EC")) { 288 // unlikely, have been checked during cipher suite negotiation. 289 shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, 290 "Not EC server cert for ECDH client key exchange"); 291 } 292 293 ECParameterSpec params = ((ECPrivateKey)privateKey).getParams(); 294 NamedGroup namedGroup = NamedGroup.valueOf(params); 295 if (namedGroup == null) { 296 // unlikely, have been checked during cipher suite negotiation. 297 shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, 298 "Unsupported EC server cert for ECDH client key exchange"); 299 } 300 301 SSLKeyExchange ke = SSLKeyExchange.valueOf( 302 shc.negotiatedCipherSuite.keyExchange); 303 if (ke == null) { 304 // unlikely 305 shc.conContext.fatal(Alert.INTERNAL_ERROR, 306 "Not supported key exchange type"); 307 return; // make the compiler happy 308 } 309 310 // parse the handshake message 311 ECDHClientKeyExchangeMessage cke = 312 new ECDHClientKeyExchangeMessage(shc, message); 313 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 314 SSLLogger.fine( 315 "Consuming ECDH ClientKeyExchange handshake message", cke); 316 } 317 318 // create the credentials 319 try { 320 ECPoint point = 321 JsseJce.decodePoint(cke.encodedPoint, params.getCurve()); 322 ECPublicKeySpec spec = new ECPublicKeySpec(point, params); 323 324 KeyFactory kf = JsseJce.getKeyFactory("EC"); 325 ECPublicKey peerPublicKey = 326 (ECPublicKey)kf.generatePublic(spec); 327 328 // check constraints of peer ECPublicKey 329 if (!shc.algorithmConstraints.permits( 330 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), 331 peerPublicKey)) { 332 throw new SSLHandshakeException( 333 "ECPublicKey does not comply to algorithm constraints"); 334 } 335 336 shc.handshakeCredentials.add(new ECDHECredentials( 337 peerPublicKey, namedGroup)); 338 } catch (GeneralSecurityException | java.io.IOException e) { 339 throw (SSLHandshakeException)(new SSLHandshakeException( 340 "Could not generate ECPublicKey").initCause(e)); 341 } 342 343 // update the states 344 SSLKeyDerivation masterKD = ke.createKeyDerivation(shc); 345 SecretKey masterSecret = masterKD.deriveKey("TODO", null); 346 shc.handshakeSession.setMasterSecret(masterSecret); 347 348 SSLTrafficKeyDerivation kd = 349 SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); 350 if (kd == null) { 351 // unlikely 352 shc.conContext.fatal(Alert.INTERNAL_ERROR, 353 "Not supported key derivation: " + shc.negotiatedProtocol); 354 } else { 355 shc.handshakeKeyDerivation = 356 kd.createKeyDerivation(shc, masterSecret); 357 } 358 } 359 } 360 361 /** 362 * The ECDHE "ClientKeyExchange" handshake message producer. 363 */ 364 private static final 365 class ECDHEClientKeyExchangeProducer implements HandshakeProducer { 366 // Prevent instantiation of this class. 367 private ECDHEClientKeyExchangeProducer() { 368 // blank 369 } 370 371 @Override 372 public byte[] produce(ConnectionContext context, 373 HandshakeMessage message) throws IOException { 374 // The producing happens in client side only. 375 ClientHandshakeContext chc = (ClientHandshakeContext)context; 376 377 ECDHECredentials ecdheCredentials = null; 378 for (SSLCredentials cd : chc.handshakeCredentials) { 379 if (cd instanceof ECDHECredentials) { 380 ecdheCredentials = (ECDHECredentials)cd; 381 break; 382 } 383 } 384 385 if (ecdheCredentials == null) { 386 chc.conContext.fatal(Alert.INTERNAL_ERROR, 387 "No ECDHE credentials negotiated for client key exchange"); 388 } 389 390 ECDHEPossession ecdhePossession = new ECDHEPossession( 391 ecdheCredentials, chc.sslContext.getSecureRandom()); 392 chc.handshakePossessions.add(ecdhePossession); 393 ECDHClientKeyExchangeMessage cke = 394 new ECDHClientKeyExchangeMessage( 395 chc, ecdhePossession.publicKey); 396 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 397 SSLLogger.fine( 398 "Produced ECDHE ClientKeyExchange handshake message", cke); 399 } 400 401 // Output the handshake message. 402 cke.write(chc.handshakeOutput); 403 chc.handshakeOutput.flush(); 404 405 // update the states 406 SSLKeyExchange ke = 407 SSLKeyExchange.valueOf(chc.negotiatedCipherSuite.keyExchange); 408 if (ke == null) { 409 // unlikely 410 chc.conContext.fatal(Alert.INTERNAL_ERROR, 411 "Not supported key exchange type"); 412 } else { 413 SSLKeyDerivation masterKD = ke.createKeyDerivation(chc); 414 SecretKey masterSecret = masterKD.deriveKey("TODO", null); 415 chc.handshakeSession.setMasterSecret(masterSecret); 416 417 SSLTrafficKeyDerivation kd = 418 SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); 419 if (kd == null) { 420 // unlikely 421 chc.conContext.fatal(Alert.INTERNAL_ERROR, 422 "Not supported key derivation: " + 423 chc.negotiatedProtocol); 424 } else { 425 chc.handshakeKeyDerivation = 426 kd.createKeyDerivation(chc, masterSecret); 427 } 428 } 429 430 // The handshake message has been delivered. 431 return null; 432 } 433 } 434 435 /** 436 * The ECDHE "ClientKeyExchange" handshake message consumer. 437 */ 438 private static final 439 class ECDHEClientKeyExchangeConsumer implements SSLConsumer { 440 // Prevent instantiation of this class. 441 private ECDHEClientKeyExchangeConsumer() { 442 // blank 443 } 444 445 @Override 446 public void consume(ConnectionContext context, 447 ByteBuffer message) throws IOException { 448 // The consuming happens in server side only. 449 ServerHandshakeContext shc = (ServerHandshakeContext)context; 450 451 ECDHEPossession ecdhePossession = null; 452 for (SSLPossession possession : shc.handshakePossessions) { 453 if (possession instanceof ECDHEPossession) { 454 ecdhePossession = (ECDHEPossession)possession; 455 break; 456 } 457 } 458 if (ecdhePossession == null) { 459 // unlikely 460 shc.conContext.fatal(Alert.INTERNAL_ERROR, 461 "No expected ECDHE possessions for client key exchange"); 462 return; // make the compiler happy 463 } 464 465 ECParameterSpec params = ecdhePossession.publicKey.getParams(); 466 NamedGroup namedGroup = NamedGroup.valueOf(params); 467 if (namedGroup == null) { 468 // unlikely, have been checked during cipher suite negotiation. 469 shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, 470 "Unsupported EC server cert for ECDHE client key exchange"); 471 } 472 473 SSLKeyExchange ke = SSLKeyExchange.valueOf( 474 shc.negotiatedCipherSuite.keyExchange); 475 if (ke == null) { 476 // unlikely 477 shc.conContext.fatal(Alert.INTERNAL_ERROR, 478 "Not supported key exchange type"); 479 return; // make the compiler happy 480 } 481 482 // parse the handshake message 483 ECDHClientKeyExchangeMessage cke = 484 new ECDHClientKeyExchangeMessage(shc, message); 485 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 486 SSLLogger.fine( 487 "Consuming ECDHE ClientKeyExchange handshake message", cke); 488 } 489 490 // create the credentials 491 try { 492 ECPoint point = 493 JsseJce.decodePoint(cke.encodedPoint, params.getCurve()); 494 ECPublicKeySpec spec = new ECPublicKeySpec(point, params); 495 496 KeyFactory kf = JsseJce.getKeyFactory("EC"); 497 ECPublicKey peerPublicKey = 498 (ECPublicKey)kf.generatePublic(spec); 499 500 // check constraints of peer ECPublicKey 501 if (!shc.algorithmConstraints.permits( 502 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), 503 peerPublicKey)) { 504 throw new SSLHandshakeException( 505 "ECPublicKey does not comply to algorithm constraints"); 506 } 507 508 shc.handshakeCredentials.add(new ECDHECredentials( 509 peerPublicKey, namedGroup)); 510 } catch (GeneralSecurityException | java.io.IOException e) { 511 throw (SSLHandshakeException)(new SSLHandshakeException( 512 "Could not generate ECPublicKey").initCause(e)); 513 } 514 515 // update the states 516 SSLKeyDerivation masterKD = ke.createKeyDerivation(shc); 517 SecretKey masterSecret = masterKD.deriveKey("TODO", null); 518 shc.handshakeSession.setMasterSecret(masterSecret); 519 520 SSLTrafficKeyDerivation kd = 521 SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); 522 if (kd == null) { 523 // unlikely 524 shc.conContext.fatal(Alert.INTERNAL_ERROR, 525 "Not supported key derivation: " + shc.negotiatedProtocol); 526 } else { 527 shc.handshakeKeyDerivation = 528 kd.createKeyDerivation(shc, masterSecret); 529 } 530 } 531 } 532 } |