org.xdi.oxauth.cert.validation.PathCertificateVerifier.java Source code

Java tutorial

Introduction

Here is the source code for org.xdi.oxauth.cert.validation.PathCertificateVerifier.java

Source

/*
 * oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text.
 *
 * Copyright (c) 2014, Gluu
 */

package org.xdi.oxauth.cert.validation;

import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Principal;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.CertPath;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertPathBuilderException;
import java.security.cert.CertPathValidator;
import java.security.cert.CertStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathBuilderResult;
import java.security.cert.PKIXCertPathValidatorResult;
import java.security.cert.TrustAnchor;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xdi.oxauth.cert.validation.model.ValidationStatus;
import org.xdi.oxauth.cert.validation.model.ValidationStatus.CertificateValidity;
import org.xdi.oxauth.cert.validation.model.ValidationStatus.ValidatorSourceType;
import org.xdi.oxauth.model.util.SecurityProviderUtility;

/**
 * Chain certificate verifier
 * 
 * @author Yuriy Movchan
 * @version March 11, 2016
 */
public class PathCertificateVerifier implements CertificateVerifier {

    private static final Logger log = LoggerFactory.getLogger(PathCertificateVerifier.class);

    private boolean verifySelfSignedCertificate;

    public PathCertificateVerifier(boolean verifySelfSignedCert) {
        SecurityProviderUtility.installBCProvider(true);

        this.verifySelfSignedCertificate = verifySelfSignedCert;
    }

    @Override
    public ValidationStatus validate(X509Certificate certificate, List<X509Certificate> issuers,
            Date validationDate) {
        X509Certificate issuer = issuers.get(0);
        ValidationStatus status = new ValidationStatus(certificate, issuer, validationDate,
                ValidatorSourceType.CHAIN, CertificateValidity.UNKNOWN);

        try {
            ArrayList<X509Certificate> chains = new ArrayList<X509Certificate>();
            chains.add(certificate);
            chains.addAll(issuers);

            Principal subjectX500Principal = certificate.getSubjectX500Principal();

            PKIXCertPathBuilderResult certPathResult = verifyCertificate(certificate, chains);
            if (certPathResult == null) {
                log.warn("Chain status is not valid for '" + subjectX500Principal + "'");
                status.setValidity(CertificateValidity.INVALID);
                return status;
            }

            log.debug("Chain status is valid for '" + subjectX500Principal + "'");
            status.setValidity(CertificateValidity.VALID);
        } catch (Exception ex) {
            log.error("OCSP exception: ", ex);
        }

        return status;
    }

    public PKIXCertPathBuilderResult verifyCertificate(X509Certificate certificate,
            List<X509Certificate> additionalCerts) {
        try {
            // Check for self-signed certificate
            if (!verifySelfSignedCertificate && isSelfSigned(certificate)) {
                log.error("The certificate is self-signed!");

                return null;
            }

            // Prepare a set of trusted root CA certificates and a set of
            // intermediate certificates
            Set<X509Certificate> trustedRootCerts = new HashSet<X509Certificate>();
            Set<X509Certificate> intermediateCerts = new HashSet<X509Certificate>();
            for (X509Certificate additionalCert : additionalCerts) {
                if (isSelfSigned(additionalCert)) {
                    trustedRootCerts.add(additionalCert);
                } else {
                    intermediateCerts.add(additionalCert);
                }
            }

            // Attempt to build the certification chain and verify it
            PKIXCertPathBuilderResult certPathBuilderResult = verifyCertificate(certificate, trustedRootCerts,
                    intermediateCerts);

            // Check that first certificate is an EE certificate
            CertPath certPath = certPathBuilderResult.getCertPath();
            List<? extends Certificate> certList = certPath.getCertificates();
            X509Certificate cert = (X509Certificate) certList.get(0);
            if (cert.getBasicConstraints() != -1) {
                log.error("Target certificate is not an EE certificate!");

                return null;
            }

            // The chain is verified. Return it as a result
            return certPathBuilderResult;
        } catch (CertPathBuilderException ex) {
            log.error("Failed to build certificate path", ex);
        } catch (GeneralSecurityException ex) {
            log.error("Failed to build certificate path", ex);
        }

        return null;
    }

    public static boolean isSelfSigned(X509Certificate certificate)
            throws CertificateException, NoSuchAlgorithmException, NoSuchProviderException {
        try {
            // Try to verify certificate signature with its own public key
            PublicKey key = certificate.getPublicKey();
            certificate.verify(key);

            return true;
        } catch (SignatureException ex) {
            // Not self-signed
            return false;
        } catch (InvalidKeyException ex) {
            // Not self-signed
            return false;
        }
    }

    /**
     * Attempts to build a certification chain for given certificate to verify
     * it. Relies on a set of root CA certificates (trust anchors) and a set of
     * intermediate certificates (to be used as part of the chain).
     */
    private PKIXCertPathBuilderResult verifyCertificate(X509Certificate certificate,
            Set<X509Certificate> trustedRootCerts, Set<X509Certificate> intermediateCerts)
            throws GeneralSecurityException {

        // Create the selector that specifies the starting certificate
        X509CertSelector selector = new X509CertSelector();
        selector.setBasicConstraints(-2);
        selector.setCertificate(certificate);

        // Create the trust anchors (set of root CA certificates)
        Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();
        for (X509Certificate trustedRootCert : trustedRootCerts) {
            trustAnchors.add(new TrustAnchor(trustedRootCert, null));
        }

        // Configure the PKIX certificate builder algorithm parameters
        PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(trustAnchors, selector);

        // Turn off default revocation-checking mechanism
        pkixParams.setRevocationEnabled(false);

        // Specify a list of intermediate certificates
        CertStore intermediateCertStore = CertStore.getInstance("Collection",
                new CollectionCertStoreParameters(intermediateCerts));
        pkixParams.addCertStore(intermediateCertStore);

        // Build and verify the certification chain
        CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME);
        PKIXCertPathBuilderResult certPathBuilderResult = (PKIXCertPathBuilderResult) builder.build(pkixParams);

        // Additional check to Verify cert path
        CertPathValidator certPathValidator = CertPathValidator.getInstance("PKIX",
                BouncyCastleProvider.PROVIDER_NAME);
        PKIXCertPathValidatorResult certPathValidationResult = (PKIXCertPathValidatorResult) certPathValidator
                .validate(certPathBuilderResult.getCertPath(), pkixParams);

        return certPathBuilderResult;
    }

    @Override
    public void destroy() {
    }

}