Showing the changes to java/pkg/sun/security/validator/SimpleValidator.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,405 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2002, 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.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.DerOutputStream; | ||
| 41 | import sun.security.util.ObjectIdentifier; | ||
| 42 | |||
| 43 | import sun.security.provider.certpath.AlgorithmChecker; | ||
| 44 | |||
| 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. | ||
| 51 | * <p> | ||
| 52 | * {@code SimpleValidator} objects are immutable once they have been created. | ||
| 53 | * Please DO NOT add methods that can change the state of an instance once | ||
| 54 | * it has been created. | ||
| 55 | * | ||
| 56 | * @author Andreas Sterbenz | ||
| 57 | */ | ||
| 58 | public final class SimpleValidator extends Validator { | ||
| 59 | |||
| 60 | // Constants for the OIDs we need | ||
| 61 | |||
| 62 | final static String OID_BASIC_CONSTRAINTS = "2.5.29.19"; | ||
| 63 | |||
| 64 | final static String OID_NETSCAPE_CERT_TYPE = "2.16.840.1.113730.1.1"; | ||
| 65 | |||
| 66 | final static String OID_KEY_USAGE = "2.5.29.15"; | ||
| 67 | |||
| 68 | final static String OID_EXTENDED_KEY_USAGE = "2.5.29.37"; | ||
| 69 | |||
| 70 | final static String OID_EKU_ANY_USAGE = "2.5.29.37.0"; | ||
| 71 | |||
| 72 | final static ObjectIdentifier OBJID_NETSCAPE_CERT_TYPE = | ||
| 73 | NetscapeCertTypeExtension.NetscapeCertType_Id; | ||
| 74 | |||
| 75 | private final static String NSCT_SSL_CA = | ||
| 76 | NetscapeCertTypeExtension.SSL_CA; | ||
| 77 | |||
| 78 | private final static String NSCT_CODE_SIGNING_CA = | ||
| 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 | */ | ||
| 87 | private final Map<X500Principal, List<X509Certificate>> | ||
| 88 | trustedX500Principals; | ||
| 89 | |||
| 90 | /** | ||
| 91 | * Set of the trusted certificates. Present only for | ||
| 92 | * getTrustedCertificates(). | ||
| 93 | */ | ||
| 94 | private final Collection<X509Certificate> trustedCerts; | ||
| 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 | */ | ||
| 122 | @Override | ||
| 123 | X509Certificate[] engineValidate(X509Certificate[] chain, | ||
| 124 | Collection<X509Certificate> otherCerts, | ||
| 125 | AlgorithmConstraints constraints, | ||
| 126 | Object parameter) throws CertificateException { | ||
| 127 | if ((chain == null) || (chain.length == 0)) { | ||
| 128 | throw new CertificateException | ||
| 129 | ("null or zero-length certificate chain"); | ||
| 130 | } | ||
| 131 | |||
| 132 | // make sure chain includes a trusted cert | ||
| 133 | chain = buildTrustedChain(chain); | ||
| 134 | |||
| 135 | Date date = validationDate; | ||
| 136 | if (date == null) { | ||
| 137 | date = new Date(); | ||
| 138 | } | ||
| 139 | |||
| 140 | // create default algorithm constraints checker | ||
| 141 | TrustAnchor anchor = new TrustAnchor(chain[chain.length - 1], null); | ||
| 142 | AlgorithmChecker defaultAlgChecker = new AlgorithmChecker(anchor); | ||
| 143 | |||
| 144 | // create application level algorithm constraints checker | ||
| 145 | AlgorithmChecker appAlgChecker = null; | ||
| 146 | if (constraints != null) { | ||
| 147 | appAlgChecker = new AlgorithmChecker(anchor, constraints); | ||
| 148 | } | ||
| 149 | |||
| 150 | // verify top down, starting at the certificate issued by | ||
| 151 | // the trust anchor | ||
| 152 | int maxPathLength = chain.length - 1; | ||
| 153 | for (int i = chain.length - 2; i >= 0; i--) { | ||
| 154 | X509Certificate issuerCert = chain[i + 1]; | ||
| 155 | X509Certificate cert = chain[i]; | ||
| 156 | |||
| 157 | // check certificate algorithm | ||
| 158 | try { | ||
| 159 | // Algorithm checker does not care about the unresolved | ||
| 160 | // critical extensions. | ||
| 161 | defaultAlgChecker.check(cert, Collections.<String>emptySet()); | ||
| 162 | if (appAlgChecker != null) { | ||
| 163 | appAlgChecker.check(cert, Collections.<String>emptySet()); | ||
| 164 | } | ||
| 165 | } catch (CertPathValidatorException cpve) { | ||
| 166 | throw new ValidatorException | ||
| 167 | (ValidatorException.T_ALGORITHM_DISABLED, cert, cpve); | ||
| 168 | } | ||
| 169 | |||
| 170 | // no validity check for code signing certs | ||
| 171 | if ((variant.equals(VAR_CODE_SIGNING) == false) | ||
| 172 | && (variant.equals(VAR_JCE_SIGNING) == false)) { | ||
| 173 | cert.checkValidity(date); | ||
| 174 | } | ||
| 175 | |||
| 176 | // check name chaining | ||
| 177 | if (cert.getIssuerX500Principal().equals( | ||
| 178 | issuerCert.getSubjectX500Principal()) == false) { | ||
| 179 | throw new ValidatorException | ||
| 180 | (ValidatorException.T_NAME_CHAINING, cert); | ||
| 181 | } | ||
| 182 | |||
| 183 | // check signature | ||
| 184 | try { | ||
| 185 | cert.verify(issuerCert.getPublicKey()); | ||
| 186 | } catch (GeneralSecurityException e) { | ||
| 187 | throw new ValidatorException | ||
| 188 | (ValidatorException.T_SIGNATURE_ERROR, cert, e); | ||
| 189 | } | ||
| 190 | |||
| 191 | // check extensions for CA certs | ||
| 192 | if (i != 0) { | ||
| 193 | maxPathLength = checkExtensions(cert, maxPathLength); | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | return chain; | ||
| 198 | } | ||
| 199 | |||
| 200 | private int checkExtensions(X509Certificate cert, int maxPathLen) | ||
| 201 | throws CertificateException { | ||
| 202 | Set<String> critSet = cert.getCriticalExtensionOIDs(); | ||
| 203 | if (critSet == null) { | ||
| 204 | critSet = Collections.<String>emptySet(); | ||
| 205 | } | ||
| 206 | |||
| 207 | // Check the basic constraints extension | ||
| 208 | int pathLenConstraint = | ||
| 209 | checkBasicConstraints(cert, critSet, maxPathLen); | ||
| 210 | |||
| 211 | // Check the key usage and extended key usage extensions | ||
| 212 | checkKeyUsage(cert, critSet); | ||
| 213 | |||
| 214 | // check Netscape certificate type extension | ||
| 215 | checkNetscapeCertType(cert, critSet); | ||
| 216 | |||
| 217 | if (!critSet.isEmpty()) { | ||
| 218 | throw new ValidatorException | ||
| 219 | ("Certificate contains unknown critical extensions: " + critSet, | ||
| 220 | ValidatorException.T_CA_EXTENSIONS, cert); | ||
| 221 | } | ||
| 222 | |||
| 223 | return pathLenConstraint; | ||
| 224 | } | ||
| 225 | |||
| 226 | private void checkNetscapeCertType(X509Certificate cert, | ||
| 227 | Set<String> critSet) throws CertificateException { | ||
| 228 | if (variant.equals(VAR_GENERIC)) { | ||
| 229 | // nothing | ||
| 230 | } else if (variant.equals(VAR_TLS_CLIENT) | ||
| 231 | || variant.equals(VAR_TLS_SERVER)) { | ||
| 232 | if (getNetscapeCertTypeBit(cert, NSCT_SSL_CA) == false) { | ||
| 233 | throw new ValidatorException | ||
| 234 | ("Invalid Netscape CertType extension for SSL CA " | ||
| 235 | + "certificate", | ||
| 236 | ValidatorException.T_CA_EXTENSIONS, cert); | ||
| 237 | } | ||
| 238 | critSet.remove(OID_NETSCAPE_CERT_TYPE); | ||
| 239 | } else if (variant.equals(VAR_CODE_SIGNING) | ||
| 240 | || variant.equals(VAR_JCE_SIGNING)) { | ||
| 241 | if (getNetscapeCertTypeBit(cert, NSCT_CODE_SIGNING_CA) == false) { | ||
| 242 | throw new ValidatorException | ||
| 243 | ("Invalid Netscape CertType extension for code " | ||
| 244 | + "signing CA certificate", | ||
| 245 | ValidatorException.T_CA_EXTENSIONS, cert); | ||
| 246 | } | ||
| 247 | critSet.remove(OID_NETSCAPE_CERT_TYPE); | ||
| 248 | } else { | ||
| 249 | throw new CertificateException("Unknown variant " + variant); | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | /** | ||
| 254 | * Get the value of the specified bit in the Netscape certificate type | ||
| 255 | * extension. If the extension is not present at all, we return true. | ||
| 256 | */ | ||
| 257 | static boolean getNetscapeCertTypeBit(X509Certificate cert, String type) { | ||
| 258 | try { | ||
| 259 | NetscapeCertTypeExtension ext; | ||
| 260 | if (cert instanceof X509CertImpl) { | ||
| 261 | X509CertImpl certImpl = (X509CertImpl)cert; | ||
| 262 | ObjectIdentifier oid = OBJID_NETSCAPE_CERT_TYPE; | ||
| 263 | ext = (NetscapeCertTypeExtension)certImpl.getExtension(oid); | ||
| 264 | if (ext == null) { | ||
| 265 | return true; | ||
| 266 | } | ||
| 267 | } else { | ||
| 268 | byte[] extVal = cert.getExtensionValue(OID_NETSCAPE_CERT_TYPE); | ||
| 269 | if (extVal == null) { | ||
| 270 | return true; | ||
| 271 | } | ||
| 272 | DerInputStream in = new DerInputStream(extVal); | ||
| 273 | byte[] encoded = in.getOctetString(); | ||
| 274 | encoded = new DerValue(encoded).getUnalignedBitString() | ||
| 275 | .toByteArray(); | ||
| 276 | ext = new NetscapeCertTypeExtension(encoded); | ||
| 277 | } | ||
| 278 | Boolean val = (Boolean)ext.get(type); | ||
| 279 | return val.booleanValue(); | ||
| 280 | } catch (IOException e) { | ||
| 281 | return false; | ||
| 282 | } | ||
| 283 | } | ||
| 284 | |||
| 285 | private int checkBasicConstraints(X509Certificate cert, | ||
| 286 | Set<String> critSet, int maxPathLen) throws CertificateException { | ||
| 287 | |||
| 288 | critSet.remove(OID_BASIC_CONSTRAINTS); | ||
| 289 | int constraints = cert.getBasicConstraints(); | ||
| 290 | // reject, if extension missing or not a CA (constraints == -1) | ||
| 291 | if (constraints < 0) { | ||
| 292 | throw new ValidatorException("End user tried to act as a CA", | ||
| 293 | ValidatorException.T_CA_EXTENSIONS, cert); | ||
| 294 | } | ||
| 295 | |||
| 296 | // if the certificate is self-issued, ignore the pathLenConstraint | ||
| 297 | // checking. | ||
| 298 | if (!X509CertImpl.isSelfIssued(cert)) { | ||
| 299 | if (maxPathLen <= 1) { // reserved one for end-entity certificate | ||
| 300 | throw new ValidatorException("Violated path length constraints", | ||
| 301 | ValidatorException.T_CA_EXTENSIONS, cert); | ||
| 302 | } | ||
| 303 | |||
| 304 | maxPathLen--; | ||
| 305 | } | ||
| 306 | |||
| 307 | if (maxPathLen > constraints) { | ||
| 308 | maxPathLen = constraints; | ||
| 309 | } | ||
| 310 | |||
| 311 | return maxPathLen; | ||
| 312 | } | ||
| 313 | |||
| 314 | /* | ||
| 315 | * Verify the key usage and extended key usage for intermediate | ||
| 316 | * certificates. | ||
| 317 | */ | ||
| 318 | private void checkKeyUsage(X509Certificate cert, Set<String> critSet) | ||
| 319 | throws CertificateException { | ||
| 320 | |||
| 321 | critSet.remove(OID_KEY_USAGE); | ||
| 322 | // EKU irrelevant in CA certificates | ||
| 323 | critSet.remove(OID_EXTENDED_KEY_USAGE); | ||
| 324 | |||
| 325 | // check key usage extension | ||
| 326 | boolean[] keyUsageInfo = cert.getKeyUsage(); | ||
| 327 | if (keyUsageInfo != null) { | ||
| 328 | // keyUsageInfo[5] is for keyCertSign. | ||
| 329 | if ((keyUsageInfo.length < 6) || (keyUsageInfo[5] == false)) { | ||
| 330 | throw new ValidatorException | ||
| 331 | ("Wrong key usage: expected keyCertSign", | ||
| 332 | ValidatorException.T_CA_EXTENSIONS, cert); | ||
| 333 | } | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | /** | ||
| 338 | * Build a trusted certificate chain. This method always returns a chain | ||
| 339 | * with a trust anchor as the final cert in the chain. If no trust anchor | ||
| 340 | * could be found, a CertificateException is thrown. | ||
| 341 | */ | ||
| 342 | private X509Certificate[] buildTrustedChain(X509Certificate[] chain) | ||
| 343 | throws CertificateException { | ||
| 344 | List<X509Certificate> c = new ArrayList<X509Certificate>(chain.length); | ||
| 345 | // scan chain starting at EE cert | ||
| 346 | // if a trusted certificate is found, append it and return | ||
| 347 | for (int i = 0; i < chain.length; i++) { | ||
| 348 | X509Certificate cert = chain[i]; | ||
| 349 | X509Certificate trustedCert = getTrustedCertificate(cert); | ||
| 350 | if (trustedCert != null) { | ||
| 351 | c.add(trustedCert); | ||
| 352 | return c.toArray(CHAIN0); | ||
| 353 | } | ||
| 354 | c.add(cert); | ||
| 355 | } | ||
| 356 | |||
| 357 | // check if we can append a trusted cert | ||
| 358 | X509Certificate cert = chain[chain.length - 1]; | ||
| 359 | X500Principal subject = cert.getSubjectX500Principal(); | ||
| 360 | X500Principal issuer = cert.getIssuerX500Principal(); | ||
| 361 | List<X509Certificate> list = trustedX500Principals.get(issuer); | ||
| 362 | if (list != null) { | ||
| 363 | X509Certificate trustedCert = list.iterator().next(); | ||
| 364 | c.add(trustedCert); | ||
| 365 | return c.toArray(CHAIN0); | ||
| 366 | } | ||
| 367 | |||
| 368 | // no trusted cert found, error | ||
| 369 | throw new ValidatorException(ValidatorException.T_NO_TRUST_ANCHOR); | ||
| 370 | } | ||
| 371 | |||
| 372 | /** | ||
| 373 | * Return a trusted certificate that matches the input certificate, | ||
| 374 | * or null if no such certificate can be found. This method also handles | ||
| 375 | * cases where a CA re-issues a trust anchor with the same public key and | ||
| 376 | * same subject and issuer names but a new validity period, etc. | ||
| 377 | */ | ||
| 378 | private X509Certificate getTrustedCertificate(X509Certificate cert) { | ||
| 379 | Principal certSubjectName = cert.getSubjectX500Principal(); | ||
| 380 | List<X509Certificate> list = trustedX500Principals.get(certSubjectName); | ||
| 381 | if (list == null) { | ||
| 382 | return null; | ||
| 383 | } | ||
| 384 | |||
| 385 | Principal certIssuerName = cert.getIssuerX500Principal(); | ||
| 386 | PublicKey certPublicKey = cert.getPublicKey(); | ||
| 387 | |||
| 388 | for (X509Certificate mycert : list) { | ||
| 389 | if (mycert.equals(cert)) { | ||
| 390 | return cert; | ||
| 391 | } | ||
| 392 | if (!mycert.getIssuerX500Principal().equals(certIssuerName)) { | ||
| 393 | continue; | ||
| 394 | } | ||
| 395 | if (!mycert.getPublicKey().equals(certPublicKey)) { | ||
| 396 | continue; | ||
| 397 | } | ||
| 398 | |||
| 399 | // All tests pass, this must be the one to use... | ||
| 400 | return mycert; | ||
| 401 | } | ||
| 402 | return null; | ||
| 403 | } | ||
| 404 | |||
| 405 | } | ||