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