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