Skip to content
CertificateUtil.java 8.05 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.LinkedList;
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 Log log = LogFactory.getLog(CertificateUtil.class);

	public CertificateUtil() {
	}

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

		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 LinkedList<URL>();

		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 LinkedList<URL>();

		// 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 excpetion is preferred.
				 */
			}
		}

		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 {
		try {
			ASN1InputStream aIn = new ASN1InputStream(ext);
			ASN1OctetString octs = (ASN1OctetString) aIn.readObject();

			aIn = new ASN1InputStream(octs.getOctets());
			return aIn.readObject();
		} catch (Exception e) {
			throw new Exception("exception processing extension " + oid, e);
		}
	}

	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<X509Certificate>();
		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 = certificateUtil.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;
	}