blob: a111f79b42761bacf3a8672da6ef41f50da4da06 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1999-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 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 */
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 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 (X509Certificate[])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 = (String[])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<String>();
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 = (String[])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<X500Principal>(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}