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.apache.harmony.xnet.provider.jsse; 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.Hashtable; 33 import java.util.List; 34 import java.util.Locale; 35 import javax.net.ssl.SSLEngine; 36 import javax.net.ssl.X509ExtendedKeyManager; 37 import 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 * 49 */ 50 public class KeyManagerImpl extends X509ExtendedKeyManager { 51 52 // hashed key store information 53 private final Hashtable<String, PrivateKeyEntry> hash; 54 55 /** 56 * Creates Key manager 57 * 58 * @param keyStore 59 * @param pwd 60 */ 61 public KeyManagerImpl(KeyStore keyStore, char[] pwd) { 62 this.hash = new Hashtable<String, PrivateKeyEntry>(); 63 final Enumeration<String> aliases; 64 try { 65 aliases = keyStore.aliases(); 66 } catch (KeyStoreException e) { 67 return; 68 } 69 for (; aliases.hasMoreElements();) { 70 final String alias = aliases.nextElement(); 71 try { 72 if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) { 73 final KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry) keyStore 74 .getEntry(alias, new KeyStore.PasswordProtection(pwd)); 75 hash.put(alias, entry); 76 } 77 } catch (KeyStoreException e) { 78 continue; 79 } catch (UnrecoverableEntryException e) { 80 continue; 81 } catch (NoSuchAlgorithmException e) { 82 continue; 83 } 84 } 85 } 86 87 public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) { 88 final String[] al = chooseAlias(keyTypes, issuers); 89 return (al == null ? null : al[0]); 90 } 91 92 public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { 93 final String[] al = chooseAlias(new String[] { keyType }, issuers); 94 return (al == null ? null : al[0]); 95 } 96 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 public String[] getClientAliases(String keyType, Principal[] issuers) { 116 return chooseAlias(new String[] { keyType }, issuers); 117 } 118 119 public String[] getServerAliases(String keyType, Principal[] issuers) { 120 return chooseAlias(new String[] { keyType }, issuers); 121 } 122 123 public PrivateKey getPrivateKey(String alias) { 124 if (alias == null) { 125 return null; 126 } 127 if (hash.containsKey(alias)) { 128 return hash.get(alias).getPrivateKey(); 129 } 130 return null; 131 } 132 133 @Override 134 public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine) { 135 final String[] al = chooseAlias(keyTypes, issuers); 136 return (al == null ? null : al[0]); 137 } 138 139 @Override 140 public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) { 141 final String[] al = chooseAlias(new String[] { keyType }, issuers); 142 return (al == null ? null : al[0]); 143 } 144 145 private String[] chooseAlias(String[] keyTypes, Principal[] issuers) { 146 if (keyTypes == null || keyTypes.length == 0) { 147 return null; 148 } 149 List<Principal> issuersList = (issuers == null) ? null : Arrays.asList(issuers); 150 ArrayList<String> found = new ArrayList<String>(); 151 for (Enumeration<String> aliases = hash.keys(); aliases.hasMoreElements();) { 152 final String alias = aliases.nextElement(); 153 final KeyStore.PrivateKeyEntry entry = hash.get(alias); 154 final Certificate[] chain = entry.getCertificateChain(); 155 final Certificate cert = chain[0]; 156 final String certKeyAlg = cert.getPublicKey().getAlgorithm(); 157 final String certSigAlg = (cert instanceof X509Certificate 158 ? ((X509Certificate) cert).getSigAlgName().toUpperCase(Locale.US) 159 : null); 160 for (String keyAlgorithm : keyTypes) { 161 if (keyAlgorithm == null) { 162 continue; 163 } 164 String sigAlgorithm; 165 // handle cases like EC_EC and EC_RSA 166 int index = keyAlgorithm.indexOf('_'); 167 if (index == -1) { 168 sigAlgorithm = keyAlgorithm; 169 } else { 170 sigAlgorithm = keyAlgorithm.substring(index + 1); 171 keyAlgorithm = keyAlgorithm.substring(0, index); 172 } 173 // key algorithm does not match 174 if (!certKeyAlg.equals(keyAlgorithm)) { 175 continue; 176 } 177 /* 178 * TODO find a more reliable test for signature 179 * algorithm. Unfortunately value varies with 180 * provider. For example for "EC" it could be 181 * "SHA1WithECDSA" or simply "ECDSA". 182 */ 183 // sig algorithm does not match 184 if (certSigAlg != null && !certSigAlg.contains(sigAlgorithm)) { 185 continue; 186 } 187 // no issuers to match, just add to return list and continue 188 if (issuers == null || issuers.length == 0) { 189 found.add(alias); 190 continue; 191 } 192 // check that a certificate in the chain was issued by one of the specified issuers 193 loop: for (Certificate certFromChain : chain) { 194 if (!(certFromChain instanceof X509Certificate)) { 195 // skip non-X509Certificates 196 continue; 197 } 198 X509Certificate xcertFromChain = (X509Certificate) certFromChain; 199 /* 200 * Note use of X500Principal from 201 * getIssuerX500Principal as opposed to Principal 202 * from getIssuerDN. Principal.equals test does 203 * not work in the case where 204 * xcertFromChain.getIssuerDN is a bouncycastle 205 * org.bouncycastle.jce.X509Principal. 206 */ 207 X500Principal issuerFromChain = xcertFromChain.getIssuerX500Principal(); 208 if (issuersList.contains(issuerFromChain)) { 209 found.add(alias); 210 } 211 } 212 } 213 } 214 if (!found.isEmpty()) { 215 return found.toArray(new String[found.size()]); 216 } 217 return null; 218 } 219 } 220