1 /* 2 * Copyright (c) 1999, 2011, 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 26 package sun.security.ssl; 27 28 import javax.net.ssl.*; 29 import java.security.*; 30 import java.security.cert.*; 31 import java.security.cert.Certificate; 32 import java.util.*; 33 import java.net.Socket; 34 35 import javax.security.auth.x500.X500Principal; 36 37 38 /** 39 * An implemention 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 */ 68 final 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 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) throws KeyStoreException, 117 NoSuchAlgorithmException, UnrecoverableKeyException { 118 119 credentialsMap = new HashMap<String,X509Credentials>(); 120 serverAliasCache = new HashMap<String,String[]>(); 121 if (ks == null) { 122 return; 123 } 124 125 for (Enumeration<String> aliases = ks.aliases(); 126 aliases.hasMoreElements(); ) { 127 String alias = aliases.nextElement(); 128 if (!ks.isKeyEntry(alias)) { 129 continue; 130 } 131 Key key = ks.getKey(alias, password); 132 if (key instanceof PrivateKey == false) { 133 continue; 134 } 135 Certificate[] certs = ks.getCertificateChain(alias); 136 if ((certs == null) || (certs.length == 0) || 137 !(certs[0] instanceof X509Certificate)) { 138 continue; 139 } 140 if (!(certs instanceof X509Certificate[])) { 141 Certificate[] tmp = new X509Certificate[certs.length]; 142 System.arraycopy(certs, 0, tmp, 0, certs.length); 143 certs = tmp; 144 } 145 146 X509Credentials cred = new X509Credentials((PrivateKey)key, 147 (X509Certificate[])certs); 148 credentialsMap.put(alias, cred); 149 if (debug != null && Debug.isOn("keymanager")) { 150 System.out.println("***"); 151 System.out.println("found key for : " + alias); 152 for (int i = 0; i < certs.length; i++) { 153 System.out.println("chain [" + i + "] = " 154 + certs[i]); 155 } 156 System.out.println("***"); 157 } 158 } 159 } 160 161 /* 162 * Returns the certificate chain associated with the given alias. 163 * 164 * @return the certificate chain (ordered with the user's certificate first 165 * and the root certificate authority last) 166 */ 167 public X509Certificate[] getCertificateChain(String alias) { 168 if (alias == null) { 169 return null; 170 } 171 X509Credentials cred = credentialsMap.get(alias); 172 if (cred == null) { 173 return null; 174 } else { 175 return cred.certificates.clone(); 176 } 177 } 178 179 /* 180 * Returns the key associated with the given alias 181 */ 182 public PrivateKey getPrivateKey(String alias) { 183 if (alias == null) { 184 return null; 185 } 186 X509Credentials cred = credentialsMap.get(alias); 187 if (cred == null) { 188 return null; 189 } else { 190 return cred.privateKey; 191 } 192 } 193 194 /* 195 * Choose an alias to authenticate the client side of a secure 196 * socket given the public key type and the list of 197 * certificate issuer authorities recognized by the peer (if any). 198 */ 199 public String chooseClientAlias(String[] keyTypes, Principal[] issuers, 200 Socket socket) { 201 /* 202 * We currently don't do anything with socket, but 203 * someday we might. It might be a useful hint for 204 * selecting one of the aliases we get back from 205 * getClientAliases(). 206 */ 207 208 if (keyTypes == null) { 209 return null; 210 } 211 212 for (int i = 0; i < keyTypes.length; i++) { 213 String[] aliases = getClientAliases(keyTypes[i], issuers); 214 if ((aliases != null) && (aliases.length > 0)) { 215 return aliases[0]; 216 } 217 } 218 return null; 219 } 220 221 /* 222 * Choose an alias to authenticate the client side of an 223 * <code>SSLEngine</code> connection given the public key type 224 * and the list of certificate issuer authorities recognized by 225 * the peer (if any). 226 * 227 * @since 1.5 228 */ 229 public String chooseEngineClientAlias(String[] keyType, 230 Principal[] issuers, SSLEngine engine) { 231 /* 232 * If we ever start using socket as a selection criteria, 233 * we'll need to adjust this. 234 */ 235 return chooseClientAlias(keyType, issuers, null); 236 } 237 238 /* 239 * Choose an alias to authenticate the server side of a secure 240 * socket given the public key type and the list of 241 * certificate issuer authorities recognized by the peer (if any). 242 */ 243 public String chooseServerAlias(String keyType, 244 Principal[] issuers, Socket socket) { 245 /* 246 * We currently don't do anything with socket, but 247 * someday we might. It might be a useful hint for 248 * selecting one of the aliases we get back from 249 * getServerAliases(). 250 */ 251 if (keyType == null) { 252 return null; 253 } 254 255 String[] aliases; 256 257 if (issuers == null || issuers.length == 0) { 258 aliases = serverAliasCache.get(keyType); 259 if (aliases == null) { 260 aliases = getServerAliases(keyType, issuers); 261 // Cache the result (positive and negative lookups) 262 if (aliases == null) { 263 aliases = STRING0; 264 } 265 serverAliasCache.put(keyType, aliases); 266 } 267 } else { 268 aliases = getServerAliases(keyType, issuers); 269 } 270 if ((aliases != null) && (aliases.length > 0)) { 271 return aliases[0]; 272 } 273 return null; 274 } 275 276 /* 277 * Choose an alias to authenticate the server side of an 278 * <code>SSLEngine</code> connection given the public key type 279 * and the list of certificate issuer authorities recognized by 280 * the peer (if any). 281 * 282 * @since 1.5 283 */ 284 public String chooseEngineServerAlias(String keyType, 285 Principal[] issuers, SSLEngine engine) { 286 /* 287 * If we ever start using socket as a selection criteria, 288 * we'll need to adjust this. 289 */ 290 return chooseServerAlias(keyType, issuers, null); 291 } 292 293 /* 294 * Get the matching aliases for authenticating the client side of a secure 295 * socket given the public key type and the list of 296 * certificate issuer authorities recognized by the peer (if any). 297 */ 298 public String[] getClientAliases(String keyType, Principal[] issuers) { 299 return getAliases(keyType, issuers); 300 } 301 302 /* 303 * Get the matching aliases for authenticating the server side of a secure 304 * socket given the public key type and the list of 305 * certificate issuer authorities recognized by the peer (if any). 306 */ 307 public String[] getServerAliases(String keyType, Principal[] issuers) { 308 return getAliases(keyType, issuers); 309 } 310 311 /* 312 * Get the matching aliases for authenticating the either 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 * Issuers comes to us in the form of X500Principal[]. 317 */ 318 private String[] getAliases(String keyType, Principal[] issuers) { 319 if (keyType == null) { 320 return null; 321 } 322 if (issuers == null) { 323 issuers = new X500Principal[0]; 324 } 325 if (issuers instanceof X500Principal[] == false) { 326 // normally, this will never happen but try to recover if it does 327 issuers = convertPrincipals(issuers); 328 } 329 String sigType; 330 if (keyType.contains("_")) { 331 int k = keyType.indexOf("_"); 332 sigType = keyType.substring(k + 1); 333 keyType = keyType.substring(0, k); 334 } else { 335 sigType = null; 336 } 337 338 X500Principal[] x500Issuers = (X500Principal[])issuers; 339 // the algorithm below does not produce duplicates, so avoid Set 340 List<String> aliases = new ArrayList<>(); 341 342 for (Map.Entry<String,X509Credentials> entry : 343 credentialsMap.entrySet()) { 344 345 String alias = entry.getKey(); 346 X509Credentials credentials = entry.getValue(); 347 X509Certificate[] certs = credentials.certificates; 348 349 if (!keyType.equals(certs[0].getPublicKey().getAlgorithm())) { 350 continue; 351 } 352 if (sigType != null) { 353 if (certs.length > 1) { 354 // if possible, check the public key in the issuer cert 355 if (!sigType.equals(certs[1].getPublicKey().getAlgorithm())) { 356 continue; 357 } 358 } else { 359 // Check the signature algorithm of the certificate itself. 360 // Look for the "withRSA" in "SHA1withRSA", etc. 361 String sigAlgName = 362 certs[0].getSigAlgName().toUpperCase(Locale.ENGLISH); 363 String pattern = "WITH" + sigType.toUpperCase(Locale.ENGLISH); 364 if (sigAlgName.contains(pattern) == false) { 365 continue; 366 } 367 } 368 } 369 370 if (issuers.length == 0) { 371 // no issuer specified, match all 372 aliases.add(alias); 373 if (debug != null && Debug.isOn("keymanager")) { 374 System.out.println("matching alias: " + alias); 375 } 376 } else { 377 Set<X500Principal> certIssuers = 378 credentials.getIssuerX500Principals(); 379 for (int i = 0; i < x500Issuers.length; i++) { 380 if (certIssuers.contains(issuers[i])) { 381 aliases.add(alias); 382 if (debug != null && Debug.isOn("keymanager")) { 383 System.out.println("matching alias: " + alias); 384 } 385 break; 386 } 387 } 388 } 389 } 390 391 String[] aliasStrings = aliases.toArray(STRING0); 392 return ((aliasStrings.length == 0) ? null : aliasStrings); 393 } 394 395 /* 396 * Convert an array of Principals to an array of X500Principals, if 397 * possible. Principals that cannot be converted are ignored. 398 */ 399 private static X500Principal[] convertPrincipals(Principal[] principals) { 400 List<X500Principal> list = new ArrayList<>(principals.length); 401 for (int i = 0; i < principals.length; i++) { 402 Principal p = principals[i]; 403 if (p instanceof X500Principal) { 404 list.add((X500Principal)p); 405 } else { 406 try { 407 list.add(new X500Principal(p.getName())); 408 } catch (IllegalArgumentException e) { 409 // ignore 410 } 411 } 412 } 413 return list.toArray(new X500Principal[list.size()]); 414 } 415 416 }