Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/provider/certpath/RevocationChecker.java
38923 views
/*1* Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.provider.certpath;2627import java.io.IOException;28import java.math.BigInteger;29import java.net.URI;30import java.net.URISyntaxException;31import java.security.AccessController;32import java.security.InvalidAlgorithmParameterException;33import java.security.NoSuchAlgorithmException;34import java.security.PrivilegedAction;35import java.security.PublicKey;36import java.security.Security;37import java.security.cert.CertPathValidatorException.BasicReason;38import java.security.cert.Extension;39import java.security.cert.*;40import java.util.*;41import javax.security.auth.x500.X500Principal;4243import static sun.security.provider.certpath.OCSP.*;44import static sun.security.provider.certpath.PKIX.*;45import sun.security.x509.*;46import static sun.security.x509.PKIXExtensions.*;47import sun.security.util.Debug;4849class RevocationChecker extends PKIXRevocationChecker {5051private static final Debug debug = Debug.getInstance("certpath");5253private TrustAnchor anchor;54private ValidatorParams params;55private boolean onlyEE;56private boolean softFail;57private boolean crlDP;58private URI responderURI;59private X509Certificate responderCert;60private List<CertStore> certStores;61private Map<X509Certificate, byte[]> ocspResponses;62private List<Extension> ocspExtensions;63private final boolean legacy;64private LinkedList<CertPathValidatorException> softFailExceptions =65new LinkedList<>();6667// state variables68private OCSPResponse.IssuerInfo issuerInfo;69private PublicKey prevPubKey;70private boolean crlSignFlag;71private int certIndex;7273private enum Mode { PREFER_OCSP, PREFER_CRLS, ONLY_CRLS, ONLY_OCSP };74private Mode mode = Mode.PREFER_OCSP;7576private static class RevocationProperties {77boolean onlyEE;78boolean ocspEnabled;79boolean crlDPEnabled;80String ocspUrl;81String ocspSubject;82String ocspIssuer;83String ocspSerial;84}8586RevocationChecker() {87legacy = false;88}8990RevocationChecker(TrustAnchor anchor, ValidatorParams params)91throws CertPathValidatorException92{93legacy = true;94init(anchor, params);95}9697void init(TrustAnchor anchor, ValidatorParams params)98throws CertPathValidatorException99{100RevocationProperties rp = getRevocationProperties();101URI uri = getOcspResponder();102responderURI = (uri == null) ? toURI(rp.ocspUrl) : uri;103X509Certificate cert = getOcspResponderCert();104responderCert = (cert == null)105? getResponderCert(rp, params.trustAnchors(),106params.certStores())107: cert;108Set<Option> options = getOptions();109for (Option option : options) {110switch (option) {111case ONLY_END_ENTITY:112case PREFER_CRLS:113case SOFT_FAIL:114case NO_FALLBACK:115break;116default:117throw new CertPathValidatorException(118"Unrecognized revocation parameter option: " + option);119}120}121softFail = options.contains(Option.SOFT_FAIL);122123// set mode, only end entity flag124if (legacy) {125mode = (rp.ocspEnabled) ? Mode.PREFER_OCSP : Mode.ONLY_CRLS;126onlyEE = rp.onlyEE;127} else {128if (options.contains(Option.NO_FALLBACK)) {129if (options.contains(Option.PREFER_CRLS)) {130mode = Mode.ONLY_CRLS;131} else {132mode = Mode.ONLY_OCSP;133}134} else if (options.contains(Option.PREFER_CRLS)) {135mode = Mode.PREFER_CRLS;136}137onlyEE = options.contains(Option.ONLY_END_ENTITY);138}139if (legacy) {140crlDP = rp.crlDPEnabled;141} else {142crlDP = true;143}144ocspResponses = getOcspResponses();145ocspExtensions = getOcspExtensions();146147this.anchor = anchor;148this.params = params;149this.certStores = new ArrayList<>(params.certStores());150try {151this.certStores.add(CertStore.getInstance("Collection",152new CollectionCertStoreParameters(params.certificates())));153} catch (InvalidAlgorithmParameterException |154NoSuchAlgorithmException e) {155// should never occur but not necessarily fatal, so log it,156// ignore and continue157if (debug != null) {158debug.println("RevocationChecker: " +159"error creating Collection CertStore: " + e);160}161}162}163164private static URI toURI(String uriString)165throws CertPathValidatorException166{167try {168if (uriString != null) {169return new URI(uriString);170}171return null;172} catch (URISyntaxException e) {173throw new CertPathValidatorException(174"cannot parse ocsp.responderURL property", e);175}176}177178private static RevocationProperties getRevocationProperties() {179return AccessController.doPrivileged(180new PrivilegedAction<RevocationProperties>() {181public RevocationProperties run() {182RevocationProperties rp = new RevocationProperties();183String onlyEE = Security.getProperty(184"com.sun.security.onlyCheckRevocationOfEECert");185rp.onlyEE = onlyEE != null186&& onlyEE.equalsIgnoreCase("true");187String ocspEnabled = Security.getProperty("ocsp.enable");188rp.ocspEnabled = ocspEnabled != null189&& ocspEnabled.equalsIgnoreCase("true");190rp.ocspUrl = Security.getProperty("ocsp.responderURL");191rp.ocspSubject192= Security.getProperty("ocsp.responderCertSubjectName");193rp.ocspIssuer194= Security.getProperty("ocsp.responderCertIssuerName");195rp.ocspSerial196= Security.getProperty("ocsp.responderCertSerialNumber");197rp.crlDPEnabled198= Boolean.getBoolean("com.sun.security.enableCRLDP");199return rp;200}201}202);203}204205private static X509Certificate getResponderCert(RevocationProperties rp,206Set<TrustAnchor> anchors,207List<CertStore> stores)208throws CertPathValidatorException209{210if (rp.ocspSubject != null) {211return getResponderCert(rp.ocspSubject, anchors, stores);212} else if (rp.ocspIssuer != null && rp.ocspSerial != null) {213return getResponderCert(rp.ocspIssuer, rp.ocspSerial,214anchors, stores);215} else if (rp.ocspIssuer != null || rp.ocspSerial != null) {216throw new CertPathValidatorException(217"Must specify both ocsp.responderCertIssuerName and " +218"ocsp.responderCertSerialNumber properties");219}220return null;221}222223private static X509Certificate getResponderCert(String subject,224Set<TrustAnchor> anchors,225List<CertStore> stores)226throws CertPathValidatorException227{228X509CertSelector sel = new X509CertSelector();229try {230sel.setSubject(new X500Principal(subject));231} catch (IllegalArgumentException e) {232throw new CertPathValidatorException(233"cannot parse ocsp.responderCertSubjectName property", e);234}235return getResponderCert(sel, anchors, stores);236}237238private static X509Certificate getResponderCert(String issuer,239String serial,240Set<TrustAnchor> anchors,241List<CertStore> stores)242throws CertPathValidatorException243{244X509CertSelector sel = new X509CertSelector();245try {246sel.setIssuer(new X500Principal(issuer));247} catch (IllegalArgumentException e) {248throw new CertPathValidatorException(249"cannot parse ocsp.responderCertIssuerName property", e);250}251try {252sel.setSerialNumber(new BigInteger(stripOutSeparators(serial), 16));253} catch (NumberFormatException e) {254throw new CertPathValidatorException(255"cannot parse ocsp.responderCertSerialNumber property", e);256}257return getResponderCert(sel, anchors, stores);258}259260private static X509Certificate getResponderCert(X509CertSelector sel,261Set<TrustAnchor> anchors,262List<CertStore> stores)263throws CertPathValidatorException264{265// first check TrustAnchors266for (TrustAnchor anchor : anchors) {267X509Certificate cert = anchor.getTrustedCert();268if (cert == null) {269continue;270}271if (sel.match(cert)) {272return cert;273}274}275// now check CertStores276for (CertStore store : stores) {277try {278Collection<? extends Certificate> certs =279store.getCertificates(sel);280if (!certs.isEmpty()) {281return (X509Certificate)certs.iterator().next();282}283} catch (CertStoreException e) {284// ignore and try next CertStore285if (debug != null) {286debug.println("CertStore exception:" + e);287}288continue;289}290}291throw new CertPathValidatorException(292"Cannot find the responder's certificate " +293"(set using the OCSP security properties).");294}295296@Override297public void init(boolean forward) throws CertPathValidatorException {298if (forward) {299throw new300CertPathValidatorException("forward checking not supported");301}302if (anchor != null) {303issuerInfo = new OCSPResponse.IssuerInfo(anchor);304prevPubKey = issuerInfo.getPublicKey();305306}307crlSignFlag = true;308if (params != null && params.certPath() != null) {309certIndex = params.certPath().getCertificates().size() - 1;310} else {311certIndex = -1;312}313softFailExceptions.clear();314}315316@Override317public boolean isForwardCheckingSupported() {318return false;319}320321@Override322public Set<String> getSupportedExtensions() {323return null;324}325326@Override327public List<CertPathValidatorException> getSoftFailExceptions() {328return Collections.unmodifiableList(softFailExceptions);329}330331@Override332public void check(Certificate cert, Collection<String> unresolvedCritExts)333throws CertPathValidatorException334{335check((X509Certificate)cert, unresolvedCritExts,336prevPubKey, crlSignFlag);337}338339private void check(X509Certificate xcert,340Collection<String> unresolvedCritExts,341PublicKey pubKey, boolean crlSignFlag)342throws CertPathValidatorException343{344if (debug != null) {345debug.println("RevocationChecker.check: checking cert" +346"\n SN: " + Debug.toHexString(xcert.getSerialNumber()) +347"\n Subject: " + xcert.getSubjectX500Principal() +348"\n Issuer: " + xcert.getIssuerX500Principal());349}350try {351if (onlyEE && xcert.getBasicConstraints() != -1) {352if (debug != null) {353debug.println("Skipping revocation check; cert is not " +354"an end entity cert");355}356return;357}358switch (mode) {359case PREFER_OCSP:360case ONLY_OCSP:361checkOCSP(xcert, unresolvedCritExts);362break;363case PREFER_CRLS:364case ONLY_CRLS:365checkCRLs(xcert, unresolvedCritExts, null,366pubKey, crlSignFlag);367break;368}369} catch (CertPathValidatorException e) {370if (e.getReason() == BasicReason.REVOKED) {371throw e;372}373boolean eSoftFail = isSoftFailException(e);374if (eSoftFail) {375if (mode == Mode.ONLY_OCSP || mode == Mode.ONLY_CRLS) {376return;377}378} else {379if (mode == Mode.ONLY_OCSP || mode == Mode.ONLY_CRLS) {380throw e;381}382}383CertPathValidatorException cause = e;384// Otherwise, failover385if (debug != null) {386debug.println("RevocationChecker.check() " + e.getMessage());387debug.println("RevocationChecker.check() preparing to failover");388}389try {390switch (mode) {391case PREFER_OCSP:392checkCRLs(xcert, unresolvedCritExts, null,393pubKey, crlSignFlag);394break;395case PREFER_CRLS:396checkOCSP(xcert, unresolvedCritExts);397break;398}399} catch (CertPathValidatorException x) {400if (debug != null) {401debug.println("RevocationChecker.check() failover failed");402debug.println("RevocationChecker.check() " + x.getMessage());403}404if (x.getReason() == BasicReason.REVOKED) {405throw x;406}407if (!isSoftFailException(x)) {408cause.addSuppressed(x);409throw cause;410} else {411// only pass if both exceptions were soft failures412if (!eSoftFail) {413throw cause;414}415}416}417} finally {418updateState(xcert);419}420}421422private boolean isSoftFailException(CertPathValidatorException e) {423if (softFail &&424e.getReason() == BasicReason.UNDETERMINED_REVOCATION_STATUS)425{426// recreate exception with correct index427CertPathValidatorException e2 = new CertPathValidatorException(428e.getMessage(), e.getCause(), params.certPath(), certIndex,429e.getReason());430softFailExceptions.addFirst(e2);431return true;432}433return false;434}435436private void updateState(X509Certificate cert)437throws CertPathValidatorException438{439issuerInfo = new OCSPResponse.IssuerInfo(anchor, cert);440441// Make new public key if parameters are missing442PublicKey pubKey = cert.getPublicKey();443if (PKIX.isDSAPublicKeyWithoutParams(pubKey)) {444// pubKey needs to inherit DSA parameters from prev key445pubKey = BasicChecker.makeInheritedParamsKey(pubKey, prevPubKey);446}447prevPubKey = pubKey;448crlSignFlag = certCanSignCrl(cert);449if (certIndex > 0) {450certIndex--;451}452}453454// Maximum clock skew in milliseconds (15 minutes) allowed when checking455// validity of CRLs456private static final long MAX_CLOCK_SKEW = 900000;457private void checkCRLs(X509Certificate cert,458Collection<String> unresolvedCritExts,459Set<X509Certificate> stackedCerts,460PublicKey pubKey, boolean signFlag)461throws CertPathValidatorException462{463checkCRLs(cert, pubKey, null, signFlag, true,464stackedCerts, params.trustAnchors());465}466467static boolean isCausedByNetworkIssue(String type, CertStoreException cse) {468boolean result;469Throwable t = cse.getCause();470471switch (type) {472case "LDAP":473if (t != null) {474// These two exception classes are inside java.naming module475String cn = t.getClass().getName();476result = (cn.equals("javax.naming.ServiceUnavailableException") ||477cn.equals("javax.naming.CommunicationException"));478} else {479result = false;480}481break;482case "SSLServer":483result = (t != null && t instanceof IOException);484break;485case "URI":486result = (t != null && t instanceof IOException);487break;488default:489// we don't know about any other remote CertStore types490return false;491}492return result;493}494495private void checkCRLs(X509Certificate cert, PublicKey prevKey,496X509Certificate prevCert, boolean signFlag,497boolean allowSeparateKey,498Set<X509Certificate> stackedCerts,499Set<TrustAnchor> anchors)500throws CertPathValidatorException501{502if (debug != null) {503debug.println("RevocationChecker.checkCRLs()" +504" ---checking revocation status ...");505}506507// Reject circular dependencies - RFC 5280 is not explicit on how508// to handle this, but does suggest that they can be a security509// risk and can create unresolvable dependencies510if (stackedCerts != null && stackedCerts.contains(cert)) {511if (debug != null) {512debug.println("RevocationChecker.checkCRLs()" +513" circular dependency");514}515throw new CertPathValidatorException516("Could not determine revocation status", null, null, -1,517BasicReason.UNDETERMINED_REVOCATION_STATUS);518}519520Set<X509CRL> possibleCRLs = new HashSet<>();521Set<X509CRL> approvedCRLs = new HashSet<>();522X509CRLSelector sel = new X509CRLSelector();523sel.setCertificateChecking(cert);524CertPathHelper.setDateAndTime(sel, params.date(), MAX_CLOCK_SKEW);525526// First, check user-specified CertStores527CertPathValidatorException networkFailureException = null;528for (CertStore store : certStores) {529try {530for (CRL crl : store.getCRLs(sel)) {531possibleCRLs.add((X509CRL)crl);532}533} catch (CertStoreException e) {534if (debug != null) {535debug.println("RevocationChecker.checkCRLs() " +536"CertStoreException: " + e.getMessage());537}538if (networkFailureException == null &&539isCausedByNetworkIssue(store.getType(),e)) {540// save this exception, we may need to throw it later541networkFailureException = new CertPathValidatorException(542"Unable to determine revocation status due to " +543"network error", e, null, -1,544BasicReason.UNDETERMINED_REVOCATION_STATUS);545}546}547}548549if (debug != null) {550debug.println("RevocationChecker.checkCRLs() " +551"possible crls.size() = " + possibleCRLs.size());552}553boolean[] reasonsMask = new boolean[9];554if (!possibleCRLs.isEmpty()) {555// Now that we have a list of possible CRLs, see which ones can556// be approved557approvedCRLs.addAll(verifyPossibleCRLs(possibleCRLs, cert, prevKey,558signFlag, reasonsMask,559anchors));560}561562if (debug != null) {563debug.println("RevocationChecker.checkCRLs() " +564"approved crls.size() = " + approvedCRLs.size());565}566567// make sure that we have at least one CRL that _could_ cover568// the certificate in question and all reasons are covered569if (!approvedCRLs.isEmpty() &&570Arrays.equals(reasonsMask, ALL_REASONS))571{572checkApprovedCRLs(cert, approvedCRLs);573} else {574// Check Distribution Points575// all CRLs returned by the DP Fetcher have also been verified576try {577if (crlDP) {578approvedCRLs.addAll(DistributionPointFetcher.getCRLs(579sel, signFlag, prevKey, prevCert,580params.sigProvider(), certStores, reasonsMask,581anchors, null, params.variant(), anchor));582}583} catch (CertStoreException e) {584if (e instanceof CertStoreTypeException) {585CertStoreTypeException cste = (CertStoreTypeException)e;586if (isCausedByNetworkIssue(cste.getType(), e)) {587throw new CertPathValidatorException(588"Unable to determine revocation status due to " +589"network error", e, null, -1,590BasicReason.UNDETERMINED_REVOCATION_STATUS);591}592}593throw new CertPathValidatorException(e);594}595if (!approvedCRLs.isEmpty() &&596Arrays.equals(reasonsMask, ALL_REASONS))597{598checkApprovedCRLs(cert, approvedCRLs);599} else {600if (allowSeparateKey) {601try {602verifyWithSeparateSigningKey(cert, prevKey, signFlag,603stackedCerts);604return;605} catch (CertPathValidatorException cpve) {606if (networkFailureException != null) {607// if a network issue previously prevented us from608// retrieving a CRL from one of the user-specified609// CertStores, throw it now so it can be handled610// appropriately611throw networkFailureException;612}613throw cpve;614}615} else {616if (networkFailureException != null) {617// if a network issue previously prevented us from618// retrieving a CRL from one of the user-specified619// CertStores, throw it now so it can be handled620// appropriately621throw networkFailureException;622}623throw new CertPathValidatorException(624"Could not determine revocation status", null, null, -1,625BasicReason.UNDETERMINED_REVOCATION_STATUS);626}627}628}629}630631private void checkApprovedCRLs(X509Certificate cert,632Set<X509CRL> approvedCRLs)633throws CertPathValidatorException634{635// See if the cert is in the set of approved crls.636if (debug != null) {637BigInteger sn = cert.getSerialNumber();638debug.println("RevocationChecker.checkApprovedCRLs() " +639"starting the final sweep...");640debug.println("RevocationChecker.checkApprovedCRLs()" +641" cert SN: " + sn.toString());642}643644CRLReason reasonCode = CRLReason.UNSPECIFIED;645X509CRLEntryImpl entry = null;646for (X509CRL crl : approvedCRLs) {647X509CRLEntry e = crl.getRevokedCertificate(cert);648if (e != null) {649try {650entry = X509CRLEntryImpl.toImpl(e);651} catch (CRLException ce) {652throw new CertPathValidatorException(ce);653}654if (debug != null) {655debug.println("RevocationChecker.checkApprovedCRLs()"656+ " CRL entry: " + entry.toString());657}658659/*660* Abort CRL validation and throw exception if there are any661* unrecognized critical CRL entry extensions (see section662* 5.3 of RFC 5280).663*/664Set<String> unresCritExts = entry.getCriticalExtensionOIDs();665if (unresCritExts != null && !unresCritExts.isEmpty()) {666/* remove any that we will process */667unresCritExts.remove(ReasonCode_Id.toString());668unresCritExts.remove(CertificateIssuer_Id.toString());669if (!unresCritExts.isEmpty()) {670throw new CertPathValidatorException(671"Unrecognized critical extension(s) in revoked " +672"CRL entry");673}674}675676reasonCode = entry.getRevocationReason();677if (reasonCode == null) {678reasonCode = CRLReason.UNSPECIFIED;679}680Date revocationDate = entry.getRevocationDate();681if (revocationDate.before(params.date())) {682Throwable t = new CertificateRevokedException(683revocationDate, reasonCode,684crl.getIssuerX500Principal(), entry.getExtensions());685throw new CertPathValidatorException(686t.getMessage(), t, null, -1, BasicReason.REVOKED);687}688}689}690}691692private void checkOCSP(X509Certificate cert,693Collection<String> unresolvedCritExts)694throws CertPathValidatorException695{696X509CertImpl currCert = null;697try {698currCert = X509CertImpl.toImpl(cert);699} catch (CertificateException ce) {700throw new CertPathValidatorException(ce);701}702703// The algorithm constraints of the OCSP trusted responder certificate704// does not need to be checked in this code. The constraints will be705// checked when the responder's certificate is validated.706707OCSPResponse response = null;708CertId certId = null;709try {710certId = new CertId(issuerInfo.getName(), issuerInfo.getPublicKey(),711currCert.getSerialNumberObject());712713// check if there is a cached OCSP response available714byte[] responseBytes = ocspResponses.get(cert);715if (responseBytes != null) {716if (debug != null) {717debug.println("Found cached OCSP response");718}719response = new OCSPResponse(responseBytes);720721// verify the response722byte[] nonce = null;723for (Extension ext : ocspExtensions) {724if (ext.getId().equals("1.3.6.1.5.5.7.48.1.2")) {725nonce = ext.getValue();726}727}728response.verify(Collections.singletonList(certId), issuerInfo,729responderCert, params.date(), nonce, params.variant());730731} else {732URI responderURI = (this.responderURI != null)733? this.responderURI734: OCSP.getResponderURI(currCert);735if (responderURI == null) {736throw new CertPathValidatorException(737"Certificate does not specify OCSP responder", null,738null, -1);739}740741response = OCSP.check(Collections.singletonList(certId),742responderURI, issuerInfo, responderCert, null,743ocspExtensions, params.variant());744}745} catch (IOException e) {746throw new CertPathValidatorException(747"Unable to determine revocation status due to network error",748e, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS);749}750751RevocationStatus rs =752(RevocationStatus)response.getSingleResponse(certId);753RevocationStatus.CertStatus certStatus = rs.getCertStatus();754if (certStatus == RevocationStatus.CertStatus.REVOKED) {755Date revocationTime = rs.getRevocationTime();756if (revocationTime.before(params.date())) {757Throwable t = new CertificateRevokedException(758revocationTime, rs.getRevocationReason(),759response.getSignerCertificate().getSubjectX500Principal(),760rs.getSingleExtensions());761throw new CertPathValidatorException(t.getMessage(), t, null,762-1, BasicReason.REVOKED);763}764} else if (certStatus == RevocationStatus.CertStatus.UNKNOWN) {765throw new CertPathValidatorException(766"Certificate's revocation status is unknown", null,767params.certPath(), -1,768BasicReason.UNDETERMINED_REVOCATION_STATUS);769}770}771772/*773* Removes any non-hexadecimal characters from a string.774*/775private static final String HEX_DIGITS = "0123456789ABCDEFabcdef";776private static String stripOutSeparators(String value) {777char[] chars = value.toCharArray();778StringBuilder hexNumber = new StringBuilder();779for (int i = 0; i < chars.length; i++) {780if (HEX_DIGITS.indexOf(chars[i]) != -1) {781hexNumber.append(chars[i]);782}783}784return hexNumber.toString();785}786787/**788* Checks that a cert can be used to verify a CRL.789*790* @param cert an X509Certificate to check791* @return a boolean specifying if the cert is allowed to vouch for the792* validity of a CRL793*/794static boolean certCanSignCrl(X509Certificate cert) {795// if the cert doesn't include the key usage ext, or796// the key usage ext asserts cRLSigning, return true,797// otherwise return false.798boolean[] keyUsage = cert.getKeyUsage();799if (keyUsage != null) {800return keyUsage[6];801}802return false;803}804805/**806* Internal method that verifies a set of possible_crls,807* and sees if each is approved, based on the cert.808*809* @param crls a set of possible CRLs to test for acceptability810* @param cert the certificate whose revocation status is being checked811* @param signFlag <code>true</code> if prevKey was trusted to sign CRLs812* @param prevKey the public key of the issuer of cert813* @param reasonsMask the reason code mask814* @param trustAnchors a <code>Set</code> of <code>TrustAnchor</code>s>815* @return a collection of approved crls (or an empty collection)816*/817private static final boolean[] ALL_REASONS =818{true, true, true, true, true, true, true, true, true};819private Collection<X509CRL> verifyPossibleCRLs(Set<X509CRL> crls,820X509Certificate cert,821PublicKey prevKey,822boolean signFlag,823boolean[] reasonsMask,824Set<TrustAnchor> anchors)825throws CertPathValidatorException826{827try {828X509CertImpl certImpl = X509CertImpl.toImpl(cert);829if (debug != null) {830debug.println("RevocationChecker.verifyPossibleCRLs: " +831"Checking CRLDPs for "832+ certImpl.getSubjectX500Principal());833}834CRLDistributionPointsExtension ext =835certImpl.getCRLDistributionPointsExtension();836List<DistributionPoint> points = null;837if (ext == null) {838// assume a DP with reasons and CRLIssuer fields omitted839// and a DP name of the cert issuer.840// TODO add issuerAltName too841X500Name certIssuer = (X500Name)certImpl.getIssuerDN();842DistributionPoint point = new DistributionPoint(843new GeneralNames().add(new GeneralName(certIssuer)),844null, null);845points = Collections.singletonList(point);846} else {847points = ext.get(CRLDistributionPointsExtension.POINTS);848}849Set<X509CRL> results = new HashSet<>();850for (DistributionPoint point : points) {851for (X509CRL crl : crls) {852if (DistributionPointFetcher.verifyCRL(853certImpl, point, crl, reasonsMask, signFlag,854prevKey, null, params.sigProvider(), anchors,855certStores, params.date(), params.variant(), anchor))856{857results.add(crl);858}859}860if (Arrays.equals(reasonsMask, ALL_REASONS))861break;862}863return results;864} catch (CertificateException | CRLException | IOException e) {865if (debug != null) {866debug.println("Exception while verifying CRL: "+e.getMessage());867e.printStackTrace();868}869return Collections.emptySet();870}871}872873/**874* We have a cert whose revocation status couldn't be verified by875* a CRL issued by the cert that issued the CRL. See if we can876* find a valid CRL issued by a separate key that can verify the877* revocation status of this certificate.878* <p>879* Note that this does not provide support for indirect CRLs,880* only CRLs signed with a different key (but the same issuer881* name) as the certificate being checked.882*883* @param currCert the <code>X509Certificate</code> to be checked884* @param prevKey the <code>PublicKey</code> that failed885* @param signFlag <code>true</code> if that key was trusted to sign CRLs886* @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s>887* whose revocation status depends on the888* non-revoked status of this cert. To avoid889* circular dependencies, we assume they're890* revoked while checking the revocation891* status of this cert.892* @throws CertPathValidatorException if the cert's revocation status893* cannot be verified successfully with another key894*/895private void verifyWithSeparateSigningKey(X509Certificate cert,896PublicKey prevKey,897boolean signFlag,898Set<X509Certificate> stackedCerts)899throws CertPathValidatorException900{901String msg = "revocation status";902if (debug != null) {903debug.println(904"RevocationChecker.verifyWithSeparateSigningKey()" +905" ---checking " + msg + "...");906}907908// Reject circular dependencies - RFC 5280 is not explicit on how909// to handle this, but does suggest that they can be a security910// risk and can create unresolvable dependencies911if ((stackedCerts != null) && stackedCerts.contains(cert)) {912if (debug != null) {913debug.println(914"RevocationChecker.verifyWithSeparateSigningKey()" +915" circular dependency");916}917throw new CertPathValidatorException918("Could not determine revocation status", null, null, -1,919BasicReason.UNDETERMINED_REVOCATION_STATUS);920}921922// Try to find another key that might be able to sign923// CRLs vouching for this cert.924// If prevKey wasn't trusted, maybe we just didn't have the right925// path to it. Don't rule that key out.926if (!signFlag) {927buildToNewKey(cert, null, stackedCerts);928} else {929buildToNewKey(cert, prevKey, stackedCerts);930}931}932933/**934* Tries to find a CertPath that establishes a key that can be935* used to verify the revocation status of a given certificate.936* Ignores keys that have previously been tried. Throws a937* CertPathValidatorException if no such key could be found.938*939* @param currCert the <code>X509Certificate</code> to be checked940* @param prevKey the <code>PublicKey</code> of the certificate whose key941* cannot be used to vouch for the CRL and should be ignored942* @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s>943* whose revocation status depends on the944* establishment of this path.945* @throws CertPathValidatorException on failure946*/947private static final boolean [] CRL_SIGN_USAGE =948{ false, false, false, false, false, false, true };949private void buildToNewKey(X509Certificate currCert,950PublicKey prevKey,951Set<X509Certificate> stackedCerts)952throws CertPathValidatorException953{954955if (debug != null) {956debug.println("RevocationChecker.buildToNewKey()" +957" starting work");958}959Set<PublicKey> badKeys = new HashSet<>();960if (prevKey != null) {961badKeys.add(prevKey);962}963X509CertSelector certSel = new RejectKeySelector(badKeys);964certSel.setSubject(currCert.getIssuerX500Principal());965certSel.setKeyUsage(CRL_SIGN_USAGE);966967Set<TrustAnchor> newAnchors = anchor == null ?968params.trustAnchors() :969Collections.singleton(anchor);970971PKIXBuilderParameters builderParams;972try {973builderParams = new PKIXBuilderParameters(newAnchors, certSel);974} catch (InvalidAlgorithmParameterException iape) {975throw new RuntimeException(iape); // should never occur976}977builderParams.setInitialPolicies(params.initialPolicies());978builderParams.setCertStores(certStores);979builderParams.setExplicitPolicyRequired980(params.explicitPolicyRequired());981builderParams.setPolicyMappingInhibited982(params.policyMappingInhibited());983builderParams.setAnyPolicyInhibited(params.anyPolicyInhibited());984// Policy qualifiers must be rejected, since we don't have985// any way to convey them back to the application.986// That's the default, so no need to write code.987builderParams.setDate(params.date());988builderParams.setCertPathCheckers(params.certPathCheckers());989builderParams.setSigProvider(params.sigProvider());990991// Skip revocation during this build to detect circular992// references. But check revocation afterwards, using the993// key (or any other that works).994builderParams.setRevocationEnabled(false);995996// check for AuthorityInformationAccess extension997if (Builder.USE_AIA == true) {998X509CertImpl currCertImpl = null;999try {1000currCertImpl = X509CertImpl.toImpl(currCert);1001} catch (CertificateException ce) {1002// ignore but log it1003if (debug != null) {1004debug.println("RevocationChecker.buildToNewKey: " +1005"error decoding cert: " + ce);1006}1007}1008AuthorityInfoAccessExtension aiaExt = null;1009if (currCertImpl != null) {1010aiaExt = currCertImpl.getAuthorityInfoAccessExtension();1011}1012if (aiaExt != null) {1013List<AccessDescription> adList = aiaExt.getAccessDescriptions();1014if (adList != null) {1015for (AccessDescription ad : adList) {1016CertStore cs = URICertStore.getInstance(ad);1017if (cs != null) {1018if (debug != null) {1019debug.println("adding AIAext CertStore");1020}1021builderParams.addCertStore(cs);1022}1023}1024}1025}1026}10271028CertPathBuilder builder = null;1029try {1030builder = CertPathBuilder.getInstance("PKIX");1031} catch (NoSuchAlgorithmException nsae) {1032throw new CertPathValidatorException(nsae);1033}1034while (true) {1035try {1036if (debug != null) {1037debug.println("RevocationChecker.buildToNewKey()" +1038" about to try build ...");1039}1040PKIXCertPathBuilderResult cpbr =1041(PKIXCertPathBuilderResult)builder.build(builderParams);10421043if (debug != null) {1044debug.println("RevocationChecker.buildToNewKey()" +1045" about to check revocation ...");1046}1047// Now check revocation of all certs in path, assuming that1048// the stackedCerts are revoked.1049if (stackedCerts == null) {1050stackedCerts = new HashSet<X509Certificate>();1051}1052stackedCerts.add(currCert);1053TrustAnchor ta = cpbr.getTrustAnchor();1054PublicKey prevKey2 = ta.getCAPublicKey();1055if (prevKey2 == null) {1056prevKey2 = ta.getTrustedCert().getPublicKey();1057}1058boolean signFlag = true;1059List<? extends Certificate> cpList =1060cpbr.getCertPath().getCertificates();1061try {1062for (int i = cpList.size() - 1; i >= 0; i--) {1063X509Certificate cert = (X509Certificate) cpList.get(i);10641065if (debug != null) {1066debug.println("RevocationChecker.buildToNewKey()"1067+ " index " + i + " checking "1068+ cert);1069}1070checkCRLs(cert, prevKey2, null, signFlag, true,1071stackedCerts, newAnchors);1072signFlag = certCanSignCrl(cert);1073prevKey2 = cert.getPublicKey();1074}1075} catch (CertPathValidatorException cpve) {1076// ignore it and try to get another key1077badKeys.add(cpbr.getPublicKey());1078continue;1079}10801081if (debug != null) {1082debug.println("RevocationChecker.buildToNewKey()" +1083" got key " + cpbr.getPublicKey());1084}1085// Now check revocation on the current cert using that key and1086// the corresponding certificate.1087// If it doesn't check out, try to find a different key.1088// And if we can't find a key, then return false.1089PublicKey newKey = cpbr.getPublicKey();1090X509Certificate newCert = cpList.isEmpty() ?1091null : (X509Certificate) cpList.get(0);1092try {1093checkCRLs(currCert, newKey, newCert,1094true, false, null, params.trustAnchors());1095// If that passed, the cert is OK!1096return;1097} catch (CertPathValidatorException cpve) {1098// If it is revoked, rethrow exception1099if (cpve.getReason() == BasicReason.REVOKED) {1100throw cpve;1101}1102// Otherwise, ignore the exception and1103// try to get another key.1104}1105badKeys.add(newKey);1106} catch (InvalidAlgorithmParameterException iape) {1107throw new CertPathValidatorException(iape);1108} catch (CertPathBuilderException cpbe) {1109throw new CertPathValidatorException1110("Could not determine revocation status", null, null,1111-1, BasicReason.UNDETERMINED_REVOCATION_STATUS);1112}1113}1114}11151116/*1117* This inner class extends the X509CertSelector to add an additional1118* check to make sure the subject public key isn't on a particular list.1119* This class is used by buildToNewKey() to make sure the builder doesn't1120* end up with a CertPath to a public key that has already been rejected.1121*/1122private static class RejectKeySelector extends X509CertSelector {1123private final Set<PublicKey> badKeySet;11241125/**1126* Creates a new <code>RejectKeySelector</code>.1127*1128* @param badPublicKeys a <code>Set</code> of1129* <code>PublicKey</code>s that1130* should be rejected (or <code>null</code>1131* if no such check should be done)1132*/1133RejectKeySelector(Set<PublicKey> badPublicKeys) {1134this.badKeySet = badPublicKeys;1135}11361137/**1138* Decides whether a <code>Certificate</code> should be selected.1139*1140* @param cert the <code>Certificate</code> to be checked1141* @return <code>true</code> if the <code>Certificate</code> should be1142* selected, <code>false</code> otherwise1143*/1144@Override1145public boolean match(Certificate cert) {1146if (!super.match(cert))1147return(false);11481149if (badKeySet.contains(cert.getPublicKey())) {1150if (debug != null)1151debug.println("RejectKeySelector.match: bad key");1152return false;1153}11541155if (debug != null)1156debug.println("RejectKeySelector.match: returning true");1157return true;1158}11591160/**1161* Return a printable representation of the <code>CertSelector</code>.1162*1163* @return a <code>String</code> describing the contents of the1164* <code>CertSelector</code>1165*/1166@Override1167public String toString() {1168StringBuilder sb = new StringBuilder();1169sb.append("RejectKeySelector: [\n");1170sb.append(super.toString());1171sb.append(badKeySet);1172sb.append("]");1173return sb.toString();1174}1175}1176}117711781179