|
Java example source code file (RevocationChecker.java)
The RevocationChecker.java Java example source code/* * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.provider.certpath; import java.io.IOException; import java.math.BigInteger; import java.net.URI; import java.net.URISyntaxException; import java.security.AccessController; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.PrivilegedAction; import java.security.PublicKey; import java.security.Security; import java.security.cert.CertPathValidatorException.BasicReason; import java.security.cert.Extension; import java.security.cert.*; import java.util.*; import javax.security.auth.x500.X500Principal; import static sun.security.provider.certpath.OCSP.*; import static sun.security.provider.certpath.PKIX.*; import sun.security.action.GetPropertyAction; import sun.security.x509.*; import static sun.security.x509.PKIXExtensions.*; import sun.security.util.Debug; class RevocationChecker extends PKIXRevocationChecker { private static final Debug debug = Debug.getInstance("certpath"); private TrustAnchor anchor; private ValidatorParams params; private boolean onlyEE; private boolean softFail; private boolean crlDP; private URI responderURI; private X509Certificate responderCert; private List<CertStore> certStores; private Map<X509Certificate, byte[]> ocspResponses; private List<Extension> ocspExtensions; private boolean legacy; private LinkedList<CertPathValidatorException> softFailExceptions = new LinkedList<>(); // state variables private X509Certificate issuerCert; private PublicKey prevPubKey; private boolean crlSignFlag; private int certIndex; private enum Mode { PREFER_OCSP, PREFER_CRLS, ONLY_CRLS, ONLY_OCSP }; private Mode mode = Mode.PREFER_OCSP; private static class RevocationProperties { boolean onlyEE; boolean ocspEnabled; boolean crlDPEnabled; String ocspUrl; String ocspSubject; String ocspIssuer; String ocspSerial; } RevocationChecker() { legacy = false; } RevocationChecker(TrustAnchor anchor, ValidatorParams params) throws CertPathValidatorException { legacy = true; init(anchor, params); } void init(TrustAnchor anchor, ValidatorParams params) throws CertPathValidatorException { RevocationProperties rp = getRevocationProperties(); URI uri = getOcspResponder(); responderURI = (uri == null) ? toURI(rp.ocspUrl) : uri; X509Certificate cert = getOcspResponderCert(); responderCert = (cert == null) ? getResponderCert(rp, params.trustAnchors(), params.certStores()) : cert; Set<Option> options = getOptions(); for (Option option : options) { switch (option) { case ONLY_END_ENTITY: case PREFER_CRLS: case SOFT_FAIL: case NO_FALLBACK: break; default: throw new CertPathValidatorException( "Unrecognized revocation parameter option: " + option); } } softFail = options.contains(Option.SOFT_FAIL); // set mode, only end entity flag if (legacy) { mode = (rp.ocspEnabled) ? Mode.PREFER_OCSP : Mode.ONLY_CRLS; onlyEE = rp.onlyEE; } else { if (options.contains(Option.NO_FALLBACK)) { if (options.contains(Option.PREFER_CRLS)) { mode = Mode.ONLY_CRLS; } else { mode = Mode.ONLY_OCSP; } } else if (options.contains(Option.PREFER_CRLS)) { mode = Mode.PREFER_CRLS; } onlyEE = options.contains(Option.ONLY_END_ENTITY); } if (legacy) { crlDP = rp.crlDPEnabled; } else { crlDP = true; } ocspResponses = getOcspResponses(); ocspExtensions = getOcspExtensions(); this.anchor = anchor; this.params = params; this.certStores = new ArrayList<>(params.certStores()); try { this.certStores.add(CertStore.getInstance("Collection", new CollectionCertStoreParameters(params.certificates()))); } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException e) { // should never occur but not necessarily fatal, so log it, // ignore and continue if (debug != null) { debug.println("RevocationChecker: " + "error creating Collection CertStore: " + e); } } } private static URI toURI(String uriString) throws CertPathValidatorException { try { if (uriString != null) { return new URI(uriString); } return null; } catch (URISyntaxException e) { throw new CertPathValidatorException( "cannot parse ocsp.responderURL property", e); } } private static RevocationProperties getRevocationProperties() { return AccessController.doPrivileged( new PrivilegedAction<RevocationProperties>() { public RevocationProperties run() { RevocationProperties rp = new RevocationProperties(); String onlyEE = Security.getProperty( "com.sun.security.onlyCheckRevocationOfEECert"); rp.onlyEE = onlyEE != null && onlyEE.equalsIgnoreCase("true"); String ocspEnabled = Security.getProperty("ocsp.enable"); rp.ocspEnabled = ocspEnabled != null && ocspEnabled.equalsIgnoreCase("true"); rp.ocspUrl = Security.getProperty("ocsp.responderURL"); rp.ocspSubject = Security.getProperty("ocsp.responderCertSubjectName"); rp.ocspIssuer = Security.getProperty("ocsp.responderCertIssuerName"); rp.ocspSerial = Security.getProperty("ocsp.responderCertSerialNumber"); rp.crlDPEnabled = Boolean.getBoolean("com.sun.security.enableCRLDP"); return rp; } } ); } private static X509Certificate getResponderCert(RevocationProperties rp, Set<TrustAnchor> anchors, List<CertStore> stores) throws CertPathValidatorException { if (rp.ocspSubject != null) { return getResponderCert(rp.ocspSubject, anchors, stores); } else if (rp.ocspIssuer != null && rp.ocspSerial != null) { return getResponderCert(rp.ocspIssuer, rp.ocspSerial, anchors, stores); } else if (rp.ocspIssuer != null || rp.ocspSerial != null) { throw new CertPathValidatorException( "Must specify both ocsp.responderCertIssuerName and " + "ocsp.responderCertSerialNumber properties"); } return null; } private static X509Certificate getResponderCert(String subject, Set<TrustAnchor> anchors, List<CertStore> stores) throws CertPathValidatorException { X509CertSelector sel = new X509CertSelector(); try { sel.setSubject(new X500Principal(subject)); } catch (IllegalArgumentException e) { throw new CertPathValidatorException( "cannot parse ocsp.responderCertSubjectName property", e); } return getResponderCert(sel, anchors, stores); } private static X509Certificate getResponderCert(String issuer, String serial, Set<TrustAnchor> anchors, List<CertStore> stores) throws CertPathValidatorException { X509CertSelector sel = new X509CertSelector(); try { sel.setIssuer(new X500Principal(issuer)); } catch (IllegalArgumentException e) { throw new CertPathValidatorException( "cannot parse ocsp.responderCertIssuerName property", e); } try { sel.setSerialNumber(new BigInteger(stripOutSeparators(serial), 16)); } catch (NumberFormatException e) { throw new CertPathValidatorException( "cannot parse ocsp.responderCertSerialNumber property", e); } return getResponderCert(sel, anchors, stores); } private static X509Certificate getResponderCert(X509CertSelector sel, Set<TrustAnchor> anchors, List<CertStore> stores) throws CertPathValidatorException { // first check TrustAnchors for (TrustAnchor anchor : anchors) { X509Certificate cert = anchor.getTrustedCert(); if (cert == null) { continue; } if (sel.match(cert)) { return cert; } } // now check CertStores for (CertStore store : stores) { try { Collection<? extends Certificate> certs = store.getCertificates(sel); if (!certs.isEmpty()) { return (X509Certificate)certs.iterator().next(); } } catch (CertStoreException e) { // ignore and try next CertStore if (debug != null) { debug.println("CertStore exception:" + e); } continue; } } throw new CertPathValidatorException( "Cannot find the responder's certificate " + "(set using the OCSP security properties)."); } @Override public void init(boolean forward) throws CertPathValidatorException { if (forward) { throw new CertPathValidatorException("forward checking not supported"); } if (anchor != null) { issuerCert = anchor.getTrustedCert(); prevPubKey = (issuerCert != null) ? issuerCert.getPublicKey() : anchor.getCAPublicKey(); } crlSignFlag = true; if (params != null && params.certPath() != null) { certIndex = params.certPath().getCertificates().size() - 1; } else { certIndex = -1; } softFailExceptions.clear(); } @Override public boolean isForwardCheckingSupported() { return false; } @Override public Set<String> getSupportedExtensions() { return null; } @Override public List<CertPathValidatorException> getSoftFailExceptions() { return Collections.unmodifiableList(softFailExceptions); } @Override public void check(Certificate cert, Collection<String> unresolvedCritExts) throws CertPathValidatorException { check((X509Certificate)cert, unresolvedCritExts, prevPubKey, crlSignFlag); } private void check(X509Certificate xcert, Collection<String> unresolvedCritExts, PublicKey pubKey, boolean crlSignFlag) throws CertPathValidatorException { try { if (onlyEE && xcert.getBasicConstraints() != -1) { if (debug != null) { debug.println("Skipping revocation check, not end " + "entity cert"); } return; } switch (mode) { case PREFER_OCSP: case ONLY_OCSP: checkOCSP(xcert, unresolvedCritExts); break; case PREFER_CRLS: case ONLY_CRLS: checkCRLs(xcert, unresolvedCritExts, null, pubKey, crlSignFlag); break; } } catch (CertPathValidatorException e) { if (e.getReason() == BasicReason.REVOKED) { throw e; } boolean eSoftFail = isSoftFailException(e); if (eSoftFail) { if (mode == Mode.ONLY_OCSP || mode == Mode.ONLY_CRLS) { return; } } else { if (mode == Mode.ONLY_OCSP || mode == Mode.ONLY_CRLS) { throw e; } } CertPathValidatorException cause = e; // Otherwise, failover if (debug != null) { debug.println("RevocationChecker.check() " + e.getMessage()); debug.println("RevocationChecker.check() preparing to failover"); } try { switch (mode) { case PREFER_OCSP: checkCRLs(xcert, unresolvedCritExts, null, pubKey, crlSignFlag); break; case PREFER_CRLS: checkOCSP(xcert, unresolvedCritExts); break; } } catch (CertPathValidatorException x) { if (debug != null) { debug.println("RevocationChecker.check() failover failed"); debug.println("RevocationChecker.check() " + x.getMessage()); } if (x.getReason() == BasicReason.REVOKED) { throw x; } if (!isSoftFailException(x)) { cause.addSuppressed(x); throw cause; } else { // only pass if both exceptions were soft failures if (!eSoftFail) { throw cause; } } } } finally { updateState(xcert); } } private boolean isSoftFailException(CertPathValidatorException e) { if (softFail && e.getReason() == BasicReason.UNDETERMINED_REVOCATION_STATUS) { // recreate exception with correct index CertPathValidatorException e2 = new CertPathValidatorException( e.getMessage(), e.getCause(), params.certPath(), certIndex, e.getReason()); softFailExceptions.addFirst(e2); return true; } return false; } private void updateState(X509Certificate cert) throws CertPathValidatorException { issuerCert = cert; // Make new public key if parameters are missing PublicKey pubKey = cert.getPublicKey(); if (PKIX.isDSAPublicKeyWithoutParams(pubKey)) { // pubKey needs to inherit DSA parameters from prev key pubKey = BasicChecker.makeInheritedParamsKey(pubKey, prevPubKey); } prevPubKey = pubKey; crlSignFlag = certCanSignCrl(cert); if (certIndex > 0) { certIndex--; } } // Maximum clock skew in milliseconds (15 minutes) allowed when checking // validity of CRLs private static final long MAX_CLOCK_SKEW = 900000; private void checkCRLs(X509Certificate cert, Collection<String> unresolvedCritExts, Set<X509Certificate> stackedCerts, PublicKey pubKey, boolean signFlag) throws CertPathValidatorException { checkCRLs(cert, pubKey, null, signFlag, true, stackedCerts, params.trustAnchors()); } private void checkCRLs(X509Certificate cert, PublicKey prevKey, X509Certificate prevCert, boolean signFlag, boolean allowSeparateKey, Set<X509Certificate> stackedCerts, Set<TrustAnchor> anchors) throws CertPathValidatorException { if (debug != null) { debug.println("RevocationChecker.checkCRLs()" + " ---checking revocation status ..."); } // reject circular dependencies - RFC 3280 is not explicit on how // to handle this, so we feel it is safest to reject them until // the issue is resolved in the PKIX WG. if (stackedCerts != null && stackedCerts.contains(cert)) { if (debug != null) { debug.println("RevocationChecker.checkCRLs()" + " circular dependency"); } throw new CertPathValidatorException ("Could not determine revocation status", null, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } Set<X509CRL> possibleCRLs = new HashSet<>(); Set<X509CRL> approvedCRLs = new HashSet<>(); X509CRLSelector sel = new X509CRLSelector(); sel.setCertificateChecking(cert); CertPathHelper.setDateAndTime(sel, params.date(), MAX_CLOCK_SKEW); // First, check user-specified CertStores CertPathValidatorException networkFailureException = null; for (CertStore store : certStores) { try { for (CRL crl : store.getCRLs(sel)) { possibleCRLs.add((X509CRL)crl); } } catch (CertStoreException e) { if (debug != null) { debug.println("RevocationChecker.checkCRLs() " + "CertStoreException: " + e.getMessage()); } if (networkFailureException == null && CertStoreHelper.isCausedByNetworkIssue(store.getType(),e)) { // save this exception, we may need to throw it later networkFailureException = new CertPathValidatorException( "Unable to determine revocation status due to " + "network error", e, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } } } if (debug != null) { debug.println("RevocationChecker.checkCRLs() " + "possible crls.size() = " + possibleCRLs.size()); } boolean[] reasonsMask = new boolean[9]; if (!possibleCRLs.isEmpty()) { // Now that we have a list of possible CRLs, see which ones can // be approved approvedCRLs.addAll(verifyPossibleCRLs(possibleCRLs, cert, prevKey, signFlag, reasonsMask, anchors)); } if (debug != null) { debug.println("RevocationChecker.checkCRLs() " + "approved crls.size() = " + approvedCRLs.size()); } // make sure that we have at least one CRL that _could_ cover // the certificate in question and all reasons are covered if (!approvedCRLs.isEmpty() && Arrays.equals(reasonsMask, ALL_REASONS)) { checkApprovedCRLs(cert, approvedCRLs); } else { // Check Distribution Points // all CRLs returned by the DP Fetcher have also been verified try { if (crlDP) { approvedCRLs.addAll(DistributionPointFetcher.getCRLs( sel, signFlag, prevKey, prevCert, params.sigProvider(), certStores, reasonsMask, anchors, null)); } } catch (CertStoreException e) { if (e instanceof CertStoreTypeException) { CertStoreTypeException cste = (CertStoreTypeException)e; if (CertStoreHelper.isCausedByNetworkIssue(cste.getType(), e)) { throw new CertPathValidatorException( "Unable to determine revocation status due to " + "network error", e, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } } throw new CertPathValidatorException(e); } if (!approvedCRLs.isEmpty() && Arrays.equals(reasonsMask, ALL_REASONS)) { checkApprovedCRLs(cert, approvedCRLs); } else { if (allowSeparateKey) { try { verifyWithSeparateSigningKey(cert, prevKey, signFlag, stackedCerts); return; } catch (CertPathValidatorException cpve) { if (networkFailureException != null) { // if a network issue previously prevented us from // retrieving a CRL from one of the user-specified // CertStores, throw it now so it can be handled // appropriately throw networkFailureException; } throw cpve; } } else { if (networkFailureException != null) { // if a network issue previously prevented us from // retrieving a CRL from one of the user-specified // CertStores, throw it now so it can be handled // appropriately throw networkFailureException; } throw new CertPathValidatorException( "Could not determine revocation status", null, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } } } } private void checkApprovedCRLs(X509Certificate cert, Set<X509CRL> approvedCRLs) throws CertPathValidatorException { // See if the cert is in the set of approved crls. if (debug != null) { BigInteger sn = cert.getSerialNumber(); debug.println("RevocationChecker.checkApprovedCRLs() " + "starting the final sweep..."); debug.println("RevocationChecker.checkApprovedCRLs()" + " cert SN: " + sn.toString()); } CRLReason reasonCode = CRLReason.UNSPECIFIED; X509CRLEntryImpl entry = null; for (X509CRL crl : approvedCRLs) { X509CRLEntry e = crl.getRevokedCertificate(cert); if (e != null) { try { entry = X509CRLEntryImpl.toImpl(e); } catch (CRLException ce) { throw new CertPathValidatorException(ce); } if (debug != null) { debug.println("RevocationChecker.checkApprovedCRLs()" + " CRL entry: " + entry.toString()); } /* * Abort CRL validation and throw exception if there are any * unrecognized critical CRL entry extensions (see section * 5.3 of RFC 3280). */ Set<String> unresCritExts = entry.getCriticalExtensionOIDs(); if (unresCritExts != null && !unresCritExts.isEmpty()) { /* remove any that we will process */ unresCritExts.remove(ReasonCode_Id.toString()); unresCritExts.remove(CertificateIssuer_Id.toString()); if (!unresCritExts.isEmpty()) { throw new CertPathValidatorException( "Unrecognized critical extension(s) in revoked " + "CRL entry"); } } reasonCode = entry.getRevocationReason(); if (reasonCode == null) { reasonCode = CRLReason.UNSPECIFIED; } Date revocationDate = entry.getRevocationDate(); if (revocationDate.before(params.date())) { Throwable t = new CertificateRevokedException( revocationDate, reasonCode, crl.getIssuerX500Principal(), entry.getExtensions()); throw new CertPathValidatorException( t.getMessage(), t, null, -1, BasicReason.REVOKED); } } } } private void checkOCSP(X509Certificate cert, Collection<String> unresolvedCritExts) throws CertPathValidatorException { X509CertImpl currCert = null; try { currCert = X509CertImpl.toImpl(cert); } catch (CertificateException ce) { throw new CertPathValidatorException(ce); } // The algorithm constraints of the OCSP trusted responder certificate // does not need to be checked in this code. The constraints will be // checked when the responder's certificate is validated. OCSPResponse response = null; CertId certId = null; try { if (issuerCert != null) { certId = new CertId(issuerCert, currCert.getSerialNumberObject()); } else { // must be an anchor name and key certId = new CertId(anchor.getCA(), anchor.getCAPublicKey(), currCert.getSerialNumberObject()); } // check if there is a cached OCSP response available byte[] responseBytes = ocspResponses.get(cert); if (responseBytes != null) { if (debug != null) { debug.println("Found cached OCSP response"); } response = new OCSPResponse(responseBytes); // verify the response byte[] nonce = null; for (Extension ext : ocspExtensions) { if (ext.getId().equals("1.3.6.1.5.5.7.48.1.2")) { nonce = ext.getValue(); } } response.verify(Collections.singletonList(certId), issuerCert, responderCert, params.date(), nonce); } else { URI responderURI = (this.responderURI != null) ? this.responderURI : OCSP.getResponderURI(currCert); if (responderURI == null) { throw new CertPathValidatorException( "Certificate does not specify OCSP responder", null, null, -1); } response = OCSP.check(Collections.singletonList(certId), responderURI, issuerCert, responderCert, null, ocspExtensions); } } catch (IOException e) { throw new CertPathValidatorException( "Unable to determine revocation status due to network error", e, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } RevocationStatus rs = (RevocationStatus)response.getSingleResponse(certId); RevocationStatus.CertStatus certStatus = rs.getCertStatus(); if (certStatus == RevocationStatus.CertStatus.REVOKED) { Date revocationTime = rs.getRevocationTime(); if (revocationTime.before(params.date())) { Throwable t = new CertificateRevokedException( revocationTime, rs.getRevocationReason(), response.getSignerCertificate().getSubjectX500Principal(), rs.getSingleExtensions()); throw new CertPathValidatorException(t.getMessage(), t, null, -1, BasicReason.REVOKED); } } else if (certStatus == RevocationStatus.CertStatus.UNKNOWN) { throw new CertPathValidatorException( "Certificate's revocation status is unknown", null, params.certPath(), -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } } /* * Removes any non-hexadecimal characters from a string. */ private static final String HEX_DIGITS = "0123456789ABCDEFabcdef"; private static String stripOutSeparators(String value) { char[] chars = value.toCharArray(); StringBuilder hexNumber = new StringBuilder(); for (int i = 0; i < chars.length; i++) { if (HEX_DIGITS.indexOf(chars[i]) != -1) { hexNumber.append(chars[i]); } } return hexNumber.toString(); } /** * Checks that a cert can be used to verify a CRL. * * @param cert an X509Certificate to check * @return a boolean specifying if the cert is allowed to vouch for the * validity of a CRL */ static boolean certCanSignCrl(X509Certificate cert) { // if the cert doesn't include the key usage ext, or // the key usage ext asserts cRLSigning, return true, // otherwise return false. boolean[] keyUsage = cert.getKeyUsage(); if (keyUsage != null) { return keyUsage[6]; } return false; } /** * Internal method that verifies a set of possible_crls, * and sees if each is approved, based on the cert. * * @param crls a set of possible CRLs to test for acceptability * @param cert the certificate whose revocation status is being checked * @param signFlag <code>true if prevKey was trusted to sign CRLs * @param prevKey the public key of the issuer of cert * @param reasonsMask the reason code mask * @param trustAnchors a <code>Set of Other Java examples (source code examples)Here is a short list of links related to this Java RevocationChecker.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.