389 lines · 15.3 KB · Java
RawDownload
/* * Copyright (c) 2002, 2010, 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.validator; import java.util.*; import java.security.*;import java.security.cert.*; import javax.security.auth.x500.X500Principal;import sun.security.action.GetBooleanAction;import sun.security.provider.certpath.AlgorithmChecker; /** * Validator implementation built on the PKIX CertPath API. This * implementation will be emphasized going forward.<p> * <p> * Note that the validate() implementation tries to use a PKIX validator * if that appears possible and a PKIX builder otherwise. This increases * performance and currently also leads to better exception messages * in case of failures. * <p> * {@code PKIXValidator} objects are immutable once they have been created. * Please DO NOT add methods that can change the state of an instance once * it has been created. * * @author Andreas Sterbenz */public final class PKIXValidator extends Validator {     /**     * Flag indicating whether to enable revocation check for the PKIX trust     * manager. Typically, this will only work if the PKIX implementation     * supports CRL distribution points as we do not manually setup CertStores.     */    private final static boolean checkTLSRevocation =        AccessController.doPrivileged            (new GetBooleanAction("com.sun.net.ssl.checkRevocation"));     // enable use of the validator if possible    private final static boolean TRY_VALIDATOR = true;     private final Set<X509Certificate> trustedCerts;    private final PKIXBuilderParameters parameterTemplate;    private int certPathLength = -1;     // needed only for the validator    private final Map<X500Principal, List<PublicKey>> trustedSubjects;    private final CertificateFactory factory;     private final boolean plugin;     PKIXValidator(String variant, Collection<X509Certificate> trustedCerts) {        super(TYPE_PKIX, variant);        if (trustedCerts instanceof Set) {            this.trustedCerts = (Set<X509Certificate>)trustedCerts;        } else {            this.trustedCerts = new HashSet<X509Certificate>(trustedCerts);        }        Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();        for (X509Certificate cert : trustedCerts) {            trustAnchors.add(new TrustAnchor(cert, null));        }        try {            parameterTemplate = new PKIXBuilderParameters(trustAnchors, null);        } catch (InvalidAlgorithmParameterException e) {            throw new RuntimeException("Unexpected error: " + e.toString(), e);        }        setDefaultParameters(variant);         // initCommon();        if (TRY_VALIDATOR) {            if (TRY_VALIDATOR == false) {                return;            }            trustedSubjects = new HashMap<X500Principal, List<PublicKey>>();            for (X509Certificate cert : trustedCerts) {                X500Principal dn = cert.getSubjectX500Principal();                List<PublicKey> keys;                if (trustedSubjects.containsKey(dn)) {                    keys = trustedSubjects.get(dn);                } else {                    keys = new ArrayList<PublicKey>();                    trustedSubjects.put(dn, keys);                }                keys.add(cert.getPublicKey());            }            try {                factory = CertificateFactory.getInstance("X.509");            } catch (CertificateException e) {                throw new RuntimeException("Internal error", e);            }            plugin = variant.equals(VAR_PLUGIN_CODE_SIGNING);        } else {            plugin = false;        }    }     PKIXValidator(String variant, PKIXBuilderParameters params) {        super(TYPE_PKIX, variant);        trustedCerts = new HashSet<X509Certificate>();        for (TrustAnchor anchor : params.getTrustAnchors()) {            X509Certificate cert = anchor.getTrustedCert();            if (cert != null) {                trustedCerts.add(cert);            }        }        parameterTemplate = params;         // initCommon();        if (TRY_VALIDATOR) {            if (TRY_VALIDATOR == false) {                return;            }            trustedSubjects = new HashMap<X500Principal, List<PublicKey>>();            for (X509Certificate cert : trustedCerts) {                X500Principal dn = cert.getSubjectX500Principal();                List<PublicKey> keys;                if (trustedSubjects.containsKey(dn)) {                    keys = trustedSubjects.get(dn);                } else {                    keys = new ArrayList<PublicKey>();                    trustedSubjects.put(dn, keys);                }                keys.add(cert.getPublicKey());            }            try {                factory = CertificateFactory.getInstance("X.509");            } catch (CertificateException e) {                throw new RuntimeException("Internal error", e);            }            plugin = variant.equals(VAR_PLUGIN_CODE_SIGNING);        } else {            plugin = false;        }    }     public Collection<X509Certificate> getTrustedCertificates() {        return trustedCerts;    }     /**     * Returns the length of the last certification path that is validated by     * CertPathValidator. This is intended primarily as a callback mechanism     * for PKIXCertPathCheckers to determine the length of the certification     * path that is being validated. It is necessary since engineValidate()     * may modify the length of the path.     *     * @return the length of the last certification path passed to     *   CertPathValidator.validate, or -1 if it has not been invoked yet     */    public int getCertPathLength() { // mutable, should be private        return certPathLength;    }     /**     * Set J2SE global default PKIX parameters. Currently, hardcoded to disable     * revocation checking. In the future, this should be configurable.     */    private void setDefaultParameters(String variant) {        if ((variant == Validator.VAR_TLS_SERVER) ||                (variant == Validator.VAR_TLS_CLIENT)) {            parameterTemplate.setRevocationEnabled(checkTLSRevocation);        } else {            parameterTemplate.setRevocationEnabled(false);        }    }     /**     * Return the PKIX parameters used by this instance. An application may     * modify the parameters but must make sure not to perform any concurrent     * validations.     */    public PKIXBuilderParameters getParameters() { // mutable, should be private        return parameterTemplate;    }     @Override    X509Certificate[] engineValidate(X509Certificate[] chain,            Collection<X509Certificate> otherCerts,            AlgorithmConstraints constraints,            Object parameter) throws CertificateException {        if ((chain == null) || (chain.length == 0)) {            throw new CertificateException                ("null or zero-length certificate chain");        }         // add  new algorithm constraints checker        PKIXBuilderParameters pkixParameters =                    (PKIXBuilderParameters) parameterTemplate.clone();        AlgorithmChecker algorithmChecker = null;        if (constraints != null) {            algorithmChecker = new AlgorithmChecker(constraints);            pkixParameters.addCertPathChecker(algorithmChecker);        }         if (TRY_VALIDATOR) {            // check that chain is in correct order and check if chain contains            // trust anchor            X500Principal prevIssuer = null;            for (int i = 0; i < chain.length; i++) {                X509Certificate cert = chain[i];                X500Principal dn = cert.getSubjectX500Principal();                if (i != 0 &&                    !dn.equals(prevIssuer)) {                    // chain is not ordered correctly, call builder instead                    return doBuild(chain, otherCerts, pkixParameters);                }                 // Check if chain[i] is already trusted. It may be inside                // trustedCerts, or has the same dn and public key as a cert                // inside trustedCerts. The latter happens when a CA has                // updated its cert with a stronger signature algorithm in JRE                // but the weak one is still in circulation.                 if (trustedCerts.contains(cert) ||          // trusted cert                        (trustedSubjects.containsKey(dn) && // replacing ...                         trustedSubjects.get(dn).contains(  // ... weak cert                            cert.getPublicKey()))) {                    if (i == 0) {                        return new X509Certificate[] {chain[0]};                    }                    // Remove and call validator on partial chain [0 .. i-1]                    X509Certificate[] newChain = new X509Certificate[i];                    System.arraycopy(chain, 0, newChain, 0, i);                    return doValidate(newChain, pkixParameters);                }                prevIssuer = cert.getIssuerX500Principal();            }             // apparently issued by trust anchor?            X509Certificate last = chain[chain.length - 1];            X500Principal issuer = last.getIssuerX500Principal();            X500Principal subject = last.getSubjectX500Principal();            if (trustedSubjects.containsKey(issuer) &&                    isSignatureValid(trustedSubjects.get(issuer), last)) {                return doValidate(chain, pkixParameters);            }             // don't fallback to builder if called from plugin/webstart            if (plugin) {                // Validate chain even if no trust anchor is found. This                // allows plugin/webstart to make sure the chain is                // otherwise valid                if (chain.length > 1) {                    X509Certificate[] newChain =                        new X509Certificate[chain.length-1];                    System.arraycopy(chain, 0, newChain, 0, newChain.length);                     // temporarily set last cert as sole trust anchor                    try {                        pkixParameters.setTrustAnchors                            (Collections.singleton(new TrustAnchor                                (chain[chain.length-1], null)));                    } catch (InvalidAlgorithmParameterException iape) {                        // should never occur, but ...                        throw new CertificateException(iape);                    }                    doValidate(newChain, pkixParameters);                }                // if the rest of the chain is valid, throw exception                // indicating no trust anchor was found                throw new ValidatorException                    (ValidatorException.T_NO_TRUST_ANCHOR);            }            // otherwise, fall back to builder        }         return doBuild(chain, otherCerts, pkixParameters);    }     private boolean isSignatureValid(List<PublicKey> keys,            X509Certificate sub) {        if (plugin) {            for (PublicKey key: keys) {                try {                    sub.verify(key);                    return true;                } catch (Exception ex) {                    continue;                }            }            return false;        }        return true; // only check if PLUGIN is set    }     private static X509Certificate[] toArray(CertPath path, TrustAnchor anchor)            throws CertificateException {        List<? extends java.security.cert.Certificate> list =                                                path.getCertificates();        X509Certificate[] chain = new X509Certificate[list.size() + 1];        list.toArray(chain);        X509Certificate trustedCert = anchor.getTrustedCert();        if (trustedCert == null) {            throw new ValidatorException                ("TrustAnchor must be specified as certificate");        }        chain[chain.length - 1] = trustedCert;        return chain;    }     /**     * Set the check date (for debugging).     */    private void setDate(PKIXBuilderParameters params) {        Date date = validationDate;        if (date != null) {            params.setDate(date);        }    }     private X509Certificate[] doValidate(X509Certificate[] chain,            PKIXBuilderParameters params) throws CertificateException {        try {            setDate(params);             // do the validation            CertPathValidator validator = CertPathValidator.getInstance("PKIX");            CertPath path = factory.generateCertPath(Arrays.asList(chain));            certPathLength = chain.length;            PKIXCertPathValidatorResult result =                (PKIXCertPathValidatorResult)validator.validate(path, params);             return toArray(path, result.getTrustAnchor());        } catch (GeneralSecurityException e) {            throw new ValidatorException                ("PKIX path validation failed: " + e.toString(), e);        }    }     private X509Certificate[] doBuild(X509Certificate[] chain,        Collection<X509Certificate> otherCerts,        PKIXBuilderParameters params) throws CertificateException {         try {            setDate(params);             // setup target constraints            X509CertSelector selector = new X509CertSelector();            selector.setCertificate(chain[0]);            params.setTargetCertConstraints(selector);             // setup CertStores            Collection<X509Certificate> certs =                                        new ArrayList<X509Certificate>();            certs.addAll(Arrays.asList(chain));            if (otherCerts != null) {                certs.addAll(otherCerts);            }            CertStore store = CertStore.getInstance("Collection",                                new CollectionCertStoreParameters(certs));            params.addCertStore(store);             // do the build            CertPathBuilder builder = CertPathBuilder.getInstance("PKIX");            PKIXCertPathBuilderResult result =                (PKIXCertPathBuilderResult)builder.build(params);             return toArray(result.getCertPath(), result.getTrustAnchor());        } catch (GeneralSecurityException e) {            throw new ValidatorException                ("PKIX path building failed: " + e.toString(), e);        }    }}