Skip to content
CertificateUtil.java 9.85 KiB
Newer Older
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
package dk.gov.oiosi.security.oces;

import dk.gov.oiosi.common.OutVariable;
import dk.gov.oiosi.common.cache.ICache;
import dk.gov.oiosi.configuration.CacheFactory;
import dk.gov.oiosi.security.CertificateIssuerSubject;
import dk.gov.oiosi.security.CertificateSubject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
dlk-truelink's avatar
dlk-truelink committed
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.jce.provider.AnnotatedException;
import org.bouncycastle.x509.extension.X509ExtensionUtil;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.List;

Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
/**
 * @since OIORASP 1.3.0
Jacob Lund Mogensen's avatar
Jacob Lund Mogensen committed
 */
public class CertificateUtil {
    private static final Log log = LogFactory.getLog(CertificateUtil.class);

    public CertificateUtil() {
    }

    public List<URL> getOcspUrls(X509Certificate cert) throws Exception {
        List<URL> urls = new ArrayList<>();

        AuthorityInformationAccess authInfoAccess = null;
        try {
            ASN1Primitive auth_info_acc = getExtensionValue(cert, Extension.authorityInfoAccess.getId());
            if (auth_info_acc != null) {
                authInfoAccess = AuthorityInformationAccess.getInstance(auth_info_acc);
            }
        } catch (AnnotatedException ae) {
            // throw new Exception();
            // ErrorBundle msg = new
            // ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlAuthInfoAccError");
            // addError(msg,index);
        }

        if (authInfoAccess != null) {
            AccessDescription[] ads = authInfoAccess.getAccessDescriptions();
            for (AccessDescription ad : ads) {
                if (ad.getAccessMethod().equals(AccessDescription.id_ad_ocsp)) {
                    GeneralName name = ad.getAccessLocation();
                    if (name.getTagNo() == GeneralName.uniformResourceIdentifier) {
                        String url = ((DERIA5String) name.getName()).getString();
                        urls.add(new URL(url));
                    }
                }
            }
        }

        return urls;
    }

    public List<URL> getIssuerUrls(X509Certificate cert) throws Exception {
        List<URL> urls = new ArrayList<>();

        AuthorityInformationAccess authInfoAccess = null;
        try {
            ASN1Primitive auth_info_acc = getExtensionValue(cert, Extension.authorityInfoAccess.getId());
            if (auth_info_acc != null) {
                authInfoAccess = AuthorityInformationAccess.getInstance(auth_info_acc);
            }
        } catch (AnnotatedException ae) {
            // throw new Exception();
            // ErrorBundle msg = new
            // ErrorBundle(RESOURCE_NAME,"CertPathReviewer.crlAuthInfoAccError");
            // addError(msg,index);
        }

        if (authInfoAccess != null) {
            AccessDescription[] ads = authInfoAccess.getAccessDescriptions();
            for (AccessDescription ad : ads) {
                if (ad.getAccessMethod().equals(AccessDescription.id_ad_caIssuers)) {
                    GeneralName name = ad.getAccessLocation();
                    if (name.getTagNo() == GeneralName.uniformResourceIdentifier) {
                        String url = ((DERIA5String) name.getName()).getString();
                        urls.add(new URL(url));
                    }
                }
            }
        }

        return urls;
    }

    public List<URL> getCrlURLs(X509Certificate cert) {
        List<URL> urls = new ArrayList<>();

        // Retrieves the raw ASN1 data of the CRL Dist Points X509 extension
        byte[] cdp = cert.getExtensionValue(X509Extension.cRLDistributionPoints.getId());
        if (cdp != null) {
            try {
                // Wraps the raw data in a container class
                CRLDistPoint crldp = CRLDistPoint.getInstance(X509ExtensionUtil.fromExtensionValue(cdp));

                DistributionPoint[] distPoints = crldp.getDistributionPoints();

                for (DistributionPoint dp : distPoints) {
                    // Only use the "General name" data in the distribution point entry.
                    GeneralNames gns = (GeneralNames) dp.getDistributionPoint().getName();
                    DERIA5String uri;
                    for (GeneralName name : gns.getNames()) {
                        // Only retrieve URLs
                        if (name.getTagNo() == GeneralName.uniformResourceIdentifier) {
                            uri = (DERIA5String) name.getName();
                            urls.add(new URL(uri.getString()));
                        }
                    }
                }
            } catch (IOException e) {
                // Could not retrieve the CRLDistPoint object. Just return empty url list.
                /*
                     * Hmm... So there is no possibility to see what happened when
                     * certificate is checked against CRL - such error is just hidden.
                     *
                     * TODO DLK: Think at least about logging such exception, throwing exception is preferred.
                     */
                log.error("Unable to find CRL URL for cert=[" + cert.getSubjectDN().getName() + "]: " + e.getMessage(), e);
            }
        }

        return urls;
    }

    /**
     * Get the url to the issuing certificate.
     *
     * @param cert A Oces2 certificate
     * @return The url for the issuing certificate, if exist.
     */
    public URL getIssuerUrl(X509Certificate cert) {
        try {
            List<URL> list = this.getIssuerUrls(cert);
            if (list.size() == 1) {
                // the url must be located on the 1. index
                return list.get(0);
            }
        } catch (Exception e) {
            log.warn("Error fetching certificate: " + e.getMessage(), e);
        }

        return null;
    }

    /**
     * Extract the value of the given extension, if it exists.
     *
     * @param ext The extension object.
     * @param oid The object identifier to obtain.
     * @return ASN1Primitive.
     * @throws Exception if the extension cannot be read.
     */
    protected static ASN1Primitive getExtensionValue(java.security.cert.X509Extension ext, String oid) throws Exception {
        byte[] bytes = ext.getExtensionValue(oid);
        if (bytes == null) {
            return null;
        }

        return getObject(oid, bytes);
    }

    private static ASN1Primitive getObject(String oid, byte[] ext) throws Exception {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
        ASN1InputStream aIn = null;
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            aIn = new ASN1InputStream(ext);
            ASN1OctetString octetString = (ASN1OctetString) aIn.readObject();
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            aIn = new ASN1InputStream(octetString.getOctets());
            return aIn.readObject();
        } catch (Exception e) {
Peter Sone Koldkjær's avatar
Peter Sone Koldkjær committed
            throw new Exception("exception processing extension=" + oid + ": " + e.getMessage(), e);
        } finally {
            if (aIn != null) {
                aIn.close();
            }
        }
    }

    public X509Certificate downloadIssuerCert(X509Certificate certificate) {
        ICache<CertificateSubject, X509Certificate> cache = CacheFactory.getInstance().getCertificateCache();
        CertificateIssuerSubject certificateIssuerSubject = new CertificateIssuerSubject(certificate);

        X509Certificate downloadedX509Certificate = null;
        OutVariable<X509Certificate> issuerCertificateOutVariable = new OutVariable<>();
        if (cache.tryGetValue(certificateIssuerSubject, issuerCertificateOutVariable)) {
            // certificate already retrieved - using the cached version
            downloadedX509Certificate = issuerCertificateOutVariable.getVariable();
        } else {
            // download the certificate, and add it to the cache.
//            CertificateUtil certificateUtil = new CertificateUtil();
            URL issuerCertificateURL = getIssuerUrl(certificate);

            if (issuerCertificateURL != null) {
                // url is not null - try to download the certificate
                try {
                    inputStream = issuerCertificateURL.openStream();
                    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
                    downloadedX509Certificate = (X509Certificate) certificateFactory.generateCertificate(inputStream);
                    cache.add(certificateIssuerSubject, downloadedX509Certificate);
                } catch (IOException e) {
                    log.warn("Error reading from url " + issuerCertificateURL + " input stream: " + e.getMessage(), e);
                } catch (CertificateException e) {
                    log.warn("Error creating certificate from url " + issuerCertificateURL + " input stream: " + e.getMessage(), e);
                } catch (Exception e) {
                    log.warn("Unexpected error during downloading certificate from url " + issuerCertificateURL + " input stream: " + e.getMessage(), e);
                } finally {
                    // remember to close the input stream.
                    if (inputStream != null) {
                        try {
                            inputStream.close();
                        } catch (IOException e) {
                            log.warn("Error closing InputStream: " + e.getMessage(), e);
                        }

                    }
                }
            }
        }

        return downloadedX509Certificate;
    }