author | martin |
Tue, 15 Sep 2015 21:56:04 -0700 | |
changeset 32649 | 2ee9017c7597 |
parent 32032 | 22badc53802f |
child 42464 | 38d967704a9f |
permissions | -rw-r--r-- |
2 | 1 |
/* |
31688
42c9b194a469
8073894: Getting to the root of certificate chains
mullan
parents:
25859
diff
changeset
|
2 |
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. |
2 | 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 |
|
5506 | 7 |
* published by the Free Software Foundation. Oracle designates this |
2 | 8 |
* particular file as subject to the "Classpath" exception as provided |
5506 | 9 |
* by Oracle in the LICENSE file that accompanied this code. |
2 | 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 |
* |
|
5506 | 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. |
|
2 | 24 |
*/ |
25 |
||
26 |
package sun.security.validator; |
|
27 |
||
28 |
import java.io.IOException; |
|
29 |
import java.util.*; |
|
30 |
||
31 |
import java.security.*; |
|
32 |
import java.security.cert.*; |
|
33 |
||
34 |
import javax.security.auth.x500.X500Principal; |
|
35 |
||
36 |
import sun.security.x509.X509CertImpl; |
|
37 |
import sun.security.x509.NetscapeCertTypeExtension; |
|
38 |
import sun.security.util.DerValue; |
|
39 |
import sun.security.util.DerInputStream; |
|
40 |
import sun.security.util.ObjectIdentifier; |
|
41 |
||
4190 | 42 |
import sun.security.provider.certpath.AlgorithmChecker; |
11900 | 43 |
import sun.security.provider.certpath.UntrustedChecker; |
4190 | 44 |
|
2 | 45 |
/** |
46 |
* A simple validator implementation. It is based on code from the JSSE |
|
47 |
* X509TrustManagerImpl. This implementation is designed for compatibility with |
|
48 |
* deployed certificates and previous J2SE versions. It will never support |
|
49 |
* more advanced features and will be deemphasized in favor of the PKIX |
|
50 |
* validator going forward. |
|
7040
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
51 |
* <p> |
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
52 |
* {@code SimpleValidator} objects are immutable once they have been created. |
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
53 |
* Please DO NOT add methods that can change the state of an instance once |
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
54 |
* it has been created. |
2 | 55 |
* |
56 |
* @author Andreas Sterbenz |
|
57 |
*/ |
|
58 |
public final class SimpleValidator extends Validator { |
|
59 |
||
60 |
// Constants for the OIDs we need |
|
61 |
||
32649
2ee9017c7597
8136583: Core libraries should use blessed modifier order
martin
parents:
32032
diff
changeset
|
62 |
static final String OID_BASIC_CONSTRAINTS = "2.5.29.19"; |
2 | 63 |
|
32649
2ee9017c7597
8136583: Core libraries should use blessed modifier order
martin
parents:
32032
diff
changeset
|
64 |
static final String OID_NETSCAPE_CERT_TYPE = "2.16.840.1.113730.1.1"; |
2 | 65 |
|
32649
2ee9017c7597
8136583: Core libraries should use blessed modifier order
martin
parents:
32032
diff
changeset
|
66 |
static final String OID_KEY_USAGE = "2.5.29.15"; |
2 | 67 |
|
32649
2ee9017c7597
8136583: Core libraries should use blessed modifier order
martin
parents:
32032
diff
changeset
|
68 |
static final String OID_EXTENDED_KEY_USAGE = "2.5.29.37"; |
2 | 69 |
|
32649
2ee9017c7597
8136583: Core libraries should use blessed modifier order
martin
parents:
32032
diff
changeset
|
70 |
static final String OID_EKU_ANY_USAGE = "2.5.29.37.0"; |
2 | 71 |
|
32649
2ee9017c7597
8136583: Core libraries should use blessed modifier order
martin
parents:
32032
diff
changeset
|
72 |
static final ObjectIdentifier OBJID_NETSCAPE_CERT_TYPE = |
2 | 73 |
NetscapeCertTypeExtension.NetscapeCertType_Id; |
74 |
||
32649
2ee9017c7597
8136583: Core libraries should use blessed modifier order
martin
parents:
32032
diff
changeset
|
75 |
private static final String NSCT_SSL_CA = |
2 | 76 |
NetscapeCertTypeExtension.SSL_CA; |
77 |
||
32649
2ee9017c7597
8136583: Core libraries should use blessed modifier order
martin
parents:
32032
diff
changeset
|
78 |
private static final String NSCT_CODE_SIGNING_CA = |
2 | 79 |
NetscapeCertTypeExtension.OBJECT_SIGNING_CA; |
80 |
||
81 |
/** |
|
82 |
* The trusted certificates as: |
|
83 |
* Map (X500Principal)subject of trusted cert -> List of X509Certificate |
|
84 |
* The list is used because there may be multiple certificates |
|
85 |
* with an identical subject DN. |
|
86 |
*/ |
|
7040
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
87 |
private final Map<X500Principal, List<X509Certificate>> |
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
88 |
trustedX500Principals; |
2 | 89 |
|
90 |
/** |
|
91 |
* Set of the trusted certificates. Present only for |
|
92 |
* getTrustedCertificates(). |
|
93 |
*/ |
|
7040
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
94 |
private final Collection<X509Certificate> trustedCerts; |
2 | 95 |
|
96 |
SimpleValidator(String variant, Collection<X509Certificate> trustedCerts) { |
|
97 |
super(TYPE_SIMPLE, variant); |
|
98 |
this.trustedCerts = trustedCerts; |
|
99 |
trustedX500Principals = |
|
100 |
new HashMap<X500Principal, List<X509Certificate>>(); |
|
101 |
for (X509Certificate cert : trustedCerts) { |
|
102 |
X500Principal principal = cert.getSubjectX500Principal(); |
|
103 |
List<X509Certificate> list = trustedX500Principals.get(principal); |
|
104 |
if (list == null) { |
|
105 |
// this actually should be a set, but duplicate entries |
|
106 |
// are not a problem and we can avoid the Set overhead |
|
107 |
list = new ArrayList<X509Certificate>(2); |
|
108 |
trustedX500Principals.put(principal, list); |
|
109 |
} |
|
110 |
list.add(cert); |
|
111 |
} |
|
112 |
} |
|
113 |
||
114 |
public Collection<X509Certificate> getTrustedCertificates() { |
|
115 |
return trustedCerts; |
|
116 |
} |
|
117 |
||
118 |
/** |
|
119 |
* Perform simple validation of chain. The arguments otherCerts and |
|
120 |
* parameter are ignored. |
|
121 |
*/ |
|
7040
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
122 |
@Override |
2 | 123 |
X509Certificate[] engineValidate(X509Certificate[] chain, |
7040
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
124 |
Collection<X509Certificate> otherCerts, |
32032 | 125 |
List<byte[]> responseList, |
7040
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
126 |
AlgorithmConstraints constraints, |
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
127 |
Object parameter) throws CertificateException { |
2 | 128 |
if ((chain == null) || (chain.length == 0)) { |
129 |
throw new CertificateException |
|
130 |
("null or zero-length certificate chain"); |
|
131 |
} |
|
132 |
||
133 |
// make sure chain includes a trusted cert |
|
134 |
chain = buildTrustedChain(chain); |
|
135 |
||
10709
d865c9f21240
7092375: Security Libraries don't build with javac -Werror
xuelei
parents:
10336
diff
changeset
|
136 |
@SuppressWarnings("deprecation") |
2 | 137 |
Date date = validationDate; |
138 |
if (date == null) { |
|
139 |
date = new Date(); |
|
140 |
} |
|
7040
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
141 |
|
11900 | 142 |
// create distrusted certificates checker |
143 |
UntrustedChecker untrustedChecker = new UntrustedChecker(); |
|
144 |
||
31688
42c9b194a469
8073894: Getting to the root of certificate chains
mullan
parents:
25859
diff
changeset
|
145 |
// check if anchor is untrusted |
42c9b194a469
8073894: Getting to the root of certificate chains
mullan
parents:
25859
diff
changeset
|
146 |
X509Certificate anchorCert = chain[chain.length - 1]; |
42c9b194a469
8073894: Getting to the root of certificate chains
mullan
parents:
25859
diff
changeset
|
147 |
try { |
42c9b194a469
8073894: Getting to the root of certificate chains
mullan
parents:
25859
diff
changeset
|
148 |
untrustedChecker.check(anchorCert); |
42c9b194a469
8073894: Getting to the root of certificate chains
mullan
parents:
25859
diff
changeset
|
149 |
} catch (CertPathValidatorException cpve) { |
42c9b194a469
8073894: Getting to the root of certificate chains
mullan
parents:
25859
diff
changeset
|
150 |
throw new ValidatorException( |
42c9b194a469
8073894: Getting to the root of certificate chains
mullan
parents:
25859
diff
changeset
|
151 |
"Untrusted certificate: "+ anchorCert.getSubjectX500Principal(), |
42c9b194a469
8073894: Getting to the root of certificate chains
mullan
parents:
25859
diff
changeset
|
152 |
ValidatorException.T_UNTRUSTED_CERT, anchorCert, cpve); |
42c9b194a469
8073894: Getting to the root of certificate chains
mullan
parents:
25859
diff
changeset
|
153 |
} |
42c9b194a469
8073894: Getting to the root of certificate chains
mullan
parents:
25859
diff
changeset
|
154 |
|
7040
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
155 |
// create default algorithm constraints checker |
31688
42c9b194a469
8073894: Getting to the root of certificate chains
mullan
parents:
25859
diff
changeset
|
156 |
TrustAnchor anchor = new TrustAnchor(anchorCert, null); |
7040
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
157 |
AlgorithmChecker defaultAlgChecker = new AlgorithmChecker(anchor); |
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
158 |
|
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
159 |
// create application level algorithm constraints checker |
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
160 |
AlgorithmChecker appAlgChecker = null; |
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
161 |
if (constraints != null) { |
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
162 |
appAlgChecker = new AlgorithmChecker(anchor, constraints); |
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
163 |
} |
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
164 |
|
2 | 165 |
// verify top down, starting at the certificate issued by |
166 |
// the trust anchor |
|
2926 | 167 |
int maxPathLength = chain.length - 1; |
2 | 168 |
for (int i = chain.length - 2; i >= 0; i--) { |
169 |
X509Certificate issuerCert = chain[i + 1]; |
|
170 |
X509Certificate cert = chain[i]; |
|
171 |
||
11900 | 172 |
// check untrusted certificate |
173 |
try { |
|
174 |
// Untrusted checker does not care about the unresolved |
|
175 |
// critical extensions. |
|
176 |
untrustedChecker.check(cert, Collections.<String>emptySet()); |
|
177 |
} catch (CertPathValidatorException cpve) { |
|
178 |
throw new ValidatorException( |
|
179 |
"Untrusted certificate: " + cert.getSubjectX500Principal(), |
|
180 |
ValidatorException.T_UNTRUSTED_CERT, cert, cpve); |
|
181 |
} |
|
182 |
||
4190 | 183 |
// check certificate algorithm |
184 |
try { |
|
8163
d9bcc1208691
7011497: new CertPathValidatorException.BasicReason enum constant for constrained algorithm
xuelei
parents:
7040
diff
changeset
|
185 |
// Algorithm checker does not care about the unresolved |
d9bcc1208691
7011497: new CertPathValidatorException.BasicReason enum constant for constrained algorithm
xuelei
parents:
7040
diff
changeset
|
186 |
// critical extensions. |
7040
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
187 |
defaultAlgChecker.check(cert, Collections.<String>emptySet()); |
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
188 |
if (appAlgChecker != null) { |
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
189 |
appAlgChecker.check(cert, Collections.<String>emptySet()); |
659824c2a550
6792180: Enhance to reject weak algorithms or conform to crypto recommendations
xuelei
parents:
5506
diff
changeset
|
190 |
} |
4190 | 191 |
} catch (CertPathValidatorException cpve) { |
192 |
throw new ValidatorException |
|
193 |
(ValidatorException.T_ALGORITHM_DISABLED, cert, cpve); |
|
194 |
} |
|
2 | 195 |
|
196 |
// no validity check for code signing certs |
|
197 |
if ((variant.equals(VAR_CODE_SIGNING) == false) |
|
198 |
&& (variant.equals(VAR_JCE_SIGNING) == false)) { |
|
199 |
cert.checkValidity(date); |
|
200 |
} |
|
201 |
||
202 |
// check name chaining |
|
203 |
if (cert.getIssuerX500Principal().equals( |
|
204 |
issuerCert.getSubjectX500Principal()) == false) { |
|
205 |
throw new ValidatorException |
|
206 |
(ValidatorException.T_NAME_CHAINING, cert); |
|
207 |
} |
|
208 |
||
209 |
// check signature |
|
210 |
try { |
|
211 |
cert.verify(issuerCert.getPublicKey()); |
|
212 |
} catch (GeneralSecurityException e) { |
|
213 |
throw new ValidatorException |
|
214 |
(ValidatorException.T_SIGNATURE_ERROR, cert, e); |
|
215 |
} |
|
216 |
||
217 |
// check extensions for CA certs |
|
218 |
if (i != 0) { |
|
2926 | 219 |
maxPathLength = checkExtensions(cert, maxPathLength); |
2 | 220 |
} |
221 |
} |
|
222 |
||
223 |
return chain; |
|
224 |
} |
|
225 |
||
2926 | 226 |
private int checkExtensions(X509Certificate cert, int maxPathLen) |
2 | 227 |
throws CertificateException { |
228 |
Set<String> critSet = cert.getCriticalExtensionOIDs(); |
|
229 |
if (critSet == null) { |
|
230 |
critSet = Collections.<String>emptySet(); |
|
231 |
} |
|
232 |
||
233 |
// Check the basic constraints extension |
|
2926 | 234 |
int pathLenConstraint = |
235 |
checkBasicConstraints(cert, critSet, maxPathLen); |
|
2 | 236 |
|
237 |
// Check the key usage and extended key usage extensions |
|
238 |
checkKeyUsage(cert, critSet); |
|
239 |
||
240 |
// check Netscape certificate type extension |
|
241 |
checkNetscapeCertType(cert, critSet); |
|
242 |
||
243 |
if (!critSet.isEmpty()) { |
|
244 |
throw new ValidatorException |
|
245 |
("Certificate contains unknown critical extensions: " + critSet, |
|
246 |
ValidatorException.T_CA_EXTENSIONS, cert); |
|
247 |
} |
|
2926 | 248 |
|
249 |
return pathLenConstraint; |
|
2 | 250 |
} |
251 |
||
252 |
private void checkNetscapeCertType(X509Certificate cert, |
|
253 |
Set<String> critSet) throws CertificateException { |
|
254 |
if (variant.equals(VAR_GENERIC)) { |
|
255 |
// nothing |
|
256 |
} else if (variant.equals(VAR_TLS_CLIENT) |
|
257 |
|| variant.equals(VAR_TLS_SERVER)) { |
|
258 |
if (getNetscapeCertTypeBit(cert, NSCT_SSL_CA) == false) { |
|
259 |
throw new ValidatorException |
|
260 |
("Invalid Netscape CertType extension for SSL CA " |
|
261 |
+ "certificate", |
|
262 |
ValidatorException.T_CA_EXTENSIONS, cert); |
|
263 |
} |
|
264 |
critSet.remove(OID_NETSCAPE_CERT_TYPE); |
|
265 |
} else if (variant.equals(VAR_CODE_SIGNING) |
|
266 |
|| variant.equals(VAR_JCE_SIGNING)) { |
|
267 |
if (getNetscapeCertTypeBit(cert, NSCT_CODE_SIGNING_CA) == false) { |
|
268 |
throw new ValidatorException |
|
269 |
("Invalid Netscape CertType extension for code " |
|
270 |
+ "signing CA certificate", |
|
271 |
ValidatorException.T_CA_EXTENSIONS, cert); |
|
272 |
} |
|
273 |
critSet.remove(OID_NETSCAPE_CERT_TYPE); |
|
274 |
} else { |
|
275 |
throw new CertificateException("Unknown variant " + variant); |
|
276 |
} |
|
277 |
} |
|
278 |
||
279 |
/** |
|
280 |
* Get the value of the specified bit in the Netscape certificate type |
|
281 |
* extension. If the extension is not present at all, we return true. |
|
282 |
*/ |
|
283 |
static boolean getNetscapeCertTypeBit(X509Certificate cert, String type) { |
|
284 |
try { |
|
285 |
NetscapeCertTypeExtension ext; |
|
286 |
if (cert instanceof X509CertImpl) { |
|
287 |
X509CertImpl certImpl = (X509CertImpl)cert; |
|
288 |
ObjectIdentifier oid = OBJID_NETSCAPE_CERT_TYPE; |
|
289 |
ext = (NetscapeCertTypeExtension)certImpl.getExtension(oid); |
|
290 |
if (ext == null) { |
|
291 |
return true; |
|
292 |
} |
|
293 |
} else { |
|
294 |
byte[] extVal = cert.getExtensionValue(OID_NETSCAPE_CERT_TYPE); |
|
295 |
if (extVal == null) { |
|
296 |
return true; |
|
297 |
} |
|
298 |
DerInputStream in = new DerInputStream(extVal); |
|
299 |
byte[] encoded = in.getOctetString(); |
|
300 |
encoded = new DerValue(encoded).getUnalignedBitString() |
|
301 |
.toByteArray(); |
|
302 |
ext = new NetscapeCertTypeExtension(encoded); |
|
303 |
} |
|
10336
0bb1999251f8
7064075: Security libraries don't build with javac -Xlint:all,-deprecation -Werror
jjg
parents:
8163
diff
changeset
|
304 |
Boolean val = ext.get(type); |
2 | 305 |
return val.booleanValue(); |
306 |
} catch (IOException e) { |
|
307 |
return false; |
|
308 |
} |
|
309 |
} |
|
310 |
||
2926 | 311 |
private int checkBasicConstraints(X509Certificate cert, |
312 |
Set<String> critSet, int maxPathLen) throws CertificateException { |
|
2 | 313 |
|
314 |
critSet.remove(OID_BASIC_CONSTRAINTS); |
|
315 |
int constraints = cert.getBasicConstraints(); |
|
316 |
// reject, if extension missing or not a CA (constraints == -1) |
|
317 |
if (constraints < 0) { |
|
318 |
throw new ValidatorException("End user tried to act as a CA", |
|
319 |
ValidatorException.T_CA_EXTENSIONS, cert); |
|
320 |
} |
|
2926 | 321 |
|
322 |
// if the certificate is self-issued, ignore the pathLenConstraint |
|
323 |
// checking. |
|
324 |
if (!X509CertImpl.isSelfIssued(cert)) { |
|
12678
e40db477dd56
7166570: JSSE certificate validation has started to fail for certificate chains
xuelei
parents:
11902
diff
changeset
|
325 |
if (maxPathLen <= 0) { |
2926 | 326 |
throw new ValidatorException("Violated path length constraints", |
327 |
ValidatorException.T_CA_EXTENSIONS, cert); |
|
328 |
} |
|
329 |
||
330 |
maxPathLen--; |
|
2 | 331 |
} |
2926 | 332 |
|
333 |
if (maxPathLen > constraints) { |
|
334 |
maxPathLen = constraints; |
|
335 |
} |
|
336 |
||
337 |
return maxPathLen; |
|
2 | 338 |
} |
339 |
||
340 |
/* |
|
341 |
* Verify the key usage and extended key usage for intermediate |
|
342 |
* certificates. |
|
343 |
*/ |
|
344 |
private void checkKeyUsage(X509Certificate cert, Set<String> critSet) |
|
345 |
throws CertificateException { |
|
346 |
||
347 |
critSet.remove(OID_KEY_USAGE); |
|
348 |
// EKU irrelevant in CA certificates |
|
349 |
critSet.remove(OID_EXTENDED_KEY_USAGE); |
|
350 |
||
351 |
// check key usage extension |
|
352 |
boolean[] keyUsageInfo = cert.getKeyUsage(); |
|
353 |
if (keyUsageInfo != null) { |
|
354 |
// keyUsageInfo[5] is for keyCertSign. |
|
355 |
if ((keyUsageInfo.length < 6) || (keyUsageInfo[5] == false)) { |
|
356 |
throw new ValidatorException |
|
357 |
("Wrong key usage: expected keyCertSign", |
|
358 |
ValidatorException.T_CA_EXTENSIONS, cert); |
|
359 |
} |
|
360 |
} |
|
361 |
} |
|
362 |
||
363 |
/** |
|
364 |
* Build a trusted certificate chain. This method always returns a chain |
|
365 |
* with a trust anchor as the final cert in the chain. If no trust anchor |
|
366 |
* could be found, a CertificateException is thrown. |
|
367 |
*/ |
|
368 |
private X509Certificate[] buildTrustedChain(X509Certificate[] chain) |
|
369 |
throws CertificateException { |
|
370 |
List<X509Certificate> c = new ArrayList<X509Certificate>(chain.length); |
|
371 |
// scan chain starting at EE cert |
|
372 |
// if a trusted certificate is found, append it and return |
|
373 |
for (int i = 0; i < chain.length; i++) { |
|
374 |
X509Certificate cert = chain[i]; |
|
375 |
X509Certificate trustedCert = getTrustedCertificate(cert); |
|
376 |
if (trustedCert != null) { |
|
377 |
c.add(trustedCert); |
|
378 |
return c.toArray(CHAIN0); |
|
379 |
} |
|
380 |
c.add(cert); |
|
381 |
} |
|
2926 | 382 |
|
2 | 383 |
// check if we can append a trusted cert |
384 |
X509Certificate cert = chain[chain.length - 1]; |
|
385 |
X500Principal subject = cert.getSubjectX500Principal(); |
|
386 |
X500Principal issuer = cert.getIssuerX500Principal(); |
|
2926 | 387 |
List<X509Certificate> list = trustedX500Principals.get(issuer); |
388 |
if (list != null) { |
|
389 |
X509Certificate trustedCert = list.iterator().next(); |
|
390 |
c.add(trustedCert); |
|
391 |
return c.toArray(CHAIN0); |
|
2 | 392 |
} |
2926 | 393 |
|
2 | 394 |
// no trusted cert found, error |
395 |
throw new ValidatorException(ValidatorException.T_NO_TRUST_ANCHOR); |
|
396 |
} |
|
397 |
||
398 |
/** |
|
399 |
* Return a trusted certificate that matches the input certificate, |
|
400 |
* or null if no such certificate can be found. This method also handles |
|
401 |
* cases where a CA re-issues a trust anchor with the same public key and |
|
402 |
* same subject and issuer names but a new validity period, etc. |
|
403 |
*/ |
|
404 |
private X509Certificate getTrustedCertificate(X509Certificate cert) { |
|
405 |
Principal certSubjectName = cert.getSubjectX500Principal(); |
|
406 |
List<X509Certificate> list = trustedX500Principals.get(certSubjectName); |
|
407 |
if (list == null) { |
|
408 |
return null; |
|
409 |
} |
|
410 |
||
411 |
Principal certIssuerName = cert.getIssuerX500Principal(); |
|
412 |
PublicKey certPublicKey = cert.getPublicKey(); |
|
413 |
||
414 |
for (X509Certificate mycert : list) { |
|
415 |
if (mycert.equals(cert)) { |
|
416 |
return cert; |
|
417 |
} |
|
418 |
if (!mycert.getIssuerX500Principal().equals(certIssuerName)) { |
|
419 |
continue; |
|
420 |
} |
|
421 |
if (!mycert.getPublicKey().equals(certPublicKey)) { |
|
422 |
continue; |
|
423 |
} |
|
424 |
||
425 |
// All tests pass, this must be the one to use... |
|
426 |
return mycert; |
|
427 |
} |
|
428 |
return null; |
|
429 |
} |
|
430 |
||
431 |
} |