Home | History | Annotate | Download | only in conscrypt
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 package org.conscrypt;
     18 
     19 import java.net.Socket;
     20 import java.security.KeyStore;
     21 import java.security.KeyStore.PrivateKeyEntry;
     22 import java.security.KeyStoreException;
     23 import java.security.NoSuchAlgorithmException;
     24 import java.security.Principal;
     25 import java.security.PrivateKey;
     26 import java.security.UnrecoverableEntryException;
     27 import java.security.cert.Certificate;
     28 import java.security.cert.X509Certificate;
     29 import java.util.ArrayList;
     30 import java.util.Arrays;
     31 import java.util.Enumeration;
     32 import java.util.HashMap;
     33 import java.util.List;
     34 import java.util.Locale;
     35 import java.util.Map;
     36 import javax.net.ssl.SSLEngine;
     37 import javax.net.ssl.X509ExtendedKeyManager;
     38 import javax.security.auth.x500.X500Principal;
     39 
     40 /**
     41  * KeyManager implementation.
     42  *
     43  * This implementation uses hashed key store information. It works faster than retrieving all of the
     44  * data from the key store. Any key store changes, that happen after key manager was created, have
     45  * no effect. The implementation does not use peer information (host, port) that may be obtained
     46  * from socket or engine.
     47  *
     48  * @see javax.net.ssl.KeyManager
     49  */
     50 class KeyManagerImpl extends X509ExtendedKeyManager {
     51 
     52     // hashed key store information
     53     private final HashMap<String, PrivateKeyEntry> hash;
     54 
     55     /**
     56      * Creates Key manager
     57      */
     58     KeyManagerImpl(KeyStore keyStore, char[] pwd) {
     59         this.hash = new HashMap<String, PrivateKeyEntry>();
     60         final Enumeration<String> aliases;
     61         try {
     62             aliases = keyStore.aliases();
     63         } catch (KeyStoreException e) {
     64             return;
     65         }
     66         for (; aliases.hasMoreElements();) {
     67             final String alias = aliases.nextElement();
     68             try {
     69                 if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {
     70                     final KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry) keyStore
     71                             .getEntry(alias, new KeyStore.PasswordProtection(pwd));
     72                     hash.put(alias, entry);
     73                 }
     74             } catch (KeyStoreException ignored) {
     75                 // Ignored.
     76             } catch (UnrecoverableEntryException ignored) {
     77                 // Ignored.
     78             } catch (NoSuchAlgorithmException ignored) {
     79                 // Ignored.
     80             }
     81         }
     82     }
     83 
     84     @Override
     85     public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
     86         final String[] al = chooseAlias(keyTypes, issuers);
     87         return (al == null ? null : al[0]);
     88     }
     89 
     90     @Override
     91     public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
     92         final String[] al = chooseAlias(new String[] { keyType }, issuers);
     93         return (al == null ? null : al[0]);
     94     }
     95 
     96     @Override
     97     public X509Certificate[] getCertificateChain(String alias) {
     98         if (alias == null) {
     99             return null;
    100         }
    101         if (hash.containsKey(alias)) {
    102             Certificate[] certs = hash.get(alias).getCertificateChain();
    103             if (certs[0] instanceof X509Certificate) {
    104                 X509Certificate[] xcerts = new X509Certificate[certs.length];
    105                 for (int i = 0; i < certs.length; i++) {
    106                     xcerts[i] = (X509Certificate) certs[i];
    107                 }
    108                 return xcerts;
    109             }
    110         }
    111         return null;
    112 
    113     }
    114 
    115     @Override
    116     public String[] getClientAliases(String keyType, Principal[] issuers) {
    117         return chooseAlias(new String[] { keyType }, issuers);
    118     }
    119 
    120     @Override
    121     public String[] getServerAliases(String keyType, Principal[] issuers) {
    122         return chooseAlias(new String[] { keyType }, issuers);
    123     }
    124 
    125     @Override
    126     public PrivateKey getPrivateKey(String alias) {
    127         if (alias == null) {
    128             return null;
    129         }
    130         if (hash.containsKey(alias)) {
    131             return hash.get(alias).getPrivateKey();
    132         }
    133         return null;
    134     }
    135 
    136     @Override
    137     public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine) {
    138         final String[] al = chooseAlias(keyTypes, issuers);
    139         return (al == null ? null : al[0]);
    140     }
    141 
    142     @Override
    143     public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) {
    144         final String[] al = chooseAlias(new String[] { keyType }, issuers);
    145         return (al == null ? null : al[0]);
    146     }
    147 
    148     private String[] chooseAlias(String[] keyTypes, Principal[] issuers) {
    149         if (keyTypes == null || keyTypes.length == 0) {
    150             return null;
    151         }
    152         List<Principal> issuersList = (issuers == null) ? null : Arrays.asList(issuers);
    153         ArrayList<String> found = new ArrayList<String>();
    154         for (final Map.Entry<String, PrivateKeyEntry> entry : hash.entrySet()) {
    155             final String alias = entry.getKey();
    156             final Certificate[] chain = entry.getValue().getCertificateChain();
    157             final Certificate cert = chain[0];
    158             final String certKeyAlg = cert.getPublicKey().getAlgorithm();
    159             final String certSigAlg = (cert instanceof X509Certificate
    160                                        ? ((X509Certificate) cert).getSigAlgName().toUpperCase(Locale.US)
    161                                        : null);
    162             for (String keyAlgorithm : keyTypes) {
    163                 if (keyAlgorithm == null) {
    164                     continue;
    165                 }
    166                 final String sigAlgorithm;
    167                 // handle cases like EC_EC and EC_RSA
    168                 int index = keyAlgorithm.indexOf('_');
    169                 if (index == -1) {
    170                     sigAlgorithm = null;
    171                 } else {
    172                     sigAlgorithm = keyAlgorithm.substring(index + 1);
    173                     keyAlgorithm = keyAlgorithm.substring(0, index);
    174                 }
    175                 // key algorithm does not match
    176                 if (!certKeyAlg.equals(keyAlgorithm)) {
    177                     continue;
    178                 }
    179                 /*
    180                  * TODO find a more reliable test for signature
    181                  * algorithm. Unfortunately value varies with
    182                  * provider. For example for "EC" it could be
    183                  * "SHA1WithECDSA" or simply "ECDSA".
    184                  */
    185                 // sig algorithm does not match
    186                 if (sigAlgorithm != null && certSigAlg != null
    187                         && !certSigAlg.contains(sigAlgorithm)) {
    188                     continue;
    189                 }
    190                 // no issuers to match, just add to return list and continue
    191                 if (issuers == null || issuers.length == 0) {
    192                     found.add(alias);
    193                     continue;
    194                 }
    195                 // check that a certificate in the chain was issued by one of the specified issuers
    196                 for (Certificate certFromChain : chain) {
    197                     if (!(certFromChain instanceof X509Certificate)) {
    198                         // skip non-X509Certificates
    199                         continue;
    200                     }
    201                     X509Certificate xcertFromChain = (X509Certificate) certFromChain;
    202                     /*
    203                      * Note use of X500Principal from
    204                      * getIssuerX500Principal as opposed to Principal
    205                      * from getIssuerDN. Principal.equals test does
    206                      * not work in the case where
    207                      * xcertFromChain.getIssuerDN is a bouncycastle
    208                      * org.bouncycastle.jce.X509Principal.
    209                      */
    210                     X500Principal issuerFromChain = xcertFromChain.getIssuerX500Principal();
    211                     if (issuersList.contains(issuerFromChain)) {
    212                         found.add(alias);
    213                     }
    214                 }
    215             }
    216         }
    217         if (!found.isEmpty()) {
    218             return found.toArray(new String[found.size()]);
    219         }
    220         return null;
    221     }
    222 }
    223