/* * Copyright 2006-2017 ICEsoft Technologies Canada Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package org.icepdf.core.pobjects.acroform.signature.certificates; import org.bouncycastle.jce.exception.ExtCertPathValidatorException; import org.icepdf.core.pobjects.acroform.signature.exceptions.CertificateVerificationException; import org.icepdf.core.pobjects.acroform.signature.exceptions.RevocationVerificationException; import org.icepdf.core.pobjects.acroform.signature.exceptions.SelfSignedVerificationException; import java.security.*; import java.security.cert.*; import java.util.Collection; import java.util.HashSet; import java.util.Set; /** * Class for building a certification chain for given certificate and verifying * it. Relies on a set of root CA certificates and intermediate certificates * that will be used for building the certification chain. The verification * process assumes that all self-signed certificates in the set are trusted * root CA certificates and all other certificates in the set are intermediate * certificates. * * @author Svetlin Nakov */ public class CertificateVerifier { /** * Attempts to build a certification chain for given certificate and to verify * it. Relies on a set of root CA certificates and intermediate certificates * that will be used for building the certification chain. The verification * process assumes that all self-signed certificates in the set are trusted * root CA certificates and all other certificates in the set are intermediate * certificates. * * @param cert - certificate for validation * @param additionalCerts - set of trusted root CA certificates that will be * used as "trust anchors" and intermediate CA certificates that will be * used as part of the certification chain. All self-signed certificates * are considered to be trusted root CA certificates. All the rest are * considered to be intermediate CA certificates. * @return the certification chain (if verification is successful) * @throws CertificateVerificationException - if the certification is not * successful (e.g. certification path cannot be built or some * certificate in the chain is expired or CRL checks are failed) */ public static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, Collection<X509Certificate> additionalCerts) throws CertificateVerificationException, CertificateExpiredException, SelfSignedVerificationException, RevocationVerificationException { try { // Check for self-signed certificate if (isSelfSigned(cert)) { throw new SelfSignedVerificationException("The certificate is self-signed."); } // 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 verifiedCertChain = verifyCertificate(cert, trustedRootCerts, intermediateCerts); // Check whether the certificate is revoked by the CRL // given in its CRL distribution point extension CRLVerifier.verifyCertificateCRLs(cert); // The chain is built and verified. Return it as a result return verifiedCertChain; } catch (CertPathBuilderException certPathEx) { if (certPathEx.getCause() instanceof ExtCertPathValidatorException) { if (certPathEx.getCause().getCause() instanceof CertificateExpiredException) { throw (CertificateExpiredException) certPathEx.getCause().getCause(); } } throw new CertificateVerificationException( "Error building certification path: " + cert.getSubjectX500Principal(), certPathEx); } catch (CertificateVerificationException cvex) { throw cvex; } catch (RevocationVerificationException e) { throw e; } catch (Exception ex) { throw new CertificateVerificationException( "Error verifying the certificate: " + cert.getSubjectX500Principal(), ex); } } /** * Checks whether given X.509 certificate is self-signed. */ public static boolean isSelfSigned(X509Certificate cert) throws CertificateException, NoSuchAlgorithmException, NoSuchProviderException { try { // Try to verify certificate signature with its own public key PublicKey key = cert.getPublicKey(); cert.verify(key); return true; } catch (SignatureException sigEx) { // Invalid signature, not self-signed return false; } catch (InvalidKeyException keyEx) { // Invalid key, not self-signed return false; } } /** * Attempts to build a certification chain for given certificate and 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). * * @param cert - certificate for validation * @param trustedRootCerts - set of trusted root CA certificates * @param intermediateCerts - set of intermediate certificates * @return the certification chain (if verification is successful) * @throws GeneralSecurityException - if the verification is not successful * (e.g. certification path cannot be built or some certificate in the * chain is expired) */ private static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, Set<X509Certificate> trustedRootCerts, Set<X509Certificate> intermediateCerts) throws GeneralSecurityException { // Create the selector that specifies the starting certificate X509CertSelector selector = new X509CertSelector(); selector.setCertificate(cert); // 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); // Disable CRL checks (this is done manually as additional step) pkixParams.setRevocationEnabled(false); // Specify a list of intermediate certificates CertStore intermediateCertStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(intermediateCerts), "BC"); pkixParams.addCertStore(intermediateCertStore); // Build and verify the certification chain CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC"); return (PKIXCertPathBuilderResult) builder.build(pkixParams); } }