Showing the changes to java/pkg/sun/security/validator/PKIXValidator.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,389 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2002, 2010, 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.validator; | ||
| 27 | |||
| 28 | import java.util.*; | ||
| 29 | |||
| 30 | import java.security.*; | ||
| 31 | import java.security.cert.*; | ||
| 32 | |||
| 33 | import javax.security.auth.x500.X500Principal; | ||
| 34 | import sun.security.action.GetBooleanAction; | ||
| 35 | import sun.security.provider.certpath.AlgorithmChecker; | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Validator implementation built on the PKIX CertPath API. This | ||
| 39 | * implementation will be emphasized going forward.<p> | ||
| 40 | * <p> | ||
| 41 | * Note that the validate() implementation tries to use a PKIX validator | ||
| 42 | * if that appears possible and a PKIX builder otherwise. This increases | ||
| 43 | * performance and currently also leads to better exception messages | ||
| 44 | * in case of failures. | ||
| 45 | * <p> | ||
| 46 | * {@code PKIXValidator} objects are immutable once they have been created. | ||
| 47 | * Please DO NOT add methods that can change the state of an instance once | ||
| 48 | * it has been created. | ||
| 49 | * | ||
| 50 | * @author Andreas Sterbenz | ||
| 51 | */ | ||
| 52 | public final class PKIXValidator extends Validator { | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Flag indicating whether to enable revocation check for the PKIX trust | ||
| 56 | * manager. Typically, this will only work if the PKIX implementation | ||
| 57 | * supports CRL distribution points as we do not manually setup CertStores. | ||
| 58 | */ | ||
| 59 | private final static boolean checkTLSRevocation = | ||
| 60 | AccessController.doPrivileged | ||
| 61 | (new GetBooleanAction("com.sun.net.ssl.checkRevocation")); | ||
| 62 | |||
| 63 | // enable use of the validator if possible | ||
| 64 | private final static boolean TRY_VALIDATOR = true; | ||
| 65 | |||
| 66 | private final Set<X509Certificate> trustedCerts; | ||
| 67 | private final PKIXBuilderParameters parameterTemplate; | ||
| 68 | private int certPathLength = -1; | ||
| 69 | |||
| 70 | // needed only for the validator | ||
| 71 | private final Map<X500Principal, List<PublicKey>> trustedSubjects; | ||
| 72 | private final CertificateFactory factory; | ||
| 73 | |||
| 74 | private final boolean plugin; | ||
| 75 | |||
| 76 | PKIXValidator(String variant, Collection<X509Certificate> trustedCerts) { | ||
| 77 | super(TYPE_PKIX, variant); | ||
| 78 | if (trustedCerts instanceof Set) { | ||
| 79 | this.trustedCerts = (Set<X509Certificate>)trustedCerts; | ||
| 80 | } else { | ||
| 81 | this.trustedCerts = new HashSet<X509Certificate>(trustedCerts); | ||
| 82 | } | ||
| 83 | Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>(); | ||
| 84 | for (X509Certificate cert : trustedCerts) { | ||
| 85 | trustAnchors.add(new TrustAnchor(cert, null)); | ||
| 86 | } | ||
| 87 | try { | ||
| 88 | parameterTemplate = new PKIXBuilderParameters(trustAnchors, null); | ||
| 89 | } catch (InvalidAlgorithmParameterException e) { | ||
| 90 | throw new RuntimeException("Unexpected error: " + e.toString(), e); | ||
| 91 | } | ||
| 92 | setDefaultParameters(variant); | ||
| 93 | |||
| 94 | // initCommon(); | ||
| 95 | if (TRY_VALIDATOR) { | ||
| 96 | if (TRY_VALIDATOR == false) { | ||
| 97 | return; | ||
| 98 | } | ||
| 99 | trustedSubjects = new HashMap<X500Principal, List<PublicKey>>(); | ||
| 100 | for (X509Certificate cert : trustedCerts) { | ||
| 101 | X500Principal dn = cert.getSubjectX500Principal(); | ||
| 102 | List<PublicKey> keys; | ||
| 103 | if (trustedSubjects.containsKey(dn)) { | ||
| 104 | keys = trustedSubjects.get(dn); | ||
| 105 | } else { | ||
| 106 | keys = new ArrayList<PublicKey>(); | ||
| 107 | trustedSubjects.put(dn, keys); | ||
| 108 | } | ||
| 109 | keys.add(cert.getPublicKey()); | ||
| 110 | } | ||
| 111 | try { | ||
| 112 | factory = CertificateFactory.getInstance("X.509"); | ||
| 113 | } catch (CertificateException e) { | ||
| 114 | throw new RuntimeException("Internal error", e); | ||
| 115 | } | ||
| 116 | plugin = variant.equals(VAR_PLUGIN_CODE_SIGNING); | ||
| 117 | } else { | ||
| 118 | plugin = false; | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | PKIXValidator(String variant, PKIXBuilderParameters params) { | ||
| 123 | super(TYPE_PKIX, variant); | ||
| 124 | trustedCerts = new HashSet<X509Certificate>(); | ||
| 125 | for (TrustAnchor anchor : params.getTrustAnchors()) { | ||
| 126 | X509Certificate cert = anchor.getTrustedCert(); | ||
| 127 | if (cert != null) { | ||
| 128 | trustedCerts.add(cert); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | parameterTemplate = params; | ||
| 132 | |||
| 133 | // initCommon(); | ||
| 134 | if (TRY_VALIDATOR) { | ||
| 135 | if (TRY_VALIDATOR == false) { | ||
| 136 | return; | ||
| 137 | } | ||
| 138 | trustedSubjects = new HashMap<X500Principal, List<PublicKey>>(); | ||
| 139 | for (X509Certificate cert : trustedCerts) { | ||
| 140 | X500Principal dn = cert.getSubjectX500Principal(); | ||
| 141 | List<PublicKey> keys; | ||
| 142 | if (trustedSubjects.containsKey(dn)) { | ||
| 143 | keys = trustedSubjects.get(dn); | ||
| 144 | } else { | ||
| 145 | keys = new ArrayList<PublicKey>(); | ||
| 146 | trustedSubjects.put(dn, keys); | ||
| 147 | } | ||
| 148 | keys.add(cert.getPublicKey()); | ||
| 149 | } | ||
| 150 | try { | ||
| 151 | factory = CertificateFactory.getInstance("X.509"); | ||
| 152 | } catch (CertificateException e) { | ||
| 153 | throw new RuntimeException("Internal error", e); | ||
| 154 | } | ||
| 155 | plugin = variant.equals(VAR_PLUGIN_CODE_SIGNING); | ||
| 156 | } else { | ||
| 157 | plugin = false; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | public Collection<X509Certificate> getTrustedCertificates() { | ||
| 162 | return trustedCerts; | ||
| 163 | } | ||
| 164 | |||
| 165 | /** | ||
| 166 | * Returns the length of the last certification path that is validated by | ||
| 167 | * CertPathValidator. This is intended primarily as a callback mechanism | ||
| 168 | * for PKIXCertPathCheckers to determine the length of the certification | ||
| 169 | * path that is being validated. It is necessary since engineValidate() | ||
| 170 | * may modify the length of the path. | ||
| 171 | * | ||
| 172 | * @return the length of the last certification path passed to | ||
| 173 | * CertPathValidator.validate, or -1 if it has not been invoked yet | ||
| 174 | */ | ||
| 175 | public int getCertPathLength() { // mutable, should be private | ||
| 176 | return certPathLength; | ||
| 177 | } | ||
| 178 | |||
| 179 | /** | ||
| 180 | * Set J2SE global default PKIX parameters. Currently, hardcoded to disable | ||
| 181 | * revocation checking. In the future, this should be configurable. | ||
| 182 | */ | ||
| 183 | private void setDefaultParameters(String variant) { | ||
| 184 | if ((variant == Validator.VAR_TLS_SERVER) || | ||
| 185 | (variant == Validator.VAR_TLS_CLIENT)) { | ||
| 186 | parameterTemplate.setRevocationEnabled(checkTLSRevocation); | ||
| 187 | } else { | ||
| 188 | parameterTemplate.setRevocationEnabled(false); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | /** | ||
| 193 | * Return the PKIX parameters used by this instance. An application may | ||
| 194 | * modify the parameters but must make sure not to perform any concurrent | ||
| 195 | * validations. | ||
| 196 | */ | ||
| 197 | public PKIXBuilderParameters getParameters() { // mutable, should be private | ||
| 198 | return parameterTemplate; | ||
| 199 | } | ||
| 200 | |||
| 201 | @Override | ||
| 202 | X509Certificate[] engineValidate(X509Certificate[] chain, | ||
| 203 | Collection<X509Certificate> otherCerts, | ||
| 204 | AlgorithmConstraints constraints, | ||
| 205 | Object parameter) throws CertificateException { | ||
| 206 | if ((chain == null) || (chain.length == 0)) { | ||
| 207 | throw new CertificateException | ||
| 208 | ("null or zero-length certificate chain"); | ||
| 209 | } | ||
| 210 | |||
| 211 | // add new algorithm constraints checker | ||
| 212 | PKIXBuilderParameters pkixParameters = | ||
| 213 | (PKIXBuilderParameters) parameterTemplate.clone(); | ||
| 214 | AlgorithmChecker algorithmChecker = null; | ||
| 215 | if (constraints != null) { | ||
| 216 | algorithmChecker = new AlgorithmChecker(constraints); | ||
| 217 | pkixParameters.addCertPathChecker(algorithmChecker); | ||
| 218 | } | ||
| 219 | |||
| 220 | if (TRY_VALIDATOR) { | ||
| 221 | // check that chain is in correct order and check if chain contains | ||
| 222 | // trust anchor | ||
| 223 | X500Principal prevIssuer = null; | ||
| 224 | for (int i = 0; i < chain.length; i++) { | ||
| 225 | X509Certificate cert = chain[i]; | ||
| 226 | X500Principal dn = cert.getSubjectX500Principal(); | ||
| 227 | if (i != 0 && | ||
| 228 | !dn.equals(prevIssuer)) { | ||
| 229 | // chain is not ordered correctly, call builder instead | ||
| 230 | return doBuild(chain, otherCerts, pkixParameters); | ||
| 231 | } | ||
| 232 | |||
| 233 | // Check if chain[i] is already trusted. It may be inside | ||
| 234 | // trustedCerts, or has the same dn and public key as a cert | ||
| 235 | // inside trustedCerts. The latter happens when a CA has | ||
| 236 | // updated its cert with a stronger signature algorithm in JRE | ||
| 237 | // but the weak one is still in circulation. | ||
| 238 | |||
| 239 | if (trustedCerts.contains(cert) || // trusted cert | ||
| 240 | (trustedSubjects.containsKey(dn) && // replacing ... | ||
| 241 | trustedSubjects.get(dn).contains( // ... weak cert | ||
| 242 | cert.getPublicKey()))) { | ||
| 243 | if (i == 0) { | ||
| 244 | return new X509Certificate[] {chain[0]}; | ||
| 245 | } | ||
| 246 | // Remove and call validator on partial chain [0 .. i-1] | ||
| 247 | X509Certificate[] newChain = new X509Certificate[i]; | ||
| 248 | System.arraycopy(chain, 0, newChain, 0, i); | ||
| 249 | return doValidate(newChain, pkixParameters); | ||
| 250 | } | ||
| 251 | prevIssuer = cert.getIssuerX500Principal(); | ||
| 252 | } | ||
| 253 | |||
| 254 | // apparently issued by trust anchor? | ||
| 255 | X509Certificate last = chain[chain.length - 1]; | ||
| 256 | X500Principal issuer = last.getIssuerX500Principal(); | ||
| 257 | X500Principal subject = last.getSubjectX500Principal(); | ||
| 258 | if (trustedSubjects.containsKey(issuer) && | ||
| 259 | isSignatureValid(trustedSubjects.get(issuer), last)) { | ||
| 260 | return doValidate(chain, pkixParameters); | ||
| 261 | } | ||
| 262 | |||
| 263 | // don't fallback to builder if called from plugin/webstart | ||
| 264 | if (plugin) { | ||
| 265 | // Validate chain even if no trust anchor is found. This | ||
| 266 | // allows plugin/webstart to make sure the chain is | ||
| 267 | // otherwise valid | ||
| 268 | if (chain.length > 1) { | ||
| 269 | X509Certificate[] newChain = | ||
| 270 | new X509Certificate[chain.length-1]; | ||
| 271 | System.arraycopy(chain, 0, newChain, 0, newChain.length); | ||
| 272 | |||
| 273 | // temporarily set last cert as sole trust anchor | ||
| 274 | try { | ||
| 275 | pkixParameters.setTrustAnchors | ||
| 276 | (Collections.singleton(new TrustAnchor | ||
| 277 | (chain[chain.length-1], null))); | ||
| 278 | } catch (InvalidAlgorithmParameterException iape) { | ||
| 279 | // should never occur, but ... | ||
| 280 | throw new CertificateException(iape); | ||
| 281 | } | ||
| 282 | doValidate(newChain, pkixParameters); | ||
| 283 | } | ||
| 284 | // if the rest of the chain is valid, throw exception | ||
| 285 | // indicating no trust anchor was found | ||
| 286 | throw new ValidatorException | ||
| 287 | (ValidatorException.T_NO_TRUST_ANCHOR); | ||
| 288 | } | ||
| 289 | // otherwise, fall back to builder | ||
| 290 | } | ||
| 291 | |||
| 292 | return doBuild(chain, otherCerts, pkixParameters); | ||
| 293 | } | ||
| 294 | |||
| 295 | private boolean isSignatureValid(List<PublicKey> keys, | ||
| 296 | X509Certificate sub) { | ||
| 297 | if (plugin) { | ||
| 298 | for (PublicKey key: keys) { | ||
| 299 | try { | ||
| 300 | sub.verify(key); | ||
| 301 | return true; | ||
| 302 | } catch (Exception ex) { | ||
| 303 | continue; | ||
| 304 | } | ||
| 305 | } | ||
| 306 | return false; | ||
| 307 | } | ||
| 308 | return true; // only check if PLUGIN is set | ||
| 309 | } | ||
| 310 | |||
| 311 | private static X509Certificate[] toArray(CertPath path, TrustAnchor anchor) | ||
| 312 | throws CertificateException { | ||
| 313 | List<? extends java.security.cert.Certificate> list = | ||
| 314 | path.getCertificates(); | ||
| 315 | X509Certificate[] chain = new X509Certificate[list.size() + 1]; | ||
| 316 | list.toArray(chain); | ||
| 317 | X509Certificate trustedCert = anchor.getTrustedCert(); | ||
| 318 | if (trustedCert == null) { | ||
| 319 | throw new ValidatorException | ||
| 320 | ("TrustAnchor must be specified as certificate"); | ||
| 321 | } | ||
| 322 | chain[chain.length - 1] = trustedCert; | ||
| 323 | return chain; | ||
| 324 | } | ||
| 325 | |||
| 326 | /** | ||
| 327 | * Set the check date (for debugging). | ||
| 328 | */ | ||
| 329 | private void setDate(PKIXBuilderParameters params) { | ||
| 330 | Date date = validationDate; | ||
| 331 | if (date != null) { | ||
| 332 | params.setDate(date); | ||
| 333 | } | ||
| 334 | } | ||
| 335 | |||
| 336 | private X509Certificate[] doValidate(X509Certificate[] chain, | ||
| 337 | PKIXBuilderParameters params) throws CertificateException { | ||
| 338 | try { | ||
| 339 | setDate(params); | ||
| 340 | |||
| 341 | // do the validation | ||
| 342 | CertPathValidator validator = CertPathValidator.getInstance("PKIX"); | ||
| 343 | CertPath path = factory.generateCertPath(Arrays.asList(chain)); | ||
| 344 | certPathLength = chain.length; | ||
| 345 | PKIXCertPathValidatorResult result = | ||
| 346 | (PKIXCertPathValidatorResult)validator.validate(path, params); | ||
| 347 | |||
| 348 | return toArray(path, result.getTrustAnchor()); | ||
| 349 | } catch (GeneralSecurityException e) { | ||
| 350 | throw new ValidatorException | ||
| 351 | ("PKIX path validation failed: " + e.toString(), e); | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | private X509Certificate[] doBuild(X509Certificate[] chain, | ||
| 356 | Collection<X509Certificate> otherCerts, | ||
| 357 | PKIXBuilderParameters params) throws CertificateException { | ||
| 358 | |||
| 359 | try { | ||
| 360 | setDate(params); | ||
| 361 | |||
| 362 | // setup target constraints | ||
| 363 | X509CertSelector selector = new X509CertSelector(); | ||
| 364 | selector.setCertificate(chain[0]); | ||
| 365 | params.setTargetCertConstraints(selector); | ||
| 366 | |||
| 367 | // setup CertStores | ||
| 368 | Collection<X509Certificate> certs = | ||
| 369 | new ArrayList<X509Certificate>(); | ||
| 370 | certs.addAll(Arrays.asList(chain)); | ||
| 371 | if (otherCerts != null) { | ||
| 372 | certs.addAll(otherCerts); | ||
| 373 | } | ||
| 374 | CertStore store = CertStore.getInstance("Collection", | ||
| 375 | new CollectionCertStoreParameters(certs)); | ||
| 376 | params.addCertStore(store); | ||
| 377 | |||
| 378 | // do the build | ||
| 379 | CertPathBuilder builder = CertPathBuilder.getInstance("PKIX"); | ||
| 380 | PKIXCertPathBuilderResult result = | ||
| 381 | (PKIXCertPathBuilderResult)builder.build(params); | ||
| 382 | |||
| 383 | return toArray(result.getCertPath(), result.getTrustAnchor()); | ||
| 384 | } catch (GeneralSecurityException e) { | ||
| 385 | throw new ValidatorException | ||
| 386 | ("PKIX path building failed: " + e.toString(), e); | ||
| 387 | } | ||
| 388 | } | ||
| 389 | } | ||