1 package org.bouncycastle.jsse.provider;
2 
3 import java.net.Socket;
4 import java.security.KeyStore;
5 import java.security.KeyStore.PrivateKeyEntry;
6 import java.security.KeyStoreException;
7 import java.security.NoSuchAlgorithmException;
8 import java.security.Principal;
9 import java.security.PrivateKey;
10 import java.security.PublicKey;
11 import java.security.UnrecoverableKeyException;
12 import java.security.cert.CertPathValidatorException;
13 import java.security.cert.CertificateException;
14 import java.security.cert.X509Certificate;
15 import java.security.interfaces.DSAPublicKey;
16 import java.security.interfaces.ECPublicKey;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.Date;
20 import java.util.Enumeration;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26 import java.util.logging.Level;
27 import java.util.logging.Logger;
28 
29 import javax.net.ssl.SSLEngine;
30 
31 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
32 import org.bouncycastle.asn1.x509.KeyPurposeId;
33 import org.bouncycastle.asn1.x9.ECNamedCurveTable;
34 import org.bouncycastle.jcajce.util.JcaJceHelper;
35 import org.bouncycastle.jsse.BCExtendedSSLSession;
36 import org.bouncycastle.jsse.BCSNIHostName;
37 import org.bouncycastle.jsse.BCX509ExtendedKeyManager;
38 import org.bouncycastle.jsse.BCX509Key;
39 import org.bouncycastle.jsse.java.security.BCAlgorithmConstraints;
40 import org.bouncycastle.tls.KeyExchangeAlgorithm;
41 import org.bouncycastle.tls.NamedGroup;
42 import org.bouncycastle.tls.ProtocolVersion;
43 import org.bouncycastle.tls.TlsUtils;
44 
45 class ProvX509KeyManagerSimple
46     extends BCX509ExtendedKeyManager
47 {
48     private static final Logger LOG = Logger.getLogger(ProvX509KeyManagerSimple.class.getName());
49 
50     private final boolean isInFipsMode;
51     private final JcaJceHelper helper;
52     private final Map<String, Credential> credentials;
53 
54     private static final Map<String, PublicKeyFilter> FILTERS_CLIENT = createFiltersClient();
55     private static final Map<String, PublicKeyFilter> FILTERS_SERVER = createFiltersServer();
56 
addECFilter13(Map<String, PublicKeyFilter> filters, int namedGroup13)57     private static void addECFilter13(Map<String, PublicKeyFilter> filters, int namedGroup13)
58     {
59         if (!NamedGroup.canBeNegotiated(namedGroup13, ProtocolVersion.TLSv13))
60         {
61             throw new IllegalStateException("Invalid named group for TLS 1.3 EC filter");
62         }
63 
64         String curveName = NamedGroup.getCurveName(namedGroup13);
65         if (null != curveName)
66         {
67             ASN1ObjectIdentifier standardOID = ECNamedCurveTable.getOID(curveName);
68             if (null != standardOID)
69             {
70                 String keyType = JsseUtils.getKeyType13("EC", namedGroup13);
71                 PublicKeyFilter filter = new ECPublicKeyFilter13(standardOID);
72                 addFilterToMap(filters, keyType, filter);
73                 return;
74             }
75         }
76 
77         LOG.warning("Failed to register public key filter for EC with " + NamedGroup.getText(namedGroup13));
78     }
79 
addFilter(Map<String, PublicKeyFilter> filters, String keyType)80     private static void addFilter(Map<String, PublicKeyFilter> filters, String keyType)
81     {
82         String algorithm = keyType;
83 
84         addFilter(filters, ProvAlgorithmChecker.KU_DIGITAL_SIGNATURE, algorithm, null, keyType);
85     }
86 
addFilter(Map<String, PublicKeyFilter> filters, Class<? extends PublicKey> clazz, String... keyTypes)87     private static void addFilter(Map<String, PublicKeyFilter> filters, Class<? extends PublicKey> clazz, String... keyTypes)
88     {
89         addFilter(filters, ProvAlgorithmChecker.KU_DIGITAL_SIGNATURE, null, clazz, keyTypes);
90     }
91 
addFilter(Map<String, PublicKeyFilter> filters, int keyUsageBit, String algorithm, Class<? extends PublicKey> clazz, String... keyTypes)92     private static void addFilter(Map<String, PublicKeyFilter> filters, int keyUsageBit, String algorithm,
93         Class<? extends PublicKey> clazz, String... keyTypes)
94     {
95         PublicKeyFilter filter = new DefaultPublicKeyFilter(algorithm, clazz, keyUsageBit);
96 
97         for (String keyType : keyTypes)
98         {
99             addFilterToMap(filters, keyType, filter);
100         }
101     }
102 
addFilterLegacyServer(Map<String, PublicKeyFilter> filters, String algorithm, int... keyExchangeAlgorithms)103     private static void addFilterLegacyServer(Map<String, PublicKeyFilter> filters, String algorithm,
104         int... keyExchangeAlgorithms)
105     {
106         addFilterLegacyServer(filters, ProvAlgorithmChecker.KU_DIGITAL_SIGNATURE, algorithm, keyExchangeAlgorithms);
107     }
108 
addFilterLegacyServer(Map<String, PublicKeyFilter> filters, int keyUsageBit, String algorithm, int... keyExchangeAlgorithms)109     private static void addFilterLegacyServer(Map<String, PublicKeyFilter> filters, int keyUsageBit, String algorithm,
110         int... keyExchangeAlgorithms)
111     {
112         addFilterLegacyServer(filters, keyUsageBit, algorithm, null, keyExchangeAlgorithms);
113     }
114 
addFilterLegacyServer(Map<String, PublicKeyFilter> filters, Class<? extends PublicKey> clazz, int... keyExchangeAlgorithms)115     private static void addFilterLegacyServer(Map<String, PublicKeyFilter> filters, Class<? extends PublicKey> clazz,
116         int... keyExchangeAlgorithms)
117     {
118         addFilterLegacyServer(filters, ProvAlgorithmChecker.KU_DIGITAL_SIGNATURE, null, clazz, keyExchangeAlgorithms);
119     }
120 
addFilterLegacyServer(Map<String, PublicKeyFilter> filters, int keyUsageBit, String algorithm, Class<? extends PublicKey> clazz, int... keyExchangeAlgorithms)121     private static void addFilterLegacyServer(Map<String, PublicKeyFilter> filters, int keyUsageBit, String algorithm,
122         Class<? extends PublicKey> clazz, int... keyExchangeAlgorithms)
123     {
124         addFilter(filters, keyUsageBit, algorithm, clazz, getKeyTypesLegacyServer(keyExchangeAlgorithms));
125     }
126 
addFilterToMap(Map<String, PublicKeyFilter> filters, String keyType, PublicKeyFilter filter)127     private static void addFilterToMap(Map<String, PublicKeyFilter> filters, String keyType, PublicKeyFilter filter)
128     {
129         if (null != filters.put(keyType, filter))
130         {
131             throw new IllegalStateException("Duplicate keys in filters");
132         }
133     }
134 
createFiltersClient()135     private static Map<String, PublicKeyFilter> createFiltersClient()
136     {
137         Map<String, PublicKeyFilter> filters = new HashMap<String, PublicKeyFilter>();
138 
139         addFilter(filters, "Ed25519");
140         addFilter(filters, "Ed448");
141 
142         addECFilter13(filters, NamedGroup.brainpoolP256r1tls13);
143         addECFilter13(filters, NamedGroup.brainpoolP384r1tls13);
144         addECFilter13(filters, NamedGroup.brainpoolP512r1tls13);
145 
146         addECFilter13(filters, NamedGroup.secp256r1);
147         addECFilter13(filters, NamedGroup.secp384r1);
148         addECFilter13(filters, NamedGroup.secp521r1);
149 
150         // TODO Perhaps check the public key OID explicitly for these
151         addFilter(filters, "RSA");
152         addFilter(filters, "RSASSA-PSS");
153 
154         addFilter(filters, DSAPublicKey.class, "DSA");
155         addFilter(filters, ECPublicKey.class, "EC");
156 
157         return Collections.unmodifiableMap(filters);
158     }
159 
createFiltersServer()160     private static Map<String, PublicKeyFilter> createFiltersServer()
161     {
162         Map<String, PublicKeyFilter> filters = new HashMap<String, PublicKeyFilter>();
163 
164         addFilter(filters, "Ed25519");
165         addFilter(filters, "Ed448");
166 
167         addECFilter13(filters, NamedGroup.brainpoolP256r1tls13);
168         addECFilter13(filters, NamedGroup.brainpoolP384r1tls13);
169         addECFilter13(filters, NamedGroup.brainpoolP512r1tls13);
170 
171         addECFilter13(filters, NamedGroup.secp256r1);
172         addECFilter13(filters, NamedGroup.secp384r1);
173         addECFilter13(filters, NamedGroup.secp521r1);
174 
175         // TODO Perhaps check the public key OID explicitly for these
176         addFilter(filters, "RSA");
177         addFilter(filters, "RSASSA-PSS");
178 
179         addFilterLegacyServer(filters, DSAPublicKey.class, KeyExchangeAlgorithm.DHE_DSS, KeyExchangeAlgorithm.SRP_DSS);
180         addFilterLegacyServer(filters, ECPublicKey.class, KeyExchangeAlgorithm.ECDHE_ECDSA);
181         addFilterLegacyServer(filters, "RSA", KeyExchangeAlgorithm.DHE_RSA, KeyExchangeAlgorithm.ECDHE_RSA,
182             KeyExchangeAlgorithm.SRP_RSA);
183         addFilterLegacyServer(filters, ProvAlgorithmChecker.KU_KEY_ENCIPHERMENT, "RSA", KeyExchangeAlgorithm.RSA);
184 
185         return Collections.unmodifiableMap(filters);
186     }
187 
getKeyTypesLegacyServer(int... keyExchangeAlgorithms)188     private static String[] getKeyTypesLegacyServer(int... keyExchangeAlgorithms)
189     {
190         int count = keyExchangeAlgorithms.length;
191         String[] keyTypes = new String[count];
192         for (int i = 0; i < count; ++i)
193         {
194             keyTypes[i] = JsseUtils.getKeyTypeLegacyServer(keyExchangeAlgorithms[i]);
195         }
196         return keyTypes;
197     }
198 
loadCredentials(KeyStore ks, char[] password)199     private static Map<String, Credential> loadCredentials(KeyStore ks, char[] password)
200         throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException
201     {
202         Map<String, Credential> credentials = new HashMap<String, Credential>(4);
203 
204         if (null != ks)
205         {
206             Enumeration<String> aliases = ks.aliases();
207             while (aliases.hasMoreElements())
208             {
209                 String alias = aliases.nextElement();
210                 if (!ks.entryInstanceOf(alias, PrivateKeyEntry.class))
211                 {
212                     continue;
213                 }
214 
215                 PrivateKey privateKey = (PrivateKey)ks.getKey(alias, password);
216                 if (null == privateKey)
217                 {
218                     continue;
219                 }
220 
221                 X509Certificate[] certificateChain = JsseUtils.getX509CertificateChain(ks.getCertificateChain(alias));
222                 if (TlsUtils.isNullOrEmpty(certificateChain))
223                 {
224                     continue;
225                 }
226 
227                 credentials.put(alias, new Credential(alias, privateKey, certificateChain));
228             }
229         }
230 
231         return Collections.unmodifiableMap(credentials);
232     }
233 
ProvX509KeyManagerSimple(boolean isInFipsMode, JcaJceHelper helper, KeyStore ks, char[] password)234     ProvX509KeyManagerSimple(boolean isInFipsMode, JcaJceHelper helper, KeyStore ks, char[] password)
235         throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException
236     {
237         this.isInFipsMode = isInFipsMode;
238         this.helper = helper;
239         this.credentials = loadCredentials(ks, password);
240     }
241 
chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket)242     public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket)
243     {
244         return chooseAlias(getKeyTypes(keyTypes), issuers, TransportData.from(socket), false);
245     }
246 
247     @Override
chooseClientKeyBC(String[] keyTypes, Principal[] issuers, Socket socket)248     public BCX509Key chooseClientKeyBC(String[] keyTypes, Principal[] issuers, Socket socket)
249     {
250         return chooseKeyBC(getKeyTypes(keyTypes), issuers, TransportData.from(socket), false);
251     }
252 
chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine)253     public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine)
254     {
255         return chooseAlias(getKeyTypes(keyTypes), issuers, TransportData.from(engine), false);
256     }
257 
258     @Override
chooseEngineClientKeyBC(String[] keyTypes, Principal[] issuers, SSLEngine engine)259     public BCX509Key chooseEngineClientKeyBC(String[] keyTypes, Principal[] issuers, SSLEngine engine)
260     {
261         return chooseKeyBC(getKeyTypes(keyTypes), issuers, TransportData.from(engine), false);
262     }
263 
chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine)264     public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine)
265     {
266         return chooseAlias(getKeyTypes(keyType), issuers, TransportData.from(engine), true);
267     }
268 
269     @Override
chooseEngineServerKeyBC(String[] keyTypes, Principal[] issuers, SSLEngine engine)270     public BCX509Key chooseEngineServerKeyBC(String[] keyTypes, Principal[] issuers, SSLEngine engine)
271     {
272         return chooseKeyBC(getKeyTypes(keyTypes), issuers, TransportData.from(engine), true);
273     }
274 
chooseServerAlias(String keyType, Principal[] issuers, Socket socket)275     public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket)
276     {
277         return chooseAlias(getKeyTypes(keyType), issuers, TransportData.from(socket), true);
278     }
279 
280     @Override
chooseServerKeyBC(String[] keyTypes, Principal[] issuers, Socket socket)281     public BCX509Key chooseServerKeyBC(String[] keyTypes, Principal[] issuers, Socket socket)
282     {
283         return chooseKeyBC(getKeyTypes(keyTypes), issuers, TransportData.from(socket), true);
284     }
285 
getCertificateChain(String alias)286     public X509Certificate[] getCertificateChain(String alias)
287     {
288         Credential credential = getCredential(alias);
289         return null == credential ? null : credential.certificateChain.clone();
290     }
291 
getClientAliases(String keyType, Principal[] issuers)292     public String[] getClientAliases(String keyType, Principal[] issuers)
293     {
294         return getAliases(getKeyTypes(keyType), issuers, null, false);
295     }
296 
getPrivateKey(String alias)297     public PrivateKey getPrivateKey(String alias)
298     {
299         Credential credential = getCredential(alias);
300         return null == credential ? null : credential.privateKey;
301     }
302 
getServerAliases(String keyType, Principal[] issuers)303     public String[] getServerAliases(String keyType, Principal[] issuers)
304     {
305         return getAliases(getKeyTypes(keyType), issuers, null, true);
306     }
307 
308     @Override
getKeyBC(String keyType, String alias)309     protected BCX509Key getKeyBC(String keyType, String alias)
310     {
311         Credential credential = getCredential(alias);
312         return createKeyBC(keyType, credential);
313     }
314 
chooseAlias(List<String> keyTypes, Principal[] issuers, TransportData transportData, boolean forServer)315     private String chooseAlias(List<String> keyTypes, Principal[] issuers, TransportData transportData,
316         boolean forServer)
317     {
318         Match bestMatch = getBestMatch(keyTypes, issuers, transportData, forServer);
319 
320         if (bestMatch.compareTo(Match.NOTHING) < 0)
321         {
322             String keyType = keyTypes.get(bestMatch.keyTypeIndex);
323             String alias = getAlias(bestMatch);
324             if (LOG.isLoggable(Level.FINE))
325             {
326                 LOG.fine("Found matching key of type: " + keyType + ", returning alias: " + alias);
327             }
328             return alias;
329         }
330 
331         LOG.fine("No matching key found");
332         return null;
333     }
334 
chooseKeyBC(List<String> keyTypes, Principal[] issuers, TransportData transportData, boolean forServer)335     private BCX509Key chooseKeyBC(List<String> keyTypes, Principal[] issuers, TransportData transportData,
336         boolean forServer)
337     {
338         Match bestMatch = getBestMatch(keyTypes, issuers, transportData, forServer);
339 
340         if (bestMatch.compareTo(Match.NOTHING) < 0)
341         {
342             String keyType = keyTypes.get(bestMatch.keyTypeIndex);
343 
344             BCX509Key keyBC = createKeyBC(keyType, bestMatch.credential);
345             if (null != keyBC)
346             {
347                 if (LOG.isLoggable(Level.FINE))
348                 {
349                     LOG.fine("Found matching key of type: " + keyType + ", from alias: " + getAlias(bestMatch));
350                 }
351                 return keyBC;
352             }
353         }
354 
355         LOG.fine("No matching key found");
356         return null;
357     }
358 
createKeyBC(String keyType, Credential credential)359     private BCX509Key createKeyBC(String keyType, Credential credential)
360     {
361         return null == credential
362             ?   null
363             :   new ProvX509Key(keyType, credential.privateKey, credential.certificateChain);
364     }
365 
getAliases(List<String> keyTypes, Principal[] issuers, TransportData transportData, boolean forServer)366     private String[] getAliases(List<String> keyTypes, Principal[] issuers, TransportData transportData,
367         boolean forServer)
368     {
369         if (!credentials.isEmpty() && !keyTypes.isEmpty())
370         {
371             int keyTypeLimit = keyTypes.size();
372             Set<Principal> uniqueIssuers = getUniquePrincipals(issuers);
373             BCAlgorithmConstraints algorithmConstraints = TransportData.getAlgorithmConstraints(transportData, true);
374             Date atDate = new Date();
375             String requestedHostName = getRequestedHostName(transportData, forServer);
376             List<Match> matches = null;
377 
378             for (Credential credential : credentials.values())
379             {
380                 Match match = getPotentialMatch(credential, keyTypes, keyTypeLimit, uniqueIssuers, algorithmConstraints,
381                     forServer, atDate, requestedHostName);
382 
383                 if (match.compareTo(Match.NOTHING) < 0)
384                 {
385                     matches = addToMatches(matches, match);
386                 }
387             }
388 
389             if (null != matches && !matches.isEmpty())
390             {
391                 // NOTE: We are relying on this being a stable sort
392                 Collections.sort(matches);
393 
394                 return getAliases(matches);
395             }
396         }
397 
398         return null;
399     }
400 
getBestMatch(List<String> keyTypes, Principal[] issuers, TransportData transportData, boolean forServer)401     private Match getBestMatch(List<String> keyTypes, Principal[] issuers, TransportData transportData,
402         boolean forServer)
403     {
404         Match bestMatchSoFar = Match.NOTHING;
405 
406         if (!credentials.isEmpty() && !keyTypes.isEmpty())
407         {
408             int keyTypeLimit = keyTypes.size();
409             Set<Principal> uniqueIssuers = getUniquePrincipals(issuers);
410             BCAlgorithmConstraints algorithmConstraints = TransportData.getAlgorithmConstraints(transportData, true);
411             Date atDate = new Date();
412             String requestedHostName = getRequestedHostName(transportData, forServer);
413 
414             for (Credential credential : credentials.values())
415             {
416                 Match match = getPotentialMatch(credential, keyTypes, keyTypeLimit, uniqueIssuers,
417                     algorithmConstraints, forServer, atDate, requestedHostName);
418 
419                 if (match.compareTo(bestMatchSoFar) < 0)
420                 {
421                     bestMatchSoFar = match;
422 
423                     if (bestMatchSoFar.isIdeal())
424                     {
425                         return bestMatchSoFar;
426                     }
427                     if (bestMatchSoFar.isValid())
428                     {
429                         keyTypeLimit = Math.min(keyTypeLimit, bestMatchSoFar.keyTypeIndex + 1);
430                     }
431                 }
432             }
433         }
434 
435         return bestMatchSoFar;
436     }
437 
getPotentialMatch(Credential credential, List<String> keyTypes, int keyTypeLimit, Set<Principal> uniqueIssuers, BCAlgorithmConstraints algorithmConstraints, boolean forServer, Date atDate, String requestedHostName)438     private Match getPotentialMatch(Credential credential, List<String> keyTypes, int keyTypeLimit,
439         Set<Principal> uniqueIssuers, BCAlgorithmConstraints algorithmConstraints, boolean forServer, Date atDate,
440         String requestedHostName)
441     {
442         X509Certificate[] chain = credential.certificateChain;
443         if (!TlsUtils.isNullOrEmpty(chain) && isSuitableChainForIssuers(chain, uniqueIssuers))
444         {
445             int keyTypeIndex = getSuitableKeyTypeForEECert(chain[0], keyTypes, keyTypeLimit, algorithmConstraints,
446                 forServer);
447             if (keyTypeIndex >= 0 && isSuitableChain(chain, algorithmConstraints, forServer))
448             {
449                 Match.Quality quality = getCertificateQuality(chain[0], atDate, requestedHostName);
450 
451                 return new Match(quality, keyTypeIndex, credential);
452             }
453         }
454         return Match.NOTHING;
455     }
456 
getCredential(String alias)457     private Credential getCredential(String alias)
458     {
459         return null == alias ? null : credentials.get(alias);
460     }
461 
isSuitableChain(X509Certificate[] chain, BCAlgorithmConstraints algorithmConstraints, boolean forServer)462     private boolean isSuitableChain(X509Certificate[] chain, BCAlgorithmConstraints algorithmConstraints,
463         boolean forServer)
464     {
465         try
466         {
467             Set<X509Certificate> trustedCerts = Collections.emptySet();
468             KeyPurposeId ekuOID = ProvX509KeyManager.getRequiredExtendedKeyUsage(forServer);
469             int kuBit = -1; // i.e. no checks; we handle them in isSuitableEECert
470 
471             ProvAlgorithmChecker.checkChain(isInFipsMode, helper, algorithmConstraints, trustedCerts, chain, ekuOID,
472                 kuBit);
473 
474             return true;
475         }
476         catch (CertPathValidatorException e)
477         {
478             return false;
479         }
480     }
481 
addToMatches(List<Match> matches, Match match)482     private static List<Match> addToMatches(List<Match> matches, Match match)
483     {
484         if (null == matches)
485         {
486             matches = new ArrayList<Match>();
487         }
488 
489         matches.add(match);
490         return matches;
491     }
492 
getAlias(Match match)493     private static String getAlias(Match match)
494     {
495         return match.credential.alias;
496     }
497 
getAliases(List<Match> matches)498     private static String[] getAliases(List<Match> matches)
499     {
500         int count = matches.size(), pos = 0;
501         String[] result = new String[count];
502         for (Match match : matches)
503         {
504             result[pos++] = getAlias(match);
505         }
506         return result;
507     }
508 
getCertificateQuality(X509Certificate certificate, Date atDate, String requestedHostName)509     private static Match.Quality getCertificateQuality(X509Certificate certificate, Date atDate, String requestedHostName)
510     {
511         try
512         {
513             certificate.checkValidity(atDate);
514         }
515         catch (CertificateException e)
516         {
517             return Match.Quality.EXPIRED;
518         }
519 
520         if (null != requestedHostName)
521         {
522             try
523             {
524                 /*
525                  * NOTE: For compatibility with SunJSSE, we also re-use HTTPS endpoint ID checks for
526                  * SNI certificate selection.
527                  */
528                 ProvX509TrustManager.checkEndpointID(requestedHostName, certificate, "HTTPS");
529             }
530             catch (CertificateException e)
531             {
532                 return Match.Quality.MISMATCH_SNI;
533             }
534         }
535 
536         /*
537          * Prefer RSA certificates with more specific KeyUsage over "multi-use" ones.
538          */
539         if ("RSA".equalsIgnoreCase(JsseUtils.getPublicKeyAlgorithm(certificate.getPublicKey())))
540         {
541             boolean[] keyUsage = certificate.getKeyUsage();
542             if (ProvAlgorithmChecker.supportsKeyUsage(keyUsage, ProvAlgorithmChecker.KU_DIGITAL_SIGNATURE) &&
543                 ProvAlgorithmChecker.supportsKeyUsage(keyUsage, ProvAlgorithmChecker.KU_KEY_ENCIPHERMENT))
544             {
545                 return Match.Quality.RSA_MULTI_USE;
546             }
547         }
548 
549         return Match.Quality.OK;
550     }
551 
getKeyTypes(String... keyTypes)552     private static List<String> getKeyTypes(String... keyTypes)
553     {
554         if (null != keyTypes && keyTypes.length > 0)
555         {
556             ArrayList<String> result = new ArrayList<String>(keyTypes.length);
557             for (String keyType : keyTypes)
558             {
559                 if (null == keyType)
560                 {
561                     throw new IllegalArgumentException("Key types cannot be null");
562                 }
563                 if (!result.contains(keyType))
564                 {
565                     result.add(keyType);
566                 }
567             }
568             return Collections.unmodifiableList(result);
569         }
570         return Collections.emptyList();
571     }
572 
getRequestedHostName(TransportData transportData, boolean forServer)573     private static String getRequestedHostName(TransportData transportData, boolean forServer)
574     {
575         if (null != transportData && forServer)
576         {
577             BCExtendedSSLSession sslSession = transportData.getHandshakeSession();
578             if (null != sslSession)
579             {
580                 BCSNIHostName sniHostName = JsseUtils.getSNIHostName(sslSession.getRequestedServerNames());
581                 if (null != sniHostName)
582                 {
583                     return sniHostName.getAsciiName();
584                 }
585             }
586         }
587         return null;
588     }
589 
getSuitableKeyTypeForEECert(X509Certificate eeCert, List<String> keyTypes, int keyTypeLimit, BCAlgorithmConstraints algorithmConstraints, boolean forServer)590     private static int getSuitableKeyTypeForEECert(X509Certificate eeCert, List<String> keyTypes, int keyTypeLimit,
591         BCAlgorithmConstraints algorithmConstraints, boolean forServer)
592     {
593         Map<String, PublicKeyFilter> filters = forServer ? FILTERS_SERVER : FILTERS_CLIENT;
594 
595         PublicKey publicKey = eeCert.getPublicKey();
596         boolean[] keyUsage = eeCert.getKeyUsage();
597 
598         for (int keyTypeIndex = 0; keyTypeIndex < keyTypeLimit; ++keyTypeIndex)
599         {
600             String keyType = keyTypes.get(keyTypeIndex);
601             PublicKeyFilter filter = filters.get(keyType);
602             if (null != filter && filter.accepts(publicKey, keyUsage, algorithmConstraints))
603             {
604                 return keyTypeIndex;
605             }
606         }
607 
608         return -1;
609     }
610 
getUniquePrincipals(Principal[] principals)611     private static Set<Principal> getUniquePrincipals(Principal[] principals)
612     {
613         if (null == principals)
614         {
615             return null;
616         }
617         if (principals.length > 0)
618         {
619             Set<Principal> result = new HashSet<Principal>();
620             for (int i = 0; i < principals.length; ++i)
621             {
622                 Principal principal = principals[i];
623                 if (null != principal)
624                 {
625                     result.add(principal);
626                 }
627             }
628             if (!result.isEmpty())
629             {
630                 return Collections.unmodifiableSet(result);
631             }
632         }
633         return Collections.emptySet();
634     }
635 
isSuitableChainForIssuers(X509Certificate[] chain, Set<Principal> uniqueIssuers)636     private static boolean isSuitableChainForIssuers(X509Certificate[] chain, Set<Principal> uniqueIssuers)
637     {
638         // NOTE: Empty issuers means same as absent issuers, per SunJSSE
639         if (null == uniqueIssuers || uniqueIssuers.isEmpty())
640         {
641             return true;
642         }
643         int pos = chain.length;
644         while (--pos >= 0)
645         {
646             if (uniqueIssuers.contains(chain[pos].getIssuerX500Principal()))
647             {
648                 return true;
649             }
650         }
651         X509Certificate eeCert = chain[0];
652         return eeCert.getBasicConstraints() >= 0
653             && uniqueIssuers.contains(eeCert.getSubjectX500Principal());
654     }
655 
656     private static class Credential
657     {
658         private final String alias;
659         private final PrivateKey privateKey;
660         private final X509Certificate[] certificateChain;
661 
Credential(String alias, PrivateKey privateKey, X509Certificate[] certificateChain)662         Credential(String alias, PrivateKey privateKey, X509Certificate[] certificateChain)
663         {
664             this.alias = alias;
665             this.privateKey = privateKey;
666             this.certificateChain = certificateChain;
667         }
668     }
669 
670     private static final class Match
671         implements Comparable<Match>
672     {
673         // NOTE: We rely on these being in preference order.
674         static enum Quality
675         {
676             OK,
677             RSA_MULTI_USE,
678             MISMATCH_SNI,
679             EXPIRED,
680             // TODO[jsse] Consider allowing certificates with invalid ExtendedKeyUsage and/or KeyUsage (as SunJSSE does)
681 //            MISMATCH_EKU,
682 //            MISMATCH_KU,
683             NONE
684         }
685 
686         static final Quality INVALID = Quality.MISMATCH_SNI;
687         static final Match NOTHING = new Match(Quality.NONE, -1, null);
688 
689         final Quality quality;
690         final int keyTypeIndex;
691         final Credential credential;
692 
Match(Quality quality, int keyTypeIndex, Credential credential)693         Match(Quality quality, int keyTypeIndex, Credential credential)
694         {
695             this.quality = quality;
696             this.keyTypeIndex = keyTypeIndex;
697             this.credential = credential;
698         }
699 
compareTo(Match that)700         public int compareTo(Match that)
701         {
702             int cmp = Boolean.compare(that.isValid(), this.isValid());
703             if (cmp == 0)
704             {
705                 cmp = Integer.compare(this.keyTypeIndex, that.keyTypeIndex);
706                 if (cmp == 0)
707                 {
708                     cmp = this.quality.compareTo(that.quality);
709                 }
710             }
711             return cmp;
712         }
713 
isIdeal()714         boolean isIdeal()
715         {
716             return Quality.OK == quality && 0 == keyTypeIndex;
717         }
718 
isValid()719         boolean isValid()
720         {
721             return quality.compareTo(INVALID) < 0;
722         }
723     }
724 
725     private static interface PublicKeyFilter
726     {
accepts(PublicKey publicKey, boolean[] keyUsage, BCAlgorithmConstraints algorithmConstraints)727         boolean accepts(PublicKey publicKey, boolean[] keyUsage, BCAlgorithmConstraints algorithmConstraints);
728     }
729 
730     private static final class DefaultPublicKeyFilter
731         implements PublicKeyFilter
732     {
733         final String algorithm;
734         final Class<? extends PublicKey> clazz;
735         final int keyUsageBit;
736 
DefaultPublicKeyFilter(String algorithm, Class<? extends PublicKey> clazz, int keyUsageBit)737         DefaultPublicKeyFilter(String algorithm, Class<? extends PublicKey> clazz, int keyUsageBit)
738         {
739             this.algorithm = algorithm;
740             this.clazz = clazz;
741             this.keyUsageBit = keyUsageBit;
742         }
743 
accepts(PublicKey publicKey, boolean[] keyUsage, BCAlgorithmConstraints algorithmConstraints)744         public boolean accepts(PublicKey publicKey, boolean[] keyUsage, BCAlgorithmConstraints algorithmConstraints)
745         {
746             return appliesTo(publicKey)
747                 && ProvAlgorithmChecker.permitsKeyUsage(publicKey, keyUsage, keyUsageBit, algorithmConstraints);
748         }
749 
appliesTo(PublicKey publicKey)750         private boolean appliesTo(PublicKey publicKey)
751         {
752             return (null != algorithm && algorithm.equalsIgnoreCase(JsseUtils.getPublicKeyAlgorithm(publicKey)))
753                 || (null != clazz && clazz.isInstance(publicKey));
754         }
755     }
756 
757     private static final class ECPublicKeyFilter13
758         implements PublicKeyFilter
759     {
760         final ASN1ObjectIdentifier standardOID;
761 
ECPublicKeyFilter13(ASN1ObjectIdentifier standardOID)762         ECPublicKeyFilter13(ASN1ObjectIdentifier standardOID)
763         {
764             this.standardOID = standardOID;
765         }
766 
accepts(PublicKey publicKey, boolean[] keyUsage, BCAlgorithmConstraints algorithmConstraints)767         public boolean accepts(PublicKey publicKey, boolean[] keyUsage, BCAlgorithmConstraints algorithmConstraints)
768         {
769             return appliesTo(publicKey)
770                 && ProvAlgorithmChecker.permitsKeyUsage(publicKey, keyUsage, ProvAlgorithmChecker.KU_DIGITAL_SIGNATURE,
771                     algorithmConstraints);
772         }
773 
appliesTo(PublicKey publicKey)774         private boolean appliesTo(PublicKey publicKey)
775         {
776             if ("EC".equalsIgnoreCase(JsseUtils.getPublicKeyAlgorithm(publicKey))
777                 || ECPublicKey.class.isInstance(publicKey))
778             {
779                 ASN1ObjectIdentifier oid = JsseUtils.getNamedCurveOID(publicKey);
780                 if (standardOID.equals(oid))
781                 {
782                     return true;
783                 }
784             }
785             return false;
786         }
787     }
788 }
789