1 // 2 // Copyright (c) ZeroC, Inc. All rights reserved. 3 // 4 5 package IceSSL; 6 7 import java.io.InputStream; 8 import java.util.ArrayList; 9 import java.util.List; 10 import java.security.cert.*; 11 import javax.net.ssl.SSLParameters; 12 13 class SSLEngine 14 { SSLEngine(IceInternal.ProtocolPluginFacade facade)15 SSLEngine(IceInternal.ProtocolPluginFacade facade) 16 { 17 _communicator = facade.getCommunicator(); 18 _logger = _communicator.getLogger(); 19 _facade = facade; 20 _securityTraceLevel = _communicator.getProperties().getPropertyAsIntWithDefault("IceSSL.Trace.Security", 0); 21 _securityTraceCategory = "Security"; 22 _trustManager = new TrustManager(_communicator); 23 } 24 initialize()25 void initialize() 26 { 27 if(_initialized) 28 { 29 return; 30 } 31 32 final String prefix = "IceSSL."; 33 Ice.Properties properties = communicator().getProperties(); 34 35 // 36 // Parse the cipher list. 37 // 38 String ciphers = properties.getProperty(prefix + "Ciphers"); 39 if(ciphers.length() > 0) 40 { 41 parseCiphers(ciphers); 42 } 43 44 String[] protocols = properties.getPropertyAsList(prefix + "Protocols"); 45 if(protocols.length != 0) 46 { 47 java.util.ArrayList<String> l = new java.util.ArrayList<String>(); 48 for(String prot : protocols) 49 { 50 String s = prot.toUpperCase(); 51 if(s.equals("SSL3") || s.equals("SSLV3")) 52 { 53 l.add("SSLv3"); 54 } 55 else if(s.equals("TLS") || s.equals("TLS1") || s.equals("TLSV1") || s.equals("TLS1_0") || 56 s.equals("TLSV1_0")) 57 { 58 l.add("TLSv1"); 59 } 60 else if(s.equals("TLS1_1") || s.equals("TLSV1_1")) 61 { 62 l.add("TLSv1.1"); 63 } 64 else if(s.equals("TLS1_2") || s.equals("TLSV1_2")) 65 { 66 l.add("TLSv1.2"); 67 } 68 else if(s.equals("TLS1_3") || s.equals("TLSV1_3")) 69 { 70 l.add("TLSv1.3"); 71 } 72 else 73 { 74 Ice.PluginInitializationException e = new Ice.PluginInitializationException(); 75 e.reason = "IceSSL: unrecognized protocol `" + prot + "'"; 76 throw e; 77 } 78 } 79 _protocols = new String[l.size()]; 80 l.toArray(_protocols); 81 } 82 83 // 84 // CheckCertName determines whether we compare the name in a peer's 85 // certificate against its hostname. 86 // 87 _checkCertName = properties.getPropertyAsIntWithDefault(prefix + "CheckCertName", 0) > 0; 88 89 // 90 // VerifyDepthMax establishes the maximum length of a peer's certificate 91 // chain, including the peer's certificate. A value of 0 means there is 92 // no maximum. 93 // 94 _verifyDepthMax = properties.getPropertyAsIntWithDefault(prefix + "VerifyDepthMax", 3); 95 96 // 97 // VerifyPeer determines whether certificate validation failures abort a connection. 98 // 99 _verifyPeer = properties.getPropertyAsIntWithDefault("IceSSL.VerifyPeer", 2); 100 101 // 102 // Check for a certificate verifier. 103 // 104 final String certVerifierClass = properties.getProperty(prefix + "CertVerifier"); 105 if(certVerifierClass.length() > 0) 106 { 107 if(_verifier != null) 108 { 109 Ice.PluginInitializationException e = new Ice.PluginInitializationException(); 110 e.reason = "IceSSL: certificate verifier already installed"; 111 throw e; 112 } 113 114 Class<?> cls = null; 115 try 116 { 117 cls = _facade.findClass(certVerifierClass); 118 } 119 catch(Throwable ex) 120 { 121 throw new Ice.PluginInitializationException( 122 "IceSSL: unable to load certificate verifier class " + certVerifierClass, ex); 123 } 124 125 try 126 { 127 _verifier = (CertificateVerifier)cls.getDeclaredConstructor().newInstance(); 128 } 129 catch(Throwable ex) 130 { 131 throw new Ice.PluginInitializationException( 132 "IceSSL: unable to instantiate certificate verifier class " + certVerifierClass, ex); 133 } 134 } 135 136 // 137 // Check for a password callback. 138 // 139 final String passwordCallbackClass = properties.getProperty(prefix + "PasswordCallback"); 140 if(passwordCallbackClass.length() > 0) 141 { 142 if(_passwordCallback != null) 143 { 144 Ice.PluginInitializationException e = new Ice.PluginInitializationException(); 145 e.reason = "IceSSL: password callback already installed"; 146 throw e; 147 } 148 149 Class<?> cls = null; 150 try 151 { 152 cls = _facade.findClass(passwordCallbackClass); 153 } 154 catch(Throwable ex) 155 { 156 throw new Ice.PluginInitializationException( 157 "IceSSL: unable to load password callback class " + passwordCallbackClass, ex); 158 } 159 160 try 161 { 162 _passwordCallback = (PasswordCallback)cls.getDeclaredConstructor().newInstance(); 163 } 164 catch(Throwable ex) 165 { 166 throw new Ice.PluginInitializationException( 167 "IceSSL: unable to instantiate password callback class " + passwordCallbackClass, ex); 168 } 169 } 170 171 // 172 // If the user doesn't supply an SSLContext, we need to create one based 173 // on property settings. 174 // 175 if(_context == null) 176 { 177 try 178 { 179 // 180 // Check for a default directory. We look in this directory for 181 // files mentioned in the configuration. 182 // 183 _defaultDir = properties.getProperty(prefix + "DefaultDir"); 184 185 // 186 // We need a SecureRandom object. 187 // 188 // NOTE: The JDK recommends obtaining a SecureRandom object like this: 189 // 190 // java.security.SecureRandom rand = java.security.SecureRandom.getInstance("SHA1PRNG"); 191 // 192 // However, there is a bug (6202721) which causes it to always use /dev/random, 193 // which can lead to long delays at program startup. The workaround is to use 194 // the default constructor. 195 // 196 java.security.SecureRandom rand = new java.security.SecureRandom(); 197 198 // 199 // Check for seed data for the random number generator. 200 // 201 final String seedFiles = properties.getProperty(prefix + "Random"); 202 if(seedFiles.length() > 0) 203 { 204 final String[] arr = seedFiles.split(java.io.File.pathSeparator); 205 for(String file : arr) 206 { 207 try 208 { 209 java.io.InputStream seedStream = openResource(file); 210 if(seedStream == null) 211 { 212 Ice.PluginInitializationException e = new Ice.PluginInitializationException(); 213 e.reason = "IceSSL: random seed file not found:\n" + file; 214 throw e; 215 } 216 217 _seeds.add(seedStream); 218 } 219 catch(java.io.IOException ex) 220 { 221 throw new Ice.PluginInitializationException( 222 "IceSSL: unable to access random seed file:\n" + file, ex); 223 } 224 } 225 } 226 227 if(!_seeds.isEmpty()) 228 { 229 byte[] seed = null; 230 int start = 0; 231 for(InputStream in : _seeds) 232 { 233 try 234 { 235 int num = in.available(); 236 if(seed == null) 237 { 238 seed = new byte[num]; 239 } 240 else 241 { 242 byte[] tmp = new byte[seed.length + num]; 243 System.arraycopy(seed, 0, tmp, 0, seed.length); 244 start = seed.length; 245 seed = tmp; 246 } 247 in.read(seed, start, num); 248 } 249 catch(java.io.IOException ex) 250 { 251 throw new Ice.PluginInitializationException("IceSSL: error while reading random seed", ex); 252 } 253 finally 254 { 255 try 256 { 257 in.close(); 258 } 259 catch(java.io.IOException e) 260 { 261 // Ignore. 262 } 263 } 264 } 265 rand.setSeed(seed); 266 } 267 _seeds.clear(); 268 269 // 270 // We call nextInt() in order to force the object to perform any time-consuming 271 // initialization tasks now. 272 // 273 rand.nextInt(); 274 275 // 276 // The keystore holds private keys and associated certificates. 277 // 278 String keystorePath = properties.getProperty(prefix + "Keystore"); 279 280 // 281 // The password for the keys. 282 // 283 String password = properties.getProperty(prefix + "Password"); 284 285 // 286 // The password for the keystore. 287 // 288 String keystorePassword = properties.getProperty(prefix + "KeystorePassword"); 289 290 // 291 // The default keystore type is usually "JKS", but the legal values are determined 292 // by the JVM implementation. Other possibilities include "PKCS12" and "BKS". 293 // 294 final String defaultType = java.security.KeyStore.getDefaultType(); 295 final String keystoreType = properties.getPropertyWithDefault(prefix + "KeystoreType", defaultType); 296 297 // 298 // The alias of the key to use in authentication. 299 // 300 String alias = properties.getProperty(prefix + "Alias"); 301 boolean overrideAlias = !alias.isEmpty(); // Always use the configured alias 302 303 // 304 // The truststore holds the certificates of trusted CAs. 305 // 306 String truststorePath = properties.getProperty(prefix + "Truststore"); 307 308 // 309 // The password for the truststore. 310 // 311 String truststorePassword = properties.getProperty(prefix + "TruststorePassword"); 312 313 // 314 // The default truststore type is usually "JKS", but the legal values are determined 315 // by the JVM implementation. Other possibilities include "PKCS12" and "BKS". 316 // 317 final String truststoreType = 318 properties.getPropertyWithDefault(prefix + "TruststoreType", 319 java.security.KeyStore.getDefaultType()); 320 321 // 322 // Collect the key managers. 323 // 324 javax.net.ssl.KeyManager[] keyManagers = null; 325 java.security.KeyStore keys = null; 326 if(_keystoreStream != null || keystorePath.length() > 0) 327 { 328 java.io.InputStream keystoreStream = null; 329 try 330 { 331 if(_keystoreStream != null) 332 { 333 keystoreStream = _keystoreStream; 334 } 335 else 336 { 337 keystoreStream = openResource(keystorePath); 338 if(keystoreStream == null) 339 { 340 Ice.PluginInitializationException e = new Ice.PluginInitializationException(); 341 e.reason = "IceSSL: keystore not found:\n" + keystorePath; 342 throw e; 343 } 344 } 345 346 keys = java.security.KeyStore.getInstance(keystoreType); 347 char[] passwordChars = null; 348 if(keystorePassword.length() > 0) 349 { 350 passwordChars = keystorePassword.toCharArray(); 351 } 352 else if(_passwordCallback != null) 353 { 354 passwordChars = _passwordCallback.getKeystorePassword(); 355 } 356 else if(keystoreType.equals("BKS") || keystoreType.equals("PKCS12")) 357 { 358 // Bouncy Castle or PKCS12 does not permit null passwords. 359 passwordChars = new char[0]; 360 } 361 362 keys.load(keystoreStream, passwordChars); 363 364 if(passwordChars != null) 365 { 366 java.util.Arrays.fill(passwordChars, '\0'); 367 } 368 keystorePassword = null; 369 } 370 catch(java.io.IOException ex) 371 { 372 throw new Ice.PluginInitializationException( 373 "IceSSL: unable to load keystore:\n" + keystorePath, ex); 374 } 375 finally 376 { 377 if(keystoreStream != null) 378 { 379 try 380 { 381 keystoreStream.close(); 382 } 383 catch(java.io.IOException e) 384 { 385 // Ignore. 386 } 387 } 388 } 389 390 String algorithm = javax.net.ssl.KeyManagerFactory.getDefaultAlgorithm(); 391 javax.net.ssl.KeyManagerFactory kmf = javax.net.ssl.KeyManagerFactory.getInstance(algorithm); 392 char[] passwordChars = new char[0]; // This password cannot be null. 393 if(password.length() > 0) 394 { 395 passwordChars = password.toCharArray(); 396 } 397 else if(_passwordCallback != null) 398 { 399 passwordChars = _passwordCallback.getPassword(alias); 400 } 401 kmf.init(keys, passwordChars); 402 if(passwordChars.length > 0) 403 { 404 java.util.Arrays.fill(passwordChars, '\0'); 405 } 406 password = null; 407 keyManagers = kmf.getKeyManagers(); 408 409 // 410 // If no alias is specified, we look for the first key entry in the key store. 411 // 412 // This is required to force the key manager to always choose a certificate 413 // even if there's no certificate signed by any of the CA names sent by the 414 // server. Ice servers might indeed not always send the CA names of their 415 // trusted roots. 416 // 417 if(alias.isEmpty()) 418 { 419 for(java.util.Enumeration<String> e = keys.aliases(); e.hasMoreElements();) 420 { 421 String a = e.nextElement(); 422 if(keys.isKeyEntry(a)) 423 { 424 alias = a; 425 break; 426 } 427 } 428 } 429 430 if(!alias.isEmpty()) 431 { 432 // 433 // If the user selected a specific alias, we need to wrap the key managers 434 // in order to return the desired alias. 435 // 436 if(!keys.isKeyEntry(alias)) 437 { 438 Ice.PluginInitializationException e = new Ice.PluginInitializationException(); 439 e.reason = "IceSSL: keystore does not contain an entry with alias `" + alias + "'"; 440 throw e; 441 } 442 443 for(int i = 0; i < keyManagers.length; ++i) 444 { 445 keyManagers[i] = new X509KeyManagerI((javax.net.ssl.X509ExtendedKeyManager)keyManagers[i], 446 alias, overrideAlias); 447 } 448 } 449 } 450 451 // 452 // Load the truststore. 453 // 454 java.security.KeyStore ts = null; 455 if(_truststoreStream != null || truststorePath.length() > 0) 456 { 457 // 458 // If the trust store and the key store are the same input 459 // stream or file, don't create another key store. 460 // 461 if((_truststoreStream != null && _truststoreStream == _keystoreStream) || 462 (truststorePath.length() > 0 && truststorePath.equals(keystorePath))) 463 { 464 assert keys != null; 465 ts = keys; 466 } 467 else 468 { 469 java.io.InputStream truststoreStream = null; 470 try 471 { 472 if(_truststoreStream != null) 473 { 474 truststoreStream = _truststoreStream; 475 } 476 else 477 { 478 truststoreStream = openResource(truststorePath); 479 if(truststoreStream == null) 480 { 481 Ice.PluginInitializationException e = new Ice.PluginInitializationException(); 482 e.reason = "IceSSL: truststore not found:\n" + truststorePath; 483 throw e; 484 } 485 } 486 487 ts = java.security.KeyStore.getInstance(truststoreType); 488 489 char[] passwordChars = null; 490 if(truststorePassword.length() > 0) 491 { 492 passwordChars = truststorePassword.toCharArray(); 493 } 494 else if(_passwordCallback != null) 495 { 496 passwordChars = _passwordCallback.getTruststorePassword(); 497 } 498 else if(truststoreType.equals("BKS") || truststoreType.equals("PKCS12")) 499 { 500 // Bouncy Castle or PKCS12 does not permit null passwords. 501 passwordChars = new char[0]; 502 } 503 504 ts.load(truststoreStream, passwordChars); 505 506 if(passwordChars != null) 507 { 508 java.util.Arrays.fill(passwordChars, '\0'); 509 } 510 truststorePassword = null; 511 } 512 catch(java.io.IOException ex) 513 { 514 throw new Ice.PluginInitializationException( 515 "IceSSL: unable to load truststore:\n" + truststorePath, ex); 516 } 517 finally 518 { 519 if(truststoreStream != null) 520 { 521 try 522 { 523 truststoreStream.close(); 524 } 525 catch(java.io.IOException e) 526 { 527 // Ignore. 528 } 529 } 530 } 531 } 532 } 533 534 // 535 // Collect the trust managers. Use IceSSL.Truststore if 536 // specified, otherwise use the Java root CAs if 537 // Ice.Use.PlatformCAs is enabled. If none of these are enabled, 538 // use the keystore or a dummy trust manager which rejects any 539 // certificate. 540 // 541 javax.net.ssl.TrustManager[] trustManagers = null; 542 { 543 String algorithm = javax.net.ssl.TrustManagerFactory.getDefaultAlgorithm(); 544 javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance(algorithm); 545 java.security.KeyStore trustStore = null; 546 if(ts != null) 547 { 548 trustStore = ts; 549 } 550 else if(properties.getPropertyAsInt("IceSSL.UsePlatformCAs") <= 0) 551 { 552 if(keys != null) 553 { 554 trustStore = keys; 555 } 556 else 557 { 558 trustManagers = new javax.net.ssl.TrustManager[] 559 { 560 new javax.net.ssl.X509TrustManager() 561 { 562 @Override 563 public void 564 checkClientTrusted(X509Certificate[] chain, String authType) 565 throws CertificateException 566 { 567 throw new CertificateException("no trust anchors"); 568 } 569 570 @Override 571 public void 572 checkServerTrusted(X509Certificate[] chain, String authType) 573 throws CertificateException 574 { 575 throw new CertificateException("no trust anchors"); 576 } 577 578 @Override 579 public X509Certificate[] 580 getAcceptedIssuers() 581 { 582 return new X509Certificate[0]; 583 } 584 } 585 }; 586 } 587 } 588 else 589 { 590 trustStore = null; 591 } 592 593 // 594 // Attempting to establish an outgoing connection with an empty truststore can 595 // cause hangs that eventually result in an exception such as: 596 // 597 // java.security.InvalidAlgorithmParameterException: the trustAnchors parameter 598 // must be non-empty 599 // 600 if(trustStore != null && trustStore.size() == 0) 601 { 602 throw new Ice.PluginInitializationException("IceSSL: truststore is empty"); 603 } 604 605 if(trustManagers == null) 606 { 607 tmf.init(trustStore); 608 trustManagers = tmf.getTrustManagers(); 609 } 610 assert(trustManagers != null); 611 } 612 613 // 614 // Create a certificate path validator to build the full 615 // certificate chain of the peer certificate. NOTE: this must 616 // be done before wrapping the trust manager since our wrappers 617 // return an empty list of accepted issuers. 618 // 619 _validator = CertPathValidator.getInstance("PKIX"); 620 java.util.Set<TrustAnchor> anchors = new java.util.HashSet<TrustAnchor>(); 621 for(javax.net.ssl.TrustManager tm : trustManagers) 622 { 623 X509Certificate[] certs = ((javax.net.ssl.X509TrustManager)tm).getAcceptedIssuers(); 624 for(X509Certificate cert : certs) 625 { 626 if(cert.getBasicConstraints() >= 0) // Only add CAs 627 { 628 anchors.add(new TrustAnchor(cert, null)); 629 } 630 } 631 } 632 if(!anchors.isEmpty()) 633 { 634 _validatorParams = new PKIXParameters(anchors); 635 _validatorParams.setRevocationEnabled(false); 636 } 637 638 // 639 // Wrap each trust manager. 640 // 641 for(int i = 0; i < trustManagers.length; ++i) 642 { 643 trustManagers[i] = new X509TrustManagerI(this, (javax.net.ssl.X509TrustManager)trustManagers[i]); 644 } 645 646 // 647 // Initialize the SSL context. 648 // 649 _context = javax.net.ssl.SSLContext.getInstance("TLS"); 650 _context.init(keyManagers, trustManagers, rand); 651 } 652 catch(java.security.GeneralSecurityException ex) 653 { 654 throw new Ice.PluginInitializationException("IceSSL: unable to initialize context", ex); 655 } 656 } 657 658 // 659 // Clear cached input streams. 660 // 661 _seeds.clear(); 662 _keystoreStream = null; 663 _truststoreStream = null; 664 665 _initialized = true; 666 } 667 getVerifiedCertificateChain(Certificate[] chain)668 Certificate[] getVerifiedCertificateChain(Certificate[] chain) 669 { 670 if(_validator == null) 671 { 672 return chain; // The user provided a custom SSLContext 673 } 674 675 if(_validatorParams == null) 676 { 677 return null; // Couldn't validate the given certificate chain, no trust anchors configured. 678 } 679 680 List<Certificate> certs = new ArrayList<Certificate>(java.util.Arrays.asList(chain)); 681 try 682 { 683 CertPath path = CertificateFactory.getInstance("X.509").generateCertPath(certs); 684 CertPathValidatorResult result = _validator.validate(path, _validatorParams); 685 Certificate root = ((PKIXCertPathValidatorResult)result).getTrustAnchor().getTrustedCert(); 686 if(!root.equals(chain[chain.length - 1])) // Only add the root certificate if it's not already in the chain 687 { 688 certs.add(root); 689 } 690 return certs.toArray(new Certificate[certs.size()]); 691 } 692 catch(Exception ex) 693 { 694 return null; // Couldn't validate the given certificate chain. 695 } 696 } 697 context(javax.net.ssl.SSLContext context)698 void context(javax.net.ssl.SSLContext context) 699 { 700 if(_initialized) 701 { 702 Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); 703 ex.reason = "IceSSL: plug-in is already initialized"; 704 throw ex; 705 } 706 707 assert(_context == null); 708 _context = context; 709 } 710 context()711 javax.net.ssl.SSLContext context() 712 { 713 return _context; 714 } 715 setCertificateVerifier(CertificateVerifier verifier)716 void setCertificateVerifier(CertificateVerifier verifier) 717 { 718 _verifier = verifier; 719 } 720 getCertificateVerifier()721 CertificateVerifier getCertificateVerifier() 722 { 723 return _verifier; 724 } 725 setPasswordCallback(PasswordCallback callback)726 void setPasswordCallback(PasswordCallback callback) 727 { 728 _passwordCallback = callback; 729 } 730 getPasswordCallback()731 PasswordCallback getPasswordCallback() 732 { 733 return _passwordCallback; 734 } 735 setKeystoreStream(java.io.InputStream stream)736 void setKeystoreStream(java.io.InputStream stream) 737 { 738 if(_initialized) 739 { 740 Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); 741 ex.reason = "IceSSL: plugin is already initialized"; 742 throw ex; 743 } 744 745 _keystoreStream = stream; 746 } 747 setTruststoreStream(java.io.InputStream stream)748 void setTruststoreStream(java.io.InputStream stream) 749 { 750 if(_initialized) 751 { 752 Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); 753 ex.reason = "IceSSL: plugin is already initialized"; 754 throw ex; 755 } 756 757 _truststoreStream = stream; 758 } 759 addSeedStream(java.io.InputStream stream)760 void addSeedStream(java.io.InputStream stream) 761 { 762 _seeds.add(stream); 763 } 764 securityTraceLevel()765 int securityTraceLevel() 766 { 767 return _securityTraceLevel; 768 } 769 securityTraceCategory()770 String securityTraceCategory() 771 { 772 return _securityTraceCategory; 773 } 774 initialized()775 boolean initialized() 776 { 777 return _initialized; 778 } 779 createSSLEngine(boolean incoming, String host, int port)780 javax.net.ssl.SSLEngine createSSLEngine(boolean incoming, String host, int port) 781 { 782 javax.net.ssl.SSLEngine engine; 783 try 784 { 785 if(host != null) 786 { 787 engine = _context.createSSLEngine(host, port); 788 } 789 else 790 { 791 engine = _context.createSSLEngine(); 792 } 793 engine.setUseClientMode(!incoming); 794 } 795 catch(Exception ex) 796 { 797 throw new Ice.SecurityException("IceSSL: couldn't create SSL engine", ex); 798 } 799 800 String[] cipherSuites = filterCiphers(engine.getSupportedCipherSuites(), engine.getEnabledCipherSuites()); 801 try 802 { 803 engine.setEnabledCipherSuites(cipherSuites); 804 } 805 catch(IllegalArgumentException ex) 806 { 807 throw new Ice.SecurityException("IceSSL: invalid ciphersuite", ex); 808 } 809 810 if(_securityTraceLevel >= 1) 811 { 812 StringBuilder s = new StringBuilder(128); 813 s.append("enabling SSL ciphersuites:"); 814 for(String suite : cipherSuites) 815 { 816 s.append("\n "); 817 s.append(suite); 818 } 819 _logger.trace(_securityTraceCategory, s.toString()); 820 } 821 822 if(_protocols != null) 823 { 824 try 825 { 826 engine.setEnabledProtocols(_protocols); 827 } 828 catch(IllegalArgumentException ex) 829 { 830 throw new Ice.SecurityException("IceSSL: invalid protocol", ex); 831 } 832 } 833 else 834 { 835 // 836 // Disable SSLv3 837 // 838 List<String> protocols = new ArrayList<String>(java.util.Arrays.asList(engine.getEnabledProtocols())); 839 if(protocols.remove("SSLv3")) 840 { 841 engine.setEnabledProtocols(protocols.toArray(new String[protocols.size()])); 842 } 843 } 844 845 if(incoming) 846 { 847 if(_verifyPeer == 0) 848 { 849 engine.setWantClientAuth(false); 850 engine.setNeedClientAuth(false); 851 } 852 else if(_verifyPeer == 1) 853 { 854 engine.setWantClientAuth(true); 855 } 856 else 857 { 858 engine.setNeedClientAuth(true); 859 } 860 } 861 else 862 { 863 // 864 // Enable the HTTPS hostname verification algorithm. This requires Android API 24 therefore we 865 // don't use it on Android in Java Compat. 866 // 867 if(_checkCertName && _verifyPeer > 0 && host != null && !IceInternal.Util.isAndroid()) 868 { 869 SSLParameters params = new SSLParameters(); 870 params.setEndpointIdentificationAlgorithm("HTTPS"); 871 engine.setSSLParameters(params); 872 } 873 } 874 875 try 876 { 877 engine.beginHandshake(); 878 } 879 catch(javax.net.ssl.SSLException ex) 880 { 881 throw new Ice.SecurityException("IceSSL: handshake error", ex); 882 } 883 884 return engine; 885 } 886 filterCiphers(String[] supportedCiphers, String[] defaultCiphers)887 String[] filterCiphers(String[] supportedCiphers, String[] defaultCiphers) 888 { 889 java.util.LinkedList<String> result = new java.util.LinkedList<String>(); 890 if(_allCiphers) 891 { 892 for(String cipher : supportedCiphers) 893 { 894 result.add(cipher); 895 } 896 } 897 else if(!_noCiphers) 898 { 899 for(String cipher : defaultCiphers) 900 { 901 result.add(cipher); 902 } 903 } 904 905 if(_ciphers != null) 906 { 907 for(CipherExpression ce : _ciphers) 908 { 909 if(ce.not) 910 { 911 java.util.Iterator<String> e = result.iterator(); 912 while(e.hasNext()) 913 { 914 String cipher = e.next(); 915 if(ce.cipher != null) 916 { 917 if(ce.cipher.equals(cipher)) 918 { 919 e.remove(); 920 } 921 } 922 else 923 { 924 assert(ce.re != null); 925 java.util.regex.Matcher m = ce.re.matcher(cipher); 926 if(m.find()) 927 { 928 e.remove(); 929 } 930 } 931 } 932 } 933 else 934 { 935 if(ce.cipher != null) 936 { 937 result.add(0, ce.cipher); 938 } 939 else 940 { 941 assert(ce.re != null); 942 for(String cipher : supportedCiphers) 943 { 944 java.util.regex.Matcher m = ce.re.matcher(cipher); 945 if(m.find()) 946 { 947 result.add(0, cipher); 948 } 949 } 950 } 951 } 952 } 953 } 954 955 String[] arr = new String[result.size()]; 956 result.toArray(arr); 957 return arr; 958 } 959 protocols()960 String[] protocols() 961 { 962 return _protocols; 963 } 964 traceConnection(String desc, javax.net.ssl.SSLEngine engine, boolean incoming)965 void traceConnection(String desc, javax.net.ssl.SSLEngine engine, boolean incoming) 966 { 967 javax.net.ssl.SSLSession session = engine.getSession(); 968 String msg = "SSL summary for " + (incoming ? "incoming" : "outgoing") + " connection\n" + 969 "cipher = " + session.getCipherSuite() + "\n" + 970 "protocol = " + session.getProtocol() + "\n" + desc; 971 _logger.trace(_securityTraceCategory, msg); 972 } 973 communicator()974 Ice.Communicator communicator() 975 { 976 return _communicator; 977 } 978 verifyPeer(String address, ConnectionInfo info, String desc)979 void verifyPeer(String address, ConnectionInfo info, String desc) 980 { 981 if(!info.incoming && _verifyPeer > 0) 982 { 983 // 984 // IceSSL.VerifyPeer is translated into the proper SSLEngine configuration 985 // for a server, but we have to do it ourselves for a client. 986 // 987 if(!info.verified) 988 { 989 throw new Ice.SecurityException("IceSSL: server did not supply a certificate"); 990 } 991 992 assert(info.certs != null && info.certs.length > 0); 993 994 // 995 // If IceSSL.CheckCertName is enabled on Android, we have to handle this manually. 996 // 997 if(_checkCertName && address.length() > 0 && IceInternal.Util.isAndroid()) 998 { 999 // 1000 // For an outgoing connection, we compare the proxy address (if any) against 1001 // fields in the server's certificate (if any). 1002 // 1003 X509Certificate cert = (X509Certificate)info.certs[0]; 1004 1005 // 1006 // Extract the IP addresses and the DNS names from the subject 1007 // alternative names. 1008 // 1009 java.util.ArrayList<String> ipAddresses = new java.util.ArrayList<String>(); 1010 java.util.ArrayList<String> dnsNames = new java.util.ArrayList<String>(); 1011 try 1012 { 1013 java.util.Collection<java.util.List<?> > subjectAltNames = cert.getSubjectAlternativeNames(); 1014 if(subjectAltNames != null) 1015 { 1016 for(java.util.List<?> l : subjectAltNames) 1017 { 1018 assert(!l.isEmpty()); 1019 Integer n = (Integer)l.get(0); 1020 if(n.intValue() == 7) 1021 { 1022 ipAddresses.add((String)l.get(1)); 1023 } 1024 else if(n.intValue() == 2) 1025 { 1026 dnsNames.add(((String)l.get(1)).toLowerCase()); 1027 } 1028 } 1029 } 1030 } 1031 catch(CertificateParsingException ex) 1032 { 1033 assert(false); 1034 } 1035 1036 // 1037 // Compare the peer's address against the common name as well as 1038 // the dnsName and ipAddress values in the subject alternative name. 1039 // 1040 boolean certNameOK = false; 1041 String dn = ""; 1042 String addrLower = address.toLowerCase(); 1043 { 1044 javax.security.auth.x500.X500Principal principal = cert.getSubjectX500Principal(); 1045 dn = principal.getName(javax.security.auth.x500.X500Principal.CANONICAL); 1046 // 1047 // Canonical format is already in lower case. 1048 // 1049 String cn = "cn=" + addrLower; 1050 int pos = dn.indexOf(cn); 1051 if(pos >= 0) 1052 { 1053 // 1054 // Ensure we match the entire common name. 1055 // 1056 certNameOK = (pos + cn.length() == dn.length()) || (dn.charAt(pos + cn.length()) == ','); 1057 } 1058 } 1059 1060 // 1061 // Compare the peer's address against the dnsName and ipAddress 1062 // values in the subject alternative name. 1063 // 1064 if(!certNameOK) 1065 { 1066 certNameOK = ipAddresses.contains(addrLower); 1067 } 1068 if(!certNameOK) 1069 { 1070 certNameOK = dnsNames.contains(addrLower); 1071 } 1072 1073 if(!certNameOK) 1074 { 1075 StringBuilder sb = new StringBuilder(128); 1076 sb.append("IceSSL: certificate validation failure:\npeer certificate does not have `"); 1077 sb.append(address); 1078 sb.append("' as its commonName or in its subjectAltName extension"); 1079 if(dn.length() > 0) 1080 { 1081 sb.append("\nSubject DN: "); 1082 sb.append(dn); 1083 } 1084 if(!dnsNames.isEmpty()) 1085 { 1086 sb.append("\nDNS names found in certificate: "); 1087 for(int j = 0; j < dnsNames.size(); ++j) 1088 { 1089 if(j > 0) 1090 { 1091 sb.append(", "); 1092 } 1093 sb.append(dnsNames.get(j)); 1094 } 1095 } 1096 if(!ipAddresses.isEmpty()) 1097 { 1098 sb.append("\nIP addresses found in certificate: "); 1099 for(int j = 0; j < ipAddresses.size(); ++j) 1100 { 1101 if(j > 0) 1102 { 1103 sb.append(", "); 1104 } 1105 sb.append(ipAddresses.get(j)); 1106 } 1107 } 1108 Ice.SecurityException ex = new Ice.SecurityException(); 1109 ex.reason = sb.toString(); 1110 throw ex; 1111 } 1112 } 1113 } 1114 1115 if(_verifyDepthMax > 0 && info.certs != null && info.certs.length > _verifyDepthMax) 1116 { 1117 String msg = (info.incoming ? "incoming" : "outgoing") + " connection rejected:\n" + 1118 "length of peer's certificate chain (" + info.certs.length + ") exceeds maximum of " + 1119 _verifyDepthMax + "\n" + desc; 1120 if(_securityTraceLevel >= 1) 1121 { 1122 _logger.trace(_securityTraceCategory, msg); 1123 } 1124 Ice.SecurityException ex = new Ice.SecurityException(); 1125 ex.reason = msg; 1126 throw ex; 1127 } 1128 1129 if(!_trustManager.verify(info, desc)) 1130 { 1131 String msg = (info.incoming ? "incoming" : "outgoing") + " connection rejected by trust manager\n" + desc; 1132 if(_securityTraceLevel >= 1) 1133 { 1134 _logger.trace(_securityTraceCategory, msg); 1135 } 1136 Ice.SecurityException ex = new Ice.SecurityException(); 1137 ex.reason = msg; 1138 throw ex; 1139 } 1140 1141 if(_verifier != null && !_verifier.verify(info)) 1142 { 1143 String msg = (info.incoming ? "incoming" : "outgoing") + " connection rejected by certificate verifier\n" + 1144 desc; 1145 if(_securityTraceLevel >= 1) 1146 { 1147 _logger.trace(_securityTraceCategory, msg); 1148 } 1149 Ice.SecurityException ex = new Ice.SecurityException(); 1150 ex.reason = msg; 1151 throw ex; 1152 } 1153 } 1154 trustManagerFailure(boolean incoming, CertificateException ex)1155 void trustManagerFailure(boolean incoming, CertificateException ex) 1156 throws CertificateException 1157 { 1158 if(_verifyPeer == 0) 1159 { 1160 if(_securityTraceLevel >= 1) 1161 { 1162 String msg = "ignoring peer verification failure"; 1163 if(_securityTraceLevel > 1) 1164 { 1165 java.io.StringWriter sw = new java.io.StringWriter(); 1166 java.io.PrintWriter pw = new java.io.PrintWriter(sw); 1167 ex.printStackTrace(pw); 1168 pw.flush(); 1169 msg += ":\n" + sw.toString(); 1170 } 1171 _logger.trace(_securityTraceCategory, msg); 1172 } 1173 } 1174 else 1175 { 1176 throw ex; 1177 } 1178 } 1179 parseCiphers(String ciphers)1180 private void parseCiphers(String ciphers) 1181 { 1182 java.util.ArrayList<CipherExpression> cipherList = new java.util.ArrayList<CipherExpression>(); 1183 String[] expr = ciphers.split("[ \t]+"); 1184 for(int i = 0; i < expr.length; ++i) 1185 { 1186 if(expr[i].equals("ALL")) 1187 { 1188 if(i != 0) 1189 { 1190 Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); 1191 ex.reason = "IceSSL: `ALL' must be first in cipher list `" + ciphers + "'"; 1192 throw ex; 1193 } 1194 _allCiphers = true; 1195 } 1196 else if(expr[i].equals("NONE")) 1197 { 1198 if(i != 0) 1199 { 1200 Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); 1201 ex.reason = "IceSSL: `NONE' must be first in cipher list `" + ciphers + "'"; 1202 throw ex; 1203 } 1204 _noCiphers = true; 1205 } 1206 else 1207 { 1208 CipherExpression ce = new CipherExpression(); 1209 String exp = expr[i]; 1210 if(exp.charAt(0) == '!') 1211 { 1212 ce.not = true; 1213 if(exp.length() > 1) 1214 { 1215 exp = exp.substring(1); 1216 } 1217 else 1218 { 1219 Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); 1220 ex.reason = "IceSSL: invalid cipher expression `" + exp + "'"; 1221 throw ex; 1222 } 1223 } 1224 1225 if(exp.charAt(0) == '(') 1226 { 1227 if(!exp.endsWith(")")) 1228 { 1229 Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); 1230 ex.reason = "IceSSL: invalid cipher expression `" + exp + "'"; 1231 throw ex; 1232 } 1233 1234 try 1235 { 1236 ce.re = java.util.regex.Pattern.compile(exp.substring(1, exp.length() - 2)); 1237 } 1238 catch(java.util.regex.PatternSyntaxException ex) 1239 { 1240 throw new Ice.PluginInitializationException( 1241 "IceSSL: invalid cipher expression `" + exp + "'", ex); 1242 } 1243 } 1244 else 1245 { 1246 ce.cipher = exp; 1247 } 1248 1249 cipherList.add(ce); 1250 } 1251 } 1252 _ciphers = new CipherExpression[cipherList.size()]; 1253 cipherList.toArray(_ciphers); 1254 } 1255 openResource(String path)1256 private java.io.InputStream openResource(String path) 1257 throws java.io.IOException 1258 { 1259 boolean isAbsolute = false; 1260 try 1261 { 1262 new java.net.URL(path); 1263 isAbsolute = true; 1264 } 1265 catch(java.net.MalformedURLException ex) 1266 { 1267 java.io.File f = new java.io.File(path); 1268 isAbsolute = f.isAbsolute(); 1269 } 1270 1271 java.io.InputStream stream = IceInternal.Util.openResource(getClass().getClassLoader(), path); 1272 1273 // 1274 // If the first attempt fails and IceSSL.DefaultDir is defined and the original path is relative, 1275 // we prepend the default directory and try again. 1276 // 1277 if(stream == null && _defaultDir.length() > 0 && !isAbsolute) 1278 { 1279 stream = IceInternal.Util.openResource(getClass().getClassLoader(), 1280 _defaultDir + java.io.File.separator + path); 1281 } 1282 1283 if(stream != null) 1284 { 1285 stream = new java.io.BufferedInputStream(stream); 1286 } 1287 1288 return stream; 1289 } 1290 1291 private static class CipherExpression 1292 { 1293 boolean not; 1294 String cipher; 1295 java.util.regex.Pattern re; 1296 } 1297 1298 private Ice.Communicator _communicator; 1299 private Ice.Logger _logger; 1300 private IceInternal.ProtocolPluginFacade _facade; 1301 private int _securityTraceLevel; 1302 private String _securityTraceCategory; 1303 private boolean _initialized; 1304 private javax.net.ssl.SSLContext _context; 1305 private String _defaultDir; 1306 private CipherExpression[] _ciphers; 1307 private boolean _allCiphers; 1308 private boolean _noCiphers; 1309 private String[] _protocols; 1310 private boolean _checkCertName; 1311 private int _verifyDepthMax; 1312 private int _verifyPeer; 1313 private CertificateVerifier _verifier; 1314 private PasswordCallback _passwordCallback; 1315 private TrustManager _trustManager; 1316 1317 private InputStream _keystoreStream; 1318 private InputStream _truststoreStream; 1319 private List<InputStream> _seeds = new ArrayList<InputStream>(); 1320 1321 private CertPathValidator _validator; 1322 private PKIXParameters _validatorParams; 1323 } 1324