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