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; 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.ArrayList; import java.util.List; /** * @since OIORASP 1.3.0 */ public class CertificateUtil { private static final Log log = LogFactory.getLog(CertificateUtil.class); public CertificateUtil() { } public List getOcspUrls(X509Certificate cert) throws Exception { List 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 getIssuerUrls(X509Certificate cert) throws Exception { List 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 getCrlURLs(X509Certificate cert) { List 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 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 { ASN1InputStream aIn = null; try { aIn = new ASN1InputStream(ext); ASN1OctetString octetString = (ASN1OctetString) aIn.readObject(); aIn = new ASN1InputStream(octetString.getOctets()); return aIn.readObject(); } catch (Exception e) { throw new Exception("exception processing extension=" + oid + ": " + e.getMessage(), e); } finally { if (aIn != null) { aIn.close(); } } } public X509Certificate downloadIssuerCert(X509Certificate certificate) { ICache cache = CacheFactory.getInstance().getCertificateCache(); CertificateIssuerSubject certificateIssuerSubject = new CertificateIssuerSubject(certificate); X509Certificate downloadedX509Certificate = null; OutVariable 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. InputStream inputStream = null; // 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; } }