1/* 2 * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package sun.security.ssl; 27 28import javax.net.ssl.*; 29import java.security.*; 30import java.security.cert.*; 31import java.security.cert.Certificate; 32import java.util.*; 33import java.net.Socket; 34 35import javax.security.auth.x500.X500Principal; 36 37 38/** 39 * An implementation of X509KeyManager backed by a KeyStore. 40 * 41 * The backing KeyStore is inspected when this object is constructed. 42 * All key entries containing a PrivateKey and a non-empty chain of 43 * X509Certificate are then copied into an internal store. This means 44 * that subsequent modifications of the KeyStore have no effect on the 45 * X509KeyManagerImpl object. 46 * 47 * Note that this class assumes that all keys are protected by the same 48 * password. 49 * 50 * The JSSE handshake code currently calls into this class via 51 * chooseClientAlias() and chooseServerAlias() to find the certificates to 52 * use. As implemented here, both always return the first alias returned by 53 * getClientAliases() and getServerAliases(). In turn, these methods are 54 * implemented by calling getAliases(), which performs the actual lookup. 55 * 56 * Note that this class currently implements no checking of the local 57 * certificates. In particular, it is *not* guaranteed that: 58 * . the certificates are within their validity period and not revoked 59 * . the signatures verify 60 * . they form a PKIX compliant chain. 61 * . the certificate extensions allow the certificate to be used for 62 * the desired purpose. 63 * 64 * Chains that fail any of these criteria will probably be rejected by 65 * the remote peer. 66 * 67 */ 68final class SunX509KeyManagerImpl extends X509ExtendedKeyManager { 69 70 private static final Debug debug = Debug.getInstance("ssl"); 71 72 private static final String[] STRING0 = new String[0]; 73 74 /* 75 * The credentials from the KeyStore as 76 * Map: String(alias) -> X509Credentials(credentials) 77 */ 78 private Map<String,X509Credentials> credentialsMap; 79 80 /* 81 * Cached server aliases for the case issuers == null. 82 * (in the current JSSE implementation, issuers are always null for 83 * server certs). See chooseServerAlias() for details. 84 * 85 * Map: String(keyType) -> String[](alias) 86 */ 87 private final Map<String,String[]> serverAliasCache; 88 89 /* 90 * Basic container for credentials implemented as an inner class. 91 */ 92 private static class X509Credentials { 93 PrivateKey privateKey; 94 X509Certificate[] certificates; 95 private Set<X500Principal> issuerX500Principals; 96 97 X509Credentials(PrivateKey privateKey, X509Certificate[] certificates) { 98 // assert privateKey and certificates != null 99 this.privateKey = privateKey; 100 this.certificates = certificates; 101 } 102 103 synchronized Set<X500Principal> getIssuerX500Principals() { 104 // lazy initialization 105 if (issuerX500Principals == null) { 106 issuerX500Principals = new HashSet<X500Principal>(); 107 for (int i = 0; i < certificates.length; i++) { 108 issuerX500Principals.add( 109 certificates[i].getIssuerX500Principal()); 110 } 111 } 112 return issuerX500Principals; 113 } 114 } 115 116 SunX509KeyManagerImpl(KeyStore ks, char[] password) 117 throws KeyStoreException, 118 NoSuchAlgorithmException, UnrecoverableKeyException { 119 120 credentialsMap = new HashMap<String,X509Credentials>(); 121 serverAliasCache = Collections.synchronizedMap( 122 new HashMap<String,String[]>()); 123 if (ks == null) { 124 return; 125 } 126 127 for (Enumeration<String> aliases = ks.aliases(); 128 aliases.hasMoreElements(); ) { 129 String alias = aliases.nextElement(); 130 if (!ks.isKeyEntry(alias)) { 131 continue; 132 } 133 Key key = ks.getKey(alias, password); 134 if (key instanceof PrivateKey == false) { 135 continue; 136 } 137 Certificate[] certs = ks.getCertificateChain(alias); 138 if ((certs == null) || (certs.length == 0) || 139 !(certs[0] instanceof X509Certificate)) { 140 continue; 141 } 142 if (!(certs instanceof X509Certificate[])) { 143 Certificate[] tmp = new X509Certificate[certs.length]; 144 System.arraycopy(certs, 0, tmp, 0, certs.length); 145 certs = tmp; 146 } 147 148 X509Credentials cred = new X509Credentials((PrivateKey)key, 149 (X509Certificate[])certs); 150 credentialsMap.put(alias, cred); 151 if (debug != null && Debug.isOn("keymanager")) { 152 System.out.println("***"); 153 System.out.println("found key for : " + alias); 154 for (int i = 0; i < certs.length; i++) { 155 System.out.println("chain [" + i + "] = " 156 + certs[i]); 157 } 158 System.out.println("***"); 159 } 160 } 161 } 162 163 /* 164 * Returns the certificate chain associated with the given alias. 165 * 166 * @return the certificate chain (ordered with the user's certificate first 167 * and the root certificate authority last) 168 */ 169 @Override 170 public X509Certificate[] getCertificateChain(String alias) { 171 if (alias == null) { 172 return null; 173 } 174 X509Credentials cred = credentialsMap.get(alias); 175 if (cred == null) { 176 return null; 177 } else { 178 return cred.certificates.clone(); 179 } 180 } 181 182 /* 183 * Returns the key associated with the given alias 184 */ 185 @Override 186 public PrivateKey getPrivateKey(String alias) { 187 if (alias == null) { 188 return null; 189 } 190 X509Credentials cred = credentialsMap.get(alias); 191 if (cred == null) { 192 return null; 193 } else { 194 return cred.privateKey; 195 } 196 } 197 198 /* 199 * Choose an alias to authenticate the client side of a secure 200 * socket given the public key type and the list of 201 * certificate issuer authorities recognized by the peer (if any). 202 */ 203 @Override 204 public String chooseClientAlias(String[] keyTypes, Principal[] issuers, 205 Socket socket) { 206 /* 207 * We currently don't do anything with socket, but 208 * someday we might. It might be a useful hint for 209 * selecting one of the aliases we get back from 210 * getClientAliases(). 211 */ 212 213 if (keyTypes == null) { 214 return null; 215 } 216 217 for (int i = 0; i < keyTypes.length; i++) { 218 String[] aliases = getClientAliases(keyTypes[i], issuers); 219 if ((aliases != null) && (aliases.length > 0)) { 220 return aliases[0]; 221 } 222 } 223 return null; 224 } 225 226 /* 227 * Choose an alias to authenticate the client side of an 228 * <code>SSLEngine</code> connection given the public key type 229 * and the list of certificate issuer authorities recognized by 230 * the peer (if any). 231 * 232 * @since 1.5 233 */ 234 @Override 235 public String chooseEngineClientAlias(String[] keyType, 236 Principal[] issuers, SSLEngine engine) { 237 /* 238 * If we ever start using socket as a selection criteria, 239 * we'll need to adjust this. 240 */ 241 return chooseClientAlias(keyType, issuers, null); 242 } 243 244 /* 245 * Choose an alias to authenticate the server side of a secure 246 * socket given the public key type and the list of 247 * certificate issuer authorities recognized by the peer (if any). 248 */ 249 @Override 250 public String chooseServerAlias(String keyType, 251 Principal[] issuers, Socket socket) { 252 /* 253 * We currently don't do anything with socket, but 254 * someday we might. It might be a useful hint for 255 * selecting one of the aliases we get back from 256 * getServerAliases(). 257 */ 258 if (keyType == null) { 259 return null; 260 } 261 262 String[] aliases; 263 264 if (issuers == null || issuers.length == 0) { 265 aliases = serverAliasCache.get(keyType); 266 if (aliases == null) { 267 aliases = getServerAliases(keyType, issuers); 268 // Cache the result (positive and negative lookups) 269 if (aliases == null) { 270 aliases = STRING0; 271 } 272 serverAliasCache.put(keyType, aliases); 273 } 274 } else { 275 aliases = getServerAliases(keyType, issuers); 276 } 277 if ((aliases != null) && (aliases.length > 0)) { 278 return aliases[0]; 279 } 280 return null; 281 } 282 283 /* 284 * Choose an alias to authenticate the server side of an 285 * <code>SSLEngine</code> connection given the public key type 286 * and the list of certificate issuer authorities recognized by 287 * the peer (if any). 288 * 289 * @since 1.5 290 */ 291 @Override 292 public String chooseEngineServerAlias(String keyType, 293 Principal[] issuers, SSLEngine engine) { 294 /* 295 * If we ever start using socket as a selection criteria, 296 * we'll need to adjust this. 297 */ 298 return chooseServerAlias(keyType, issuers, null); 299 } 300 301 /* 302 * Get the matching aliases for authenticating the client side of a secure 303 * socket given the public key type and the list of 304 * certificate issuer authorities recognized by the peer (if any). 305 */ 306 @Override 307 public String[] getClientAliases(String keyType, Principal[] issuers) { 308 return getAliases(keyType, issuers); 309 } 310 311 /* 312 * Get the matching aliases for authenticating the server side of a secure 313 * socket given the public key type and the list of 314 * certificate issuer authorities recognized by the peer (if any). 315 */ 316 @Override 317 public String[] getServerAliases(String keyType, Principal[] issuers) { 318 return getAliases(keyType, issuers); 319 } 320 321 /* 322 * Get the matching aliases for authenticating the either side of a secure 323 * socket given the public key type and the list of 324 * certificate issuer authorities recognized by the peer (if any). 325 * 326 * Issuers comes to us in the form of X500Principal[]. 327 */ 328 private String[] getAliases(String keyType, Principal[] issuers) { 329 if (keyType == null) { 330 return null; 331 } 332 if (issuers == null) { 333 issuers = new X500Principal[0]; 334 } 335 if (issuers instanceof X500Principal[] == false) { 336 // normally, this will never happen but try to recover if it does 337 issuers = convertPrincipals(issuers); 338 } 339 String sigType; 340 if (keyType.contains("_")) { 341 int k = keyType.indexOf('_'); 342 sigType = keyType.substring(k + 1); 343 keyType = keyType.substring(0, k); 344 } else { 345 sigType = null; 346 } 347 348 X500Principal[] x500Issuers = (X500Principal[])issuers; 349 // the algorithm below does not produce duplicates, so avoid Set 350 List<String> aliases = new ArrayList<>(); 351 352 for (Map.Entry<String,X509Credentials> entry : 353 credentialsMap.entrySet()) { 354 355 String alias = entry.getKey(); 356 X509Credentials credentials = entry.getValue(); 357 X509Certificate[] certs = credentials.certificates; 358 359 if (!keyType.equals(certs[0].getPublicKey().getAlgorithm())) { 360 continue; 361 } 362 if (sigType != null) { 363 if (certs.length > 1) { 364 // if possible, check the public key in the issuer cert 365 if (!sigType.equals( 366 certs[1].getPublicKey().getAlgorithm())) { 367 continue; 368 } 369 } else { 370 // Check the signature algorithm of the certificate itself. 371 // Look for the "withRSA" in "SHA1withRSA", etc. 372 String sigAlgName = 373 certs[0].getSigAlgName().toUpperCase(Locale.ENGLISH); 374 String pattern = "WITH" + 375 sigType.toUpperCase(Locale.ENGLISH); 376 if (sigAlgName.contains(pattern) == false) { 377 continue; 378 } 379 } 380 } 381 382 if (issuers.length == 0) { 383 // no issuer specified, match all 384 aliases.add(alias); 385 if (debug != null && Debug.isOn("keymanager")) { 386 System.out.println("matching alias: " + alias); 387 } 388 } else { 389 Set<X500Principal> certIssuers = 390 credentials.getIssuerX500Principals(); 391 for (int i = 0; i < x500Issuers.length; i++) { 392 if (certIssuers.contains(issuers[i])) { 393 aliases.add(alias); 394 if (debug != null && Debug.isOn("keymanager")) { 395 System.out.println("matching alias: " + alias); 396 } 397 break; 398 } 399 } 400 } 401 } 402 403 String[] aliasStrings = aliases.toArray(STRING0); 404 return ((aliasStrings.length == 0) ? null : aliasStrings); 405 } 406 407 /* 408 * Convert an array of Principals to an array of X500Principals, if 409 * possible. Principals that cannot be converted are ignored. 410 */ 411 private static X500Principal[] convertPrincipals(Principal[] principals) { 412 List<X500Principal> list = new ArrayList<>(principals.length); 413 for (int i = 0; i < principals.length; i++) { 414 Principal p = principals[i]; 415 if (p instanceof X500Principal) { 416 list.add((X500Principal)p); 417 } else { 418 try { 419 list.add(new X500Principal(p.getName())); 420 } catch (IllegalArgumentException e) { 421 // ignore 422 } 423 } 424 } 425 return list.toArray(new X500Principal[list.size()]); 426 } 427} 428