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