1 /* ClientHandshake.java -- 2 Copyright (C) 2006 Free Software Foundation, Inc. 3 4 This file is a part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or (at 9 your option) any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 19 USA 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package gnu.javax.net.ssl.provider; 40 41 import static gnu.javax.net.ssl.provider.ClientHandshake.State.*; 42 import static gnu.javax.net.ssl.provider.KeyExchangeAlgorithm.*; 43 44 import gnu.classpath.debug.Component; 45 import gnu.java.security.action.GetSecurityPropertyAction; 46 import gnu.javax.crypto.key.dh.GnuDHPublicKey; 47 import gnu.javax.net.ssl.AbstractSessionContext; 48 import gnu.javax.net.ssl.Session; 49 import gnu.javax.net.ssl.provider.Alert.Description; 50 import gnu.javax.net.ssl.provider.Alert.Level; 51 import gnu.javax.net.ssl.provider.CertificateRequest.ClientCertificateType; 52 import gnu.javax.net.ssl.provider.ServerNameList.NameType; 53 import gnu.javax.net.ssl.provider.ServerNameList.ServerName; 54 55 import java.nio.ByteBuffer; 56 import java.security.AccessController; 57 import java.security.InvalidAlgorithmParameterException; 58 import java.security.InvalidKeyException; 59 import java.security.KeyPair; 60 import java.security.KeyPairGenerator; 61 import java.security.MessageDigest; 62 import java.security.NoSuchAlgorithmException; 63 import java.security.PrivateKey; 64 import java.security.SignatureException; 65 import java.security.cert.CertificateException; 66 import java.security.cert.X509Certificate; 67 import java.util.Arrays; 68 import java.util.Collections; 69 import java.util.LinkedList; 70 import java.util.List; 71 import java.util.zip.Deflater; 72 import java.util.zip.Inflater; 73 74 import javax.crypto.BadPaddingException; 75 import javax.crypto.Cipher; 76 import javax.crypto.IllegalBlockSizeException; 77 import javax.crypto.NoSuchPaddingException; 78 import javax.crypto.interfaces.DHPrivateKey; 79 import javax.crypto.interfaces.DHPublicKey; 80 import javax.crypto.spec.DHParameterSpec; 81 import javax.net.ssl.SSLException; 82 import javax.net.ssl.SSLPeerUnverifiedException; 83 import javax.net.ssl.X509ExtendedKeyManager; 84 import javax.net.ssl.SSLEngineResult.HandshakeStatus; 85 import javax.security.auth.x500.X500Principal; 86 87 /** 88 * @author Casey Marshall (csm@gnu.org) 89 */ 90 public class ClientHandshake extends AbstractHandshake 91 { 92 static enum State 93 { 94 WRITE_CLIENT_HELLO (false, true), 95 READ_SERVER_HELLO (true, false), 96 READ_CERTIFICATE (true, false), 97 READ_SERVER_KEY_EXCHANGE (true, false), 98 READ_CERTIFICATE_REQUEST (true, false), 99 READ_SERVER_HELLO_DONE (true, false), 100 WRITE_CERTIFICATE (false, true), 101 WRITE_CLIENT_KEY_EXCHANGE (false, true), 102 WRITE_CERTIFICATE_VERIFY (false, true), 103 WRITE_FINISHED (false, true), 104 READ_FINISHED (true, false), 105 DONE (false, false); 106 107 private final boolean isWriteState; 108 private final boolean isReadState; 109 State(boolean isReadState, boolean isWriteState)110 private State(boolean isReadState, boolean isWriteState) 111 { 112 this.isReadState = isReadState; 113 this.isWriteState = isWriteState; 114 } 115 isReadState()116 boolean isReadState() 117 { 118 return isReadState; 119 } 120 isWriteState()121 boolean isWriteState() 122 { 123 return isWriteState; 124 } 125 } 126 127 private State state; 128 private ByteBuffer outBuffer; 129 private boolean continuedSession; 130 private SessionImpl continued; 131 private KeyPair dhPair; 132 private String keyAlias; 133 private PrivateKey privateKey; 134 private MaxFragmentLength maxFragmentLengthSent; 135 private boolean truncatedHMacSent; 136 private ProtocolVersion sentVersion; 137 138 // Delegated tasks. 139 private CertVerifier certVerifier; 140 private ParamsVerifier paramsVerifier; 141 private DelegatedTask keyExchange; 142 private CertLoader certLoader; 143 private GenCertVerify genCertVerify; 144 ClientHandshake(SSLEngineImpl engine)145 public ClientHandshake(SSLEngineImpl engine) throws NoSuchAlgorithmException 146 { 147 super(engine); 148 state = WRITE_CLIENT_HELLO; 149 continuedSession = false; 150 } 151 152 /* (non-Javadoc) 153 * @see gnu.javax.net.ssl.provider.AbstractHandshake#implHandleInput() 154 */ implHandleInput()155 @Override protected HandshakeStatus implHandleInput() throws SSLException 156 { 157 if (state == DONE) 158 return HandshakeStatus.FINISHED; 159 160 if (state.isWriteState() 161 || (outBuffer != null && outBuffer.hasRemaining())) 162 return HandshakeStatus.NEED_WRAP; 163 164 // Copy the current buffer, and prepare it for reading. 165 ByteBuffer buffer = handshakeBuffer.duplicate (); 166 buffer.flip(); 167 buffer.position(handshakeOffset); 168 169 Handshake handshake = new Handshake(buffer.slice(), 170 engine.session().suite, 171 engine.session().version); 172 173 if (Debug.DEBUG) 174 logger.logv(Component.SSL_HANDSHAKE, "processing in state {0}:\n{1}", 175 state, handshake); 176 177 switch (state) 178 { 179 // Server Hello. 180 case READ_SERVER_HELLO: 181 { 182 if (handshake.type() != Handshake.Type.SERVER_HELLO) 183 throw new AlertException(new Alert(Alert.Level.FATAL, 184 Alert.Description.UNEXPECTED_MESSAGE)); 185 ServerHello hello = (ServerHello) handshake.body(); 186 serverRandom = hello.random().copy(); 187 engine.session().suite = hello.cipherSuite(); 188 engine.session().version = hello.version(); 189 compression = hello.compressionMethod(); 190 Session.ID serverId = new Session.ID(hello.sessionId()); 191 if (continued != null 192 && continued.id().equals(serverId)) 193 { 194 continuedSession = true; 195 engine.setSession(continued); 196 } 197 else if (engine.getEnableSessionCreation()) 198 { 199 ((AbstractSessionContext) engine.contextImpl 200 .engineGetClientSessionContext()).put(engine.session()); 201 } 202 ExtensionList extensions = hello.extensions(); 203 if (extensions != null) 204 { 205 for (Extension extension : extensions) 206 { 207 Extension.Type type = extension.type(); 208 if (type == null) 209 continue; 210 switch (type) 211 { 212 case MAX_FRAGMENT_LENGTH: 213 MaxFragmentLength mfl 214 = (MaxFragmentLength) extension.value(); 215 if (maxFragmentLengthSent == mfl) 216 engine.session().setApplicationBufferSize(mfl.maxLength()); 217 break; 218 219 case TRUNCATED_HMAC: 220 if (truncatedHMacSent) 221 engine.session().setTruncatedMac(true); 222 break; 223 } 224 } 225 } 226 227 KeyExchangeAlgorithm kex = engine.session().suite.keyExchangeAlgorithm(); 228 if (continuedSession) 229 { 230 byte[][] keys = generateKeys(clientRandom, serverRandom, 231 engine.session()); 232 setupSecurityParameters(keys, true, engine, compression); 233 state = READ_FINISHED; 234 } 235 else if (kex == RSA || kex == DH_DSS || kex == DH_RSA 236 || kex == DHE_DSS || kex == DHE_RSA || kex == RSA_PSK) 237 state = READ_CERTIFICATE; 238 else if (kex == DH_anon || kex == PSK || kex == DHE_PSK) 239 state = READ_SERVER_KEY_EXCHANGE; 240 else 241 state = READ_CERTIFICATE_REQUEST; 242 } 243 break; 244 245 // Server Certificate. 246 case READ_CERTIFICATE: 247 { 248 if (handshake.type() != Handshake.Type.CERTIFICATE) 249 { 250 // We need a certificate for non-anonymous suites. 251 if (engine.session().suite.signatureAlgorithm() != SignatureAlgorithm.ANONYMOUS) 252 throw new AlertException(new Alert(Level.FATAL, 253 Description.UNEXPECTED_MESSAGE)); 254 state = READ_SERVER_KEY_EXCHANGE; 255 } 256 Certificate cert = (Certificate) handshake.body(); 257 X509Certificate[] chain = null; 258 try 259 { 260 chain = cert.certificates().toArray(new X509Certificate[0]); 261 } 262 catch (CertificateException ce) 263 { 264 throw new AlertException(new Alert(Level.FATAL, 265 Description.BAD_CERTIFICATE), 266 ce); 267 } 268 catch (NoSuchAlgorithmException nsae) 269 { 270 throw new AlertException(new Alert(Level.FATAL, 271 Description.UNSUPPORTED_CERTIFICATE), 272 nsae); 273 } 274 engine.session().setPeerCertificates(chain); 275 certVerifier = new CertVerifier(true, chain); 276 tasks.add(certVerifier); 277 278 // If we are doing an RSA key exchange, generate our parameters. 279 KeyExchangeAlgorithm kea = engine.session().suite.keyExchangeAlgorithm(); 280 if (kea == RSA || kea == RSA_PSK) 281 { 282 keyExchange = new RSAGen(kea == RSA); 283 tasks.add(keyExchange); 284 if (kea == RSA) 285 state = READ_CERTIFICATE_REQUEST; 286 else 287 state = READ_SERVER_KEY_EXCHANGE; 288 } 289 else 290 state = READ_SERVER_KEY_EXCHANGE; 291 } 292 break; 293 294 // Server Key Exchange. 295 case READ_SERVER_KEY_EXCHANGE: 296 { 297 CipherSuite s = engine.session().suite; 298 KeyExchangeAlgorithm kexalg = s.keyExchangeAlgorithm(); 299 // XXX also SRP. 300 if (kexalg != DHE_DSS && kexalg != DHE_RSA && kexalg != DH_anon 301 && kexalg != DHE_PSK && kexalg != PSK && kexalg != RSA_PSK) 302 throw new AlertException(new Alert(Level.FATAL, 303 Description.UNEXPECTED_MESSAGE)); 304 305 if (handshake.type() != Handshake.Type.SERVER_KEY_EXCHANGE) 306 { 307 if (kexalg != RSA_PSK && kexalg != PSK) 308 throw new AlertException(new Alert(Level.FATAL, 309 Description.UNEXPECTED_MESSAGE)); 310 state = READ_CERTIFICATE_REQUEST; 311 return HandshakeStatus.NEED_UNWRAP; 312 } 313 314 ServerKeyExchange skex = (ServerKeyExchange) handshake.body(); 315 ByteBuffer paramsBuffer = null; 316 if (kexalg == DHE_DSS || kexalg == DHE_RSA || kexalg == DH_anon) 317 { 318 ServerDHParams dhParams = (ServerDHParams) skex.params(); 319 ByteBuffer b = dhParams.buffer(); 320 paramsBuffer = ByteBuffer.allocate(b.remaining()); 321 paramsBuffer.put(b); 322 } 323 324 if (s.signatureAlgorithm() != SignatureAlgorithm.ANONYMOUS) 325 { 326 byte[] signature = skex.signature().signature(); 327 paramsVerifier = new ParamsVerifier(paramsBuffer, signature); 328 tasks.add(paramsVerifier); 329 } 330 331 if (kexalg == DHE_DSS || kexalg == DHE_RSA || kexalg == DH_anon) 332 { 333 ServerDHParams dhParams = (ServerDHParams) skex.params(); 334 DHPublicKey serverKey = new GnuDHPublicKey(null, 335 dhParams.p(), 336 dhParams.g(), 337 dhParams.y()); 338 DHParameterSpec params = new DHParameterSpec(dhParams.p(), 339 dhParams.g()); 340 keyExchange = new ClientDHGen(serverKey, params, true); 341 tasks.add(keyExchange); 342 } 343 if (kexalg == DHE_PSK) 344 { 345 ServerDHE_PSKParameters pskParams = (ServerDHE_PSKParameters) 346 skex.params(); 347 ServerDHParams dhParams = pskParams.params(); 348 DHPublicKey serverKey = new GnuDHPublicKey(null, 349 dhParams.p(), 350 dhParams.g(), 351 dhParams.y()); 352 DHParameterSpec params = new DHParameterSpec(dhParams.p(), 353 dhParams.g()); 354 keyExchange = new ClientDHGen(serverKey, params, false); 355 tasks.add(keyExchange); 356 } 357 state = READ_CERTIFICATE_REQUEST; 358 } 359 break; 360 361 // Certificate Request. 362 case READ_CERTIFICATE_REQUEST: 363 { 364 if (handshake.type() != Handshake.Type.CERTIFICATE_REQUEST) 365 { 366 state = READ_SERVER_HELLO_DONE; 367 return HandshakeStatus.NEED_UNWRAP; 368 } 369 370 CertificateRequest req = (CertificateRequest) handshake.body(); 371 ClientCertificateTypeList types = req.types(); 372 LinkedList<String> typeList = new LinkedList<String>(); 373 for (ClientCertificateType t : types) 374 typeList.add(t.name()); 375 376 X500PrincipalList issuers = req.authorities(); 377 LinkedList<X500Principal> issuerList = new LinkedList<X500Principal>(); 378 for (X500Principal p : issuers) 379 issuerList.add(p); 380 381 certLoader = new CertLoader(typeList, issuerList); 382 tasks.add(certLoader); 383 } 384 break; 385 386 // Server Hello Done. 387 case READ_SERVER_HELLO_DONE: 388 { 389 if (handshake.type() != Handshake.Type.SERVER_HELLO_DONE) 390 throw new AlertException(new Alert(Level.FATAL, 391 Description.UNEXPECTED_MESSAGE)); 392 state = WRITE_CERTIFICATE; 393 } 394 break; 395 396 // Finished. 397 case READ_FINISHED: 398 { 399 if (handshake.type() != Handshake.Type.FINISHED) 400 throw new AlertException(new Alert(Level.FATAL, 401 Description.UNEXPECTED_MESSAGE)); 402 403 Finished serverFinished = (Finished) handshake.body(); 404 MessageDigest md5copy = null; 405 MessageDigest shacopy = null; 406 try 407 { 408 md5copy = (MessageDigest) md5.clone(); 409 shacopy = (MessageDigest) sha.clone(); 410 } 411 catch (CloneNotSupportedException cnse) 412 { 413 // We're improperly configured to use a non-cloneable 414 // md5/sha-1, OR there's a runtime bug. 415 throw new SSLException(cnse); 416 } 417 Finished clientFinished = 418 new Finished(generateFinished(md5copy, shacopy, 419 false, engine.session()), 420 engine.session().version); 421 422 if (Debug.DEBUG) 423 logger.logv(Component.SSL_HANDSHAKE, "clientFinished: {0}", 424 clientFinished); 425 426 if (engine.session().version == ProtocolVersion.SSL_3) 427 { 428 if (!Arrays.equals(clientFinished.md5Hash(), 429 serverFinished.md5Hash()) 430 || !Arrays.equals(clientFinished.shaHash(), 431 serverFinished.shaHash())) 432 { 433 engine.session().invalidate(); 434 throw new SSLException("session verify failed"); 435 } 436 } 437 else 438 { 439 if (!Arrays.equals(clientFinished.verifyData(), 440 serverFinished.verifyData())) 441 { 442 engine.session().invalidate(); 443 throw new SSLException("session verify failed"); 444 } 445 } 446 447 if (continuedSession) 448 { 449 engine.changeCipherSpec(); 450 state = WRITE_FINISHED; 451 } 452 else 453 state = DONE; 454 } 455 break; 456 457 default: 458 throw new IllegalStateException("invalid state: " + state); 459 } 460 461 handshakeOffset += handshake.length() + 4; 462 463 if (!tasks.isEmpty()) 464 return HandshakeStatus.NEED_TASK; 465 if (state.isWriteState() 466 || (outBuffer != null && outBuffer.hasRemaining())) 467 return HandshakeStatus.NEED_WRAP; 468 if (state.isReadState()) 469 return HandshakeStatus.NEED_UNWRAP; 470 471 return HandshakeStatus.FINISHED; 472 } 473 474 /* (non-Javadoc) 475 * @see gnu.javax.net.ssl.provider.AbstractHandshake#implHandleOutput(java.nio.ByteBuffer) 476 */ implHandleOutput(ByteBuffer fragment)477 @Override protected HandshakeStatus implHandleOutput(ByteBuffer fragment) 478 throws SSLException 479 { 480 if (Debug.DEBUG) 481 logger.logv(Component.SSL_HANDSHAKE, "output to {0}; state:{1}; outBuffer:{2}", 482 fragment, state, outBuffer); 483 484 // Drain the output buffer, if it needs it. 485 if (outBuffer != null && outBuffer.hasRemaining()) 486 { 487 int l = Math.min(fragment.remaining(), outBuffer.remaining()); 488 fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l)); 489 outBuffer.position(outBuffer.position() + l); 490 } 491 492 if (!fragment.hasRemaining()) 493 { 494 if (state.isWriteState() || outBuffer.hasRemaining()) 495 return HandshakeStatus.NEED_WRAP; 496 else 497 return HandshakeStatus.NEED_UNWRAP; 498 } 499 500 outer_loop: 501 while (fragment.remaining() >= 4 && state.isWriteState()) 502 { 503 if (Debug.DEBUG) 504 logger.logv(Component.SSL_HANDSHAKE, "loop state={0}", state); 505 506 switch (state) 507 { 508 case WRITE_CLIENT_HELLO: 509 { 510 ClientHelloBuilder hello = new ClientHelloBuilder(); 511 AbstractSessionContext ctx = (AbstractSessionContext) 512 engine.contextImpl.engineGetClientSessionContext(); 513 continued = (SessionImpl) ctx.getSession(engine.getPeerHost(), 514 engine.getPeerPort()); 515 engine.session().setId(new Session.ID(new byte[0])); 516 Session.ID sid = engine.session().id(); 517 // If we have a session that we may want to continue, send 518 // that ID. 519 if (continued != null) 520 sid = continued.id(); 521 522 hello.setSessionId(sid.id()); 523 sentVersion = chooseVersion(); 524 hello.setVersion(sentVersion); 525 hello.setCipherSuites(getSuites()); 526 hello.setCompressionMethods(getCompressionMethods()); 527 Random r = hello.random(); 528 r.setGmtUnixTime(Util.unixTime()); 529 byte[] nonce = new byte[28]; 530 engine.session().random().nextBytes(nonce); 531 r.setRandomBytes(nonce); 532 clientRandom = r.copy(); 533 if (enableExtensions()) 534 { 535 List<Extension> extensions = new LinkedList<Extension>(); 536 MaxFragmentLength fraglen = maxFragmentLength(); 537 if (fraglen != null) 538 { 539 extensions.add(new Extension(Extension.Type.MAX_FRAGMENT_LENGTH, 540 fraglen)); 541 maxFragmentLengthSent = fraglen; 542 } 543 544 String host = engine.getPeerHost(); 545 if (host != null) 546 { 547 ServerName name 548 = new ServerName(NameType.HOST_NAME, host); 549 ServerNameList names 550 = new ServerNameList(Collections.singletonList(name)); 551 extensions.add(new Extension(Extension.Type.SERVER_NAME, 552 names)); 553 } 554 555 if (truncatedHMac()) 556 { 557 extensions.add(new Extension(Extension.Type.TRUNCATED_HMAC, 558 new TruncatedHMAC())); 559 truncatedHMacSent = true; 560 } 561 562 ExtensionList elist = new ExtensionList(extensions); 563 hello.setExtensions(elist.buffer()); 564 } 565 else 566 hello.setDisableExtensions(true); 567 568 if (Debug.DEBUG) 569 logger.logv(Component.SSL_HANDSHAKE, "{0}", hello); 570 571 fragment.putInt((Handshake.Type.CLIENT_HELLO.getValue() << 24) 572 | (hello.length() & 0xFFFFFF)); 573 outBuffer = hello.buffer(); 574 int l = Math.min(fragment.remaining(), outBuffer.remaining()); 575 fragment.put((ByteBuffer) outBuffer.duplicate() 576 .limit(outBuffer.position() + l)); 577 outBuffer.position(outBuffer.position() + l); 578 579 state = READ_SERVER_HELLO; 580 } 581 break; 582 583 case WRITE_CERTIFICATE: 584 { 585 java.security.cert.Certificate[] chain 586 = engine.session().getLocalCertificates(); 587 if (chain != null) 588 { 589 CertificateBuilder cert 590 = new CertificateBuilder(CertificateType.X509); 591 try 592 { 593 cert.setCertificates(Arrays.asList(chain)); 594 } 595 catch (CertificateException ce) 596 { 597 throw new AlertException(new Alert(Level.FATAL, 598 Description.INTERNAL_ERROR), 599 ce); 600 } 601 602 outBuffer = cert.buffer(); 603 604 fragment.putInt((Handshake.Type.CERTIFICATE.getValue() << 24) 605 | (cert.length() & 0xFFFFFF)); 606 607 int l = Math.min(fragment.remaining(), outBuffer.remaining()); 608 fragment.put((ByteBuffer) outBuffer.duplicate() 609 .limit(outBuffer.position() + l)); 610 outBuffer.position(outBuffer.position() + l); 611 } 612 state = WRITE_CLIENT_KEY_EXCHANGE; 613 } 614 break; 615 616 case WRITE_CLIENT_KEY_EXCHANGE: 617 { 618 KeyExchangeAlgorithm kea = engine.session().suite.keyExchangeAlgorithm(); 619 ClientKeyExchangeBuilder ckex 620 = new ClientKeyExchangeBuilder(engine.session().suite, 621 engine.session().version); 622 if (kea == DHE_DSS || kea == DHE_RSA || kea == DH_anon 623 || kea == DH_DSS || kea == DH_RSA) 624 { 625 assert(dhPair != null); 626 DHPublicKey pubkey = (DHPublicKey) dhPair.getPublic(); 627 ClientDiffieHellmanPublic pub 628 = new ClientDiffieHellmanPublic(pubkey.getY()); 629 ckex.setExchangeKeys(pub.buffer()); 630 } 631 if (kea == RSA || kea == RSA_PSK) 632 { 633 assert(keyExchange instanceof RSAGen); 634 assert(keyExchange.hasRun()); 635 if (keyExchange.thrown() != null) 636 throw new AlertException(new Alert(Level.FATAL, 637 Description.HANDSHAKE_FAILURE), 638 keyExchange.thrown()); 639 EncryptedPreMasterSecret epms 640 = new EncryptedPreMasterSecret(((RSAGen) keyExchange).encryptedSecret(), 641 engine.session().version); 642 if (kea == RSA) 643 ckex.setExchangeKeys(epms.buffer()); 644 else 645 { 646 String identity = getPSKIdentity(); 647 if (identity == null) 648 throw new SSLException("no pre-shared-key identity;" 649 + " set the security property" 650 + " \"jessie.client.psk.identity\""); 651 ClientRSA_PSKParameters params = 652 new ClientRSA_PSKParameters(identity, epms.buffer()); 653 ckex.setExchangeKeys(params.buffer()); 654 generatePSKSecret(identity, preMasterSecret, true); 655 } 656 } 657 if (kea == DHE_PSK) 658 { 659 assert(keyExchange instanceof ClientDHGen); 660 assert(dhPair != null); 661 String identity = getPSKIdentity(); 662 if (identity == null) 663 throw new SSLException("no pre-shared key identity; set" 664 + " the security property" 665 + " \"jessie.client.psk.identity\""); 666 DHPublicKey pubkey = (DHPublicKey) dhPair.getPublic(); 667 ClientDHE_PSKParameters params = 668 new ClientDHE_PSKParameters(identity, 669 new ClientDiffieHellmanPublic(pubkey.getY())); 670 ckex.setExchangeKeys(params.buffer()); 671 generatePSKSecret(identity, preMasterSecret, true); 672 } 673 if (kea == PSK) 674 { 675 String identity = getPSKIdentity(); 676 if (identity == null) 677 throw new SSLException("no pre-shared key identity; set" 678 + " the security property" 679 + " \"jessie.client.psk.identity\""); 680 generatePSKSecret(identity, null, true); 681 ClientPSKParameters params = new ClientPSKParameters(identity); 682 ckex.setExchangeKeys(params.buffer()); 683 } 684 if (kea == NONE) 685 { 686 Inflater inflater = null; 687 Deflater deflater = null; 688 if (compression == CompressionMethod.ZLIB) 689 { 690 inflater = new Inflater(); 691 deflater = new Deflater(); 692 } 693 inParams = new InputSecurityParameters(null, null, inflater, 694 engine.session(), 695 engine.session().suite); 696 outParams = new OutputSecurityParameters(null, null, deflater, 697 engine.session(), 698 engine.session().suite); 699 engine.session().privateData.masterSecret = new byte[0]; 700 } 701 702 if (Debug.DEBUG) 703 logger.logv(Component.SSL_HANDSHAKE, "{0}", ckex); 704 705 outBuffer = ckex.buffer(); 706 if (Debug.DEBUG) 707 logger.logv(Component.SSL_HANDSHAKE, "client kex buffer {0}", outBuffer); 708 fragment.putInt((Handshake.Type.CLIENT_KEY_EXCHANGE.getValue() << 24) 709 | (ckex.length() & 0xFFFFFF)); 710 int l = Math.min(fragment.remaining(), outBuffer.remaining()); 711 fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l)); 712 outBuffer.position(outBuffer.position() + l); 713 714 if (privateKey != null) 715 { 716 genCertVerify = new GenCertVerify(md5, sha); 717 tasks.add(genCertVerify); 718 state = WRITE_CERTIFICATE_VERIFY; 719 } 720 else 721 { 722 engine.changeCipherSpec(); 723 state = WRITE_FINISHED; 724 } 725 } 726 // Both states terminate in a NEED_TASK, or a need to change cipher 727 // specs; so we can't write any more messages here. 728 break outer_loop; 729 730 case WRITE_CERTIFICATE_VERIFY: 731 { 732 assert(genCertVerify != null); 733 assert(genCertVerify.hasRun()); 734 CertificateVerify verify = new CertificateVerify(genCertVerify.signed(), 735 engine.session().suite.signatureAlgorithm()); 736 737 outBuffer = verify.buffer(); 738 fragment.putInt((Handshake.Type.CERTIFICATE_VERIFY.getValue() << 24) 739 | (verify.length() & 0xFFFFFF)); 740 int l = Math.min(fragment.remaining(), outBuffer.remaining()); 741 fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l)); 742 outBuffer.position(outBuffer.position() + l); 743 744 // XXX This is a potential problem: we may not have drained 745 // outBuffer, but set the changeCipherSpec toggle. 746 engine.changeCipherSpec(); 747 state = WRITE_FINISHED; 748 } 749 break outer_loop; 750 751 case WRITE_FINISHED: 752 { 753 MessageDigest md5copy = null; 754 MessageDigest shacopy = null; 755 try 756 { 757 md5copy = (MessageDigest) md5.clone(); 758 shacopy = (MessageDigest) sha.clone(); 759 } 760 catch (CloneNotSupportedException cnse) 761 { 762 // We're improperly configured to use a non-cloneable 763 // md5/sha-1, OR there's a runtime bug. 764 throw new SSLException(cnse); 765 } 766 outBuffer 767 = generateFinished(md5copy, shacopy, true, 768 engine.session()); 769 770 fragment.putInt((Handshake.Type.FINISHED.getValue() << 24) 771 | outBuffer.remaining() & 0xFFFFFF); 772 773 int l = Math.min(outBuffer.remaining(), fragment.remaining()); 774 fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l)); 775 outBuffer.position(outBuffer.position() + l); 776 777 if (continuedSession) 778 state = DONE; 779 else 780 state = READ_FINISHED; 781 } 782 break; 783 784 default: 785 throw new IllegalStateException("invalid state: " + state); 786 } 787 } 788 789 if (!tasks.isEmpty()) 790 return HandshakeStatus.NEED_TASK; 791 if (state.isWriteState() || 792 (outBuffer != null && outBuffer.hasRemaining())) 793 return HandshakeStatus.NEED_WRAP; 794 if (state.isReadState()) 795 return HandshakeStatus.NEED_UNWRAP; 796 797 return HandshakeStatus.FINISHED; 798 } 799 800 /* (non-Javadoc) 801 * @see gnu.javax.net.ssl.provider.AbstractHandshake#status() 802 */ status()803 @Override HandshakeStatus status() 804 { 805 if (state.isReadState()) 806 return HandshakeStatus.NEED_UNWRAP; 807 if (state.isWriteState()) 808 return HandshakeStatus.NEED_WRAP; 809 return HandshakeStatus.FINISHED; 810 } 811 checkKeyExchange()812 @Override void checkKeyExchange() throws SSLException 813 { 814 // XXX implement. 815 } 816 817 /* (non-Javadoc) 818 * @see gnu.javax.net.ssl.provider.AbstractHandshake#handleV2Hello(java.nio.ByteBuffer) 819 */ handleV2Hello(ByteBuffer hello)820 @Override void handleV2Hello(ByteBuffer hello) throws SSLException 821 { 822 throw new SSLException("this should be impossible"); 823 } 824 chooseVersion()825 private ProtocolVersion chooseVersion() throws SSLException 826 { 827 // Select the highest enabled version, for our initial key exchange. 828 ProtocolVersion version = null; 829 for (String ver : engine.getEnabledProtocols()) 830 { 831 try 832 { 833 ProtocolVersion v = ProtocolVersion.forName(ver); 834 if (version == null || version.compareTo(v) < 0) 835 version = v; 836 } 837 catch (Exception x) 838 { 839 continue; 840 } 841 } 842 843 if (version == null) 844 throw new SSLException("no suitable enabled versions"); 845 846 return version; 847 } 848 getSuites()849 private List<CipherSuite> getSuites() throws SSLException 850 { 851 List<CipherSuite> suites = new LinkedList<CipherSuite>(); 852 for (String s : engine.getEnabledCipherSuites()) 853 { 854 CipherSuite suite = CipherSuite.forName(s); 855 if (suite != null) 856 suites.add(suite); 857 } 858 if (suites.isEmpty()) 859 throw new SSLException("no cipher suites enabled"); 860 return suites; 861 } 862 getCompressionMethods()863 private List<CompressionMethod> getCompressionMethods() 864 { 865 List<CompressionMethod> methods = new LinkedList<CompressionMethod>(); 866 GetSecurityPropertyAction gspa = new GetSecurityPropertyAction("jessie.enable.compression"); 867 if (Boolean.valueOf(AccessController.doPrivileged(gspa))) 868 methods.add(CompressionMethod.ZLIB); 869 methods.add(CompressionMethod.NULL); 870 return methods; 871 } 872 enableExtensions()873 private boolean enableExtensions() 874 { 875 GetSecurityPropertyAction action 876 = new GetSecurityPropertyAction("jessie.client.enable.extensions"); 877 return Boolean.valueOf(AccessController.doPrivileged(action)); 878 } 879 maxFragmentLength()880 private MaxFragmentLength maxFragmentLength() 881 { 882 GetSecurityPropertyAction action 883 = new GetSecurityPropertyAction("jessie.client.maxFragmentLength"); 884 String s = AccessController.doPrivileged(action); 885 if (s != null) 886 { 887 try 888 { 889 int len = Integer.parseInt(s); 890 switch (len) 891 { 892 case 9: 893 case (1 << 9): return MaxFragmentLength.LEN_2_9; 894 case 10: 895 case (1 << 10): return MaxFragmentLength.LEN_2_10; 896 case 11: 897 case (1 << 11): return MaxFragmentLength.LEN_2_11; 898 case 12: 899 case (1 << 12): return MaxFragmentLength.LEN_2_12; 900 } 901 } 902 catch (NumberFormatException nfe) 903 { 904 } 905 } 906 return null; 907 } 908 truncatedHMac()909 private boolean truncatedHMac() 910 { 911 GetSecurityPropertyAction action 912 = new GetSecurityPropertyAction("jessie.client.truncatedHMac"); 913 return Boolean.valueOf(AccessController.doPrivileged(action)); 914 } 915 getPSKIdentity()916 private String getPSKIdentity() 917 { 918 GetSecurityPropertyAction action 919 = new GetSecurityPropertyAction("jessie.client.psk.identity"); 920 return AccessController.doPrivileged(action); 921 } 922 923 // Delegated tasks. 924 925 class ParamsVerifier extends DelegatedTask 926 { 927 private final ByteBuffer paramsBuffer; 928 private final byte[] signature; 929 private boolean verified; 930 ParamsVerifier(ByteBuffer paramsBuffer, byte[] signature)931 ParamsVerifier(ByteBuffer paramsBuffer, byte[] signature) 932 { 933 this.paramsBuffer = paramsBuffer; 934 this.signature = signature; 935 } 936 implRun()937 public void implRun() 938 throws InvalidKeyException, NoSuchAlgorithmException, 939 SSLPeerUnverifiedException, SignatureException 940 { 941 java.security.Signature s 942 = java.security.Signature.getInstance(engine.session().suite 943 .signatureAlgorithm().algorithm()); 944 s.initVerify(engine.session().getPeerCertificates()[0]); 945 s.update(paramsBuffer); 946 verified = s.verify(signature); 947 synchronized (this) 948 { 949 notifyAll(); 950 } 951 } 952 verified()953 boolean verified() 954 { 955 return verified; 956 } 957 } 958 959 class ClientDHGen extends DelegatedTask 960 { 961 private final DHPublicKey serverKey; 962 private final DHParameterSpec params; 963 private final boolean full; 964 ClientDHGen(DHPublicKey serverKey, DHParameterSpec params, boolean full)965 ClientDHGen(DHPublicKey serverKey, DHParameterSpec params, boolean full) 966 { 967 this.serverKey = serverKey; 968 this.params = params; 969 this.full = full; 970 } 971 implRun()972 public void implRun() 973 throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, 974 SSLException 975 { 976 if (Debug.DEBUG) 977 logger.log(Component.SSL_DELEGATED_TASK, "running client DH phase"); 978 if (paramsVerifier != null) 979 { 980 synchronized (paramsVerifier) 981 { 982 try 983 { 984 while (!paramsVerifier.hasRun()) 985 paramsVerifier.wait(500); 986 } 987 catch (InterruptedException ie) 988 { 989 // Ignore. 990 } 991 } 992 } 993 KeyPairGenerator gen = KeyPairGenerator.getInstance("DH"); 994 gen.initialize(params, engine.session().random()); 995 dhPair = gen.generateKeyPair(); 996 if (Debug.DEBUG_KEY_EXCHANGE) 997 logger.logv(Component.SSL_KEY_EXCHANGE, 998 "client keys public:{0} private:{1}", dhPair.getPublic(), 999 dhPair.getPrivate()); 1000 1001 initDiffieHellman((DHPrivateKey) dhPair.getPrivate(), engine.session().random()); 1002 1003 // We have enough info to do the full key exchange; so let's do it. 1004 DHPhase phase = new DHPhase(serverKey, full); 1005 phase.run(); 1006 if (phase.thrown() != null) 1007 throw new SSLException(phase.thrown()); 1008 } 1009 serverKey()1010 DHPublicKey serverKey() 1011 { 1012 return serverKey; 1013 } 1014 } 1015 1016 class CertLoader extends DelegatedTask 1017 { 1018 private final List<String> keyTypes; 1019 private final List<X500Principal> issuers; 1020 CertLoader(List<String> keyTypes, List<X500Principal> issuers)1021 CertLoader(List<String> keyTypes, List<X500Principal> issuers) 1022 { 1023 this.keyTypes = keyTypes; 1024 this.issuers = issuers; 1025 } 1026 implRun()1027 public void implRun() 1028 { 1029 X509ExtendedKeyManager km = engine.contextImpl.keyManager; 1030 if (km == null) 1031 return; 1032 keyAlias = km.chooseEngineClientAlias(keyTypes.toArray(new String[keyTypes.size()]), 1033 issuers.toArray(new X500Principal[issuers.size()]), 1034 engine); 1035 engine.session().setLocalCertificates(km.getCertificateChain(keyAlias)); 1036 privateKey = km.getPrivateKey(keyAlias); 1037 } 1038 } 1039 1040 class RSAGen extends DelegatedTask 1041 { 1042 private byte[] encryptedPreMasterSecret; 1043 private final boolean full; 1044 RSAGen()1045 RSAGen() 1046 { 1047 this(true); 1048 } 1049 RSAGen(boolean full)1050 RSAGen(boolean full) 1051 { 1052 this.full = full; 1053 } 1054 implRun()1055 public void implRun() 1056 throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, 1057 NoSuchAlgorithmException, NoSuchPaddingException, 1058 SSLException 1059 { 1060 if (certVerifier != null) 1061 { 1062 synchronized (certVerifier) 1063 { 1064 try 1065 { 1066 while (!certVerifier.hasRun()) 1067 certVerifier.wait(500); 1068 } 1069 catch (InterruptedException ie) 1070 { 1071 // Ignore. 1072 } 1073 } 1074 } 1075 preMasterSecret = new byte[48]; 1076 engine.session().random().nextBytes(preMasterSecret); 1077 preMasterSecret[0] = (byte) sentVersion.major(); 1078 preMasterSecret[1] = (byte) sentVersion.minor(); 1079 Cipher rsa = Cipher.getInstance("RSA"); 1080 java.security.cert.Certificate cert 1081 = engine.session().getPeerCertificates()[0]; 1082 if (cert instanceof X509Certificate) 1083 { 1084 boolean[] keyUsage = ((X509Certificate) cert).getKeyUsage(); 1085 if (keyUsage != null && !keyUsage[2]) 1086 throw new InvalidKeyException("certificate's keyUsage does not permit keyEncipherment"); 1087 } 1088 rsa.init(Cipher.ENCRYPT_MODE, cert.getPublicKey()); 1089 encryptedPreMasterSecret = rsa.doFinal(preMasterSecret); 1090 1091 // Generate our session keys, because we can. 1092 if (full) 1093 { 1094 generateMasterSecret(clientRandom, serverRandom, engine.session()); 1095 byte[][] keys = generateKeys(clientRandom, serverRandom, engine.session()); 1096 setupSecurityParameters(keys, true, engine, compression); 1097 } 1098 } 1099 encryptedSecret()1100 byte[] encryptedSecret() 1101 { 1102 return encryptedPreMasterSecret; 1103 } 1104 } 1105 1106 class GenCertVerify extends DelegatedTask 1107 { 1108 private final MessageDigest md5, sha; 1109 private byte[] signed; 1110 GenCertVerify(MessageDigest md5, MessageDigest sha)1111 GenCertVerify(MessageDigest md5, MessageDigest sha) 1112 { 1113 try 1114 { 1115 this.md5 = (MessageDigest) md5.clone(); 1116 this.sha = (MessageDigest) sha.clone(); 1117 } 1118 catch (CloneNotSupportedException cnse) 1119 { 1120 // Our message digests *should* be cloneable. 1121 throw new Error(cnse); 1122 } 1123 } 1124 implRun()1125 public void implRun() 1126 throws InvalidKeyException, NoSuchAlgorithmException, SignatureException 1127 { 1128 byte[] toSign; 1129 if (engine.session().version == ProtocolVersion.SSL_3) 1130 { 1131 toSign = genV3CertificateVerify(md5, sha, engine.session()); 1132 } 1133 else 1134 { 1135 if (engine.session().suite.signatureAlgorithm() == SignatureAlgorithm.RSA) 1136 toSign = Util.concat(md5.digest(), sha.digest()); 1137 else 1138 toSign = sha.digest(); 1139 } 1140 1141 java.security.Signature sig = 1142 java.security.Signature.getInstance(engine.session().suite.signatureAlgorithm().name()); 1143 sig.initSign(privateKey); 1144 sig.update(toSign); 1145 signed = sig.sign(); 1146 } 1147 signed()1148 byte[] signed() 1149 { 1150 return signed; 1151 } 1152 } 1153 } 1154