|
1 /* |
|
2 * Copyright 1997-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 |
|
26 |
|
27 package sun.security.ssl; |
|
28 |
|
29 import java.util.*; |
|
30 import java.security.*; |
|
31 import java.security.cert.*; |
|
32 |
|
33 import javax.net.ssl.*; |
|
34 |
|
35 import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager; |
|
36 |
|
37 import sun.security.validator.*; |
|
38 |
|
39 import sun.security.util.HostnameChecker; |
|
40 |
|
41 /** |
|
42 * This class implements the SunJSSE X.509 trust manager using the internal |
|
43 * validator API in J2SE core. The logic in this class is minimal.<p> |
|
44 * |
|
45 * This class supports both the Simple validation algorithm from previous |
|
46 * JSSE versions and PKIX validation. Currently, it is not possible for the |
|
47 * application to specify PKIX parameters other than trust anchors. This will |
|
48 * be fixed in a future release using new APIs. When that happens, it may also |
|
49 * make sense to separate the Simple and PKIX trust managers into separate |
|
50 * classes. |
|
51 * |
|
52 * @author Andreas Sterbenz |
|
53 * @author Xuelei Fan |
|
54 */ |
|
55 final class X509TrustManagerImpl extends X509ExtendedTrustManager |
|
56 implements X509TrustManager { |
|
57 |
|
58 /** |
|
59 * Flag indicating whether to enable revocation check for the PKIX trust |
|
60 * manager. Typically, this will only work if the PKIX implementation |
|
61 * supports CRL distribution points as we do not manually setup CertStores. |
|
62 */ |
|
63 private final static boolean checkRevocation = |
|
64 Debug.getBooleanProperty("com.sun.net.ssl.checkRevocation", false); |
|
65 |
|
66 private final String validatorType; |
|
67 |
|
68 /** |
|
69 * The Set of trusted X509Certificates. |
|
70 */ |
|
71 private final Collection<X509Certificate> trustedCerts; |
|
72 |
|
73 private final PKIXBuilderParameters pkixParams; |
|
74 |
|
75 // note that we need separate validator for client and server due to |
|
76 // the different extension checks. They are initialized lazily on demand. |
|
77 private volatile Validator clientValidator, serverValidator; |
|
78 |
|
79 private static final Debug debug = Debug.getInstance("ssl"); |
|
80 |
|
81 X509TrustManagerImpl(String validatorType, KeyStore ks) |
|
82 throws KeyStoreException { |
|
83 this.validatorType = validatorType; |
|
84 this.pkixParams = null; |
|
85 if (ks == null) { |
|
86 trustedCerts = Collections.<X509Certificate>emptySet(); |
|
87 } else { |
|
88 trustedCerts = KeyStores.getTrustedCerts(ks); |
|
89 } |
|
90 showTrustedCerts(); |
|
91 } |
|
92 |
|
93 X509TrustManagerImpl(String validatorType, PKIXBuilderParameters params) { |
|
94 this.validatorType = validatorType; |
|
95 this.pkixParams = params; |
|
96 // create server validator eagerly so that we can conveniently |
|
97 // get the trusted certificates |
|
98 // clients need it anyway eventually, and servers will not mind |
|
99 // the little extra footprint |
|
100 Validator v = getValidator(Validator.VAR_TLS_SERVER); |
|
101 trustedCerts = v.getTrustedCertificates(); |
|
102 serverValidator = v; |
|
103 showTrustedCerts(); |
|
104 } |
|
105 |
|
106 private void showTrustedCerts() { |
|
107 if (debug != null && Debug.isOn("trustmanager")) { |
|
108 for (X509Certificate cert : trustedCerts) { |
|
109 System.out.println("adding as trusted cert:"); |
|
110 System.out.println(" Subject: " |
|
111 + cert.getSubjectX500Principal()); |
|
112 System.out.println(" Issuer: " |
|
113 + cert.getIssuerX500Principal()); |
|
114 System.out.println(" Algorithm: " |
|
115 + cert.getPublicKey().getAlgorithm() |
|
116 + "; Serial number: 0x" |
|
117 + cert.getSerialNumber().toString(16)); |
|
118 System.out.println(" Valid from " |
|
119 + cert.getNotBefore() + " until " |
|
120 + cert.getNotAfter()); |
|
121 System.out.println(); |
|
122 } |
|
123 } |
|
124 } |
|
125 |
|
126 private Validator getValidator(String variant) { |
|
127 Validator v; |
|
128 if (pkixParams == null) { |
|
129 v = Validator.getInstance(validatorType, variant, trustedCerts); |
|
130 // if the PKIX validator is created from a KeyStore, |
|
131 // disable revocation checking |
|
132 if (v instanceof PKIXValidator) { |
|
133 PKIXValidator pkixValidator = (PKIXValidator)v; |
|
134 pkixValidator.getParameters().setRevocationEnabled |
|
135 (checkRevocation); |
|
136 } |
|
137 } else { |
|
138 v = Validator.getInstance(validatorType, variant, pkixParams); |
|
139 } |
|
140 return v; |
|
141 } |
|
142 |
|
143 private static X509Certificate[] validate(Validator v, |
|
144 X509Certificate[] chain, String authType) throws CertificateException { |
|
145 Object o = JsseJce.beginFipsProvider(); |
|
146 try { |
|
147 return v.validate(chain, null, authType); |
|
148 } finally { |
|
149 JsseJce.endFipsProvider(o); |
|
150 } |
|
151 } |
|
152 |
|
153 /** |
|
154 * Returns true if the client certificate can be trusted. |
|
155 * |
|
156 * @param chain certificates which establish an identity for the client. |
|
157 * Chains of arbitrary length are supported, and certificates |
|
158 * marked internally as trusted will short-circuit signature checks. |
|
159 * @throws IllegalArgumentException if null or zero-length chain |
|
160 * is passed in for the chain parameter or if null or zero-length |
|
161 * string is passed in for the authType parameter. |
|
162 * @throws CertificateException if the certificate chain is not trusted |
|
163 * by this TrustManager. |
|
164 */ |
|
165 public void checkClientTrusted(X509Certificate chain[], String authType) |
|
166 throws CertificateException { |
|
167 if (chain == null || chain.length == 0) { |
|
168 throw new IllegalArgumentException( |
|
169 "null or zero-length certificate chain"); |
|
170 } |
|
171 if (authType == null || authType.length() == 0) { |
|
172 throw new IllegalArgumentException( |
|
173 "null or zero-length authentication type"); |
|
174 } |
|
175 |
|
176 // assume double checked locking with a volatile flag works |
|
177 // (guaranteed under the new Tiger memory model) |
|
178 Validator v = clientValidator; |
|
179 if (v == null) { |
|
180 synchronized (this) { |
|
181 v = clientValidator; |
|
182 if (v == null) { |
|
183 v = getValidator(Validator.VAR_TLS_CLIENT); |
|
184 clientValidator = v; |
|
185 } |
|
186 } |
|
187 } |
|
188 X509Certificate[] trustedChain = validate(v, chain, null); |
|
189 if (debug != null && Debug.isOn("trustmanager")) { |
|
190 System.out.println("Found trusted certificate:"); |
|
191 System.out.println(trustedChain[trustedChain.length - 1]); |
|
192 } |
|
193 } |
|
194 |
|
195 /** |
|
196 * Returns true if the server certifcate can be trusted. |
|
197 * |
|
198 * @param chain certificates which establish an identity for the server. |
|
199 * Chains of arbitrary length are supported, and certificates |
|
200 * marked internally as trusted will short-circuit signature checks. |
|
201 * @throws IllegalArgumentException if null or zero-length chain |
|
202 * is passed in for the chain parameter or if null or zero-length |
|
203 * string is passed in for the authType parameter. |
|
204 * @throws CertificateException if the certificate chain is not trusted |
|
205 * by this TrustManager. |
|
206 */ |
|
207 public void checkServerTrusted(X509Certificate chain[], String authType) |
|
208 throws CertificateException { |
|
209 if (chain == null || chain.length == 0) { |
|
210 throw new IllegalArgumentException( |
|
211 "null or zero-length certificate chain"); |
|
212 } |
|
213 if (authType == null || authType.length() == 0) { |
|
214 throw new IllegalArgumentException( |
|
215 "null or zero-length authentication type"); |
|
216 } |
|
217 |
|
218 // assume double checked locking with a volatile flag works |
|
219 // (guaranteed under the new Tiger memory model) |
|
220 Validator v = serverValidator; |
|
221 if (v == null) { |
|
222 synchronized (this) { |
|
223 v = serverValidator; |
|
224 if (v == null) { |
|
225 v = getValidator(Validator.VAR_TLS_SERVER); |
|
226 serverValidator = v; |
|
227 } |
|
228 } |
|
229 } |
|
230 X509Certificate[] trustedChain = validate(v, chain, authType); |
|
231 if (debug != null && Debug.isOn("trustmanager")) { |
|
232 System.out.println("Found trusted certificate:"); |
|
233 System.out.println(trustedChain[trustedChain.length - 1]); |
|
234 } |
|
235 } |
|
236 |
|
237 /** |
|
238 * Returns a list of CAs accepted to authenticate entities for the |
|
239 * specified purpose. |
|
240 * |
|
241 * @param purpose activity for which CAs should be trusted |
|
242 * @return list of CAs accepted for authenticating such tasks |
|
243 */ |
|
244 public X509Certificate[] getAcceptedIssuers() { |
|
245 X509Certificate[] certsArray = new X509Certificate[trustedCerts.size()]; |
|
246 trustedCerts.toArray(certsArray); |
|
247 return certsArray; |
|
248 } |
|
249 |
|
250 /** |
|
251 * Given the partial or complete certificate chain provided by the |
|
252 * peer, check its identity and build a certificate path to a trusted |
|
253 * root, return if it can be validated and is trusted for client SSL |
|
254 * authentication based on the authentication type. |
|
255 */ |
|
256 public void checkClientTrusted(X509Certificate[] chain, String authType, |
|
257 String hostname, String algorithm) throws CertificateException { |
|
258 checkClientTrusted(chain, authType); |
|
259 checkIdentity(hostname, chain[0], algorithm); |
|
260 } |
|
261 |
|
262 /** |
|
263 * Given the partial or complete certificate chain provided by the |
|
264 * peer, check its identity and build a certificate path to a trusted |
|
265 * root, return if it can be validated and is trusted for server SSL |
|
266 * authentication based on the authentication type. |
|
267 */ |
|
268 public void checkServerTrusted(X509Certificate[] chain, String authType, |
|
269 String hostname, String algorithm) throws CertificateException { |
|
270 checkServerTrusted(chain, authType); |
|
271 checkIdentity(hostname, chain[0], algorithm); |
|
272 } |
|
273 |
|
274 // Identify the peer by its certificate and hostname. |
|
275 private void checkIdentity(String hostname, X509Certificate cert, |
|
276 String algorithm) throws CertificateException { |
|
277 if (algorithm != null && algorithm.length() != 0) { |
|
278 // if IPv6 strip off the "[]" |
|
279 if (hostname != null && hostname.startsWith("[") && |
|
280 hostname.endsWith("]")) { |
|
281 hostname = hostname.substring(1, hostname.length()-1); |
|
282 } |
|
283 |
|
284 if (algorithm.equalsIgnoreCase("HTTPS")) { |
|
285 HostnameChecker.getInstance(HostnameChecker.TYPE_TLS).match( |
|
286 hostname, cert); |
|
287 } else if (algorithm.equalsIgnoreCase("LDAP")) { |
|
288 HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP).match( |
|
289 hostname, cert); |
|
290 } else { |
|
291 throw new CertificateException( |
|
292 "Unknown identification algorithm: " + algorithm); |
|
293 } |
|
294 } |
|
295 } |
|
296 } |