Newer
Older
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
*
* The Original Code is Java RASP toolkit.
*
* The Initial Developer of the Original Code is Lenio. Portions
* created by Lenio are Copyright (C) 2007 Danish National IT and
* Telecom Agency (http://www.itst.dk). All Rights Reserved.
* Tommy Dejbjerg Pedersen, Lenio
* Martin Bentzen, Accenture
* Jesper Jensen, Avanade
* Ramzi Fadel, Avanade
* Christian Pedersen, Accenture
* Morten Hougesen, Accenture
* Mikkel Hippe Brun, ITST
* Finn Hartmann Jordal, ITST
* Christian Lanng, ITST
package dk.gov.oiosi.security.revocation.ocsp;
import dk.certifikat.ocsp.client.OcspCertificateResponse;
Jacob Lund Mogensen
committed
import dk.certifikat.ocsp.client.OcspCertificateResponse.OcspCertificateStatus;
import dk.certifikat.ocsp.client.OcspClient;
import dk.certifikat.ocsp.client.OcspRegistry;
import dk.certifikat.ocsp.client.OcspSystemException;
import dk.gov.oiosi.common.OutVariable;
import dk.gov.oiosi.configuration.CacheFactory;
import dk.gov.oiosi.configuration.ConfigurationException;
import dk.gov.oiosi.configuration.ConfigurationHandler;
import dk.gov.oiosi.security.CertificateHandlingException;
import dk.gov.oiosi.security.revocation.IRevocationLookup;
import dk.gov.oiosi.security.revocation.RevocationException;
Jacob Lund Mogensen
committed
import dk.gov.oiosi.security.revocation.RevocationResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.ocsp.OCSPException;
import javax.security.auth.x500.X500Principal;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URISyntaxException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
Jacob Lund Mogensen
committed
import java.util.List;
/**
* Class for checking certificate revocation status against an OCSP server.
*/
public class OcspLookup implements IRevocationLookup {
/**
* local log.
*/
private Log log = LogFactory.getLog(OcspLookup.class);
/**
* OCSP configuration.
*/
private OcspConfig configuration;
/**
* Root certificate
*/
private List<X509Certificate> rootcertList = null;
// Set cache time to one hour
private final ICache<X500Principal, RevocationResponse> ocspCache = CacheFactory.getInstance().getOcspLookupCache();
/**
* Gets the configuration of the lookup client.
*
* @return OCSP configuration
*/
public OcspConfig getConfiguration() {
return configuration;
}
/**
* Instantiates OcspLookup and loads the OCES default root certificate.
*
* @param conf Configuration parameters
*/
public OcspLookup(OcspConfig conf) {
configuration = conf;
}
/**
* Creates a new OcspLookup class
*
* @param conf The configuration to use in this lookup
* @param defaultRootCertificate The root certificate
*/
public OcspLookup(OcspConfig conf, X509Certificate defaultRootCertificate) {
configuration = conf;
//rootcert = defaultRootCertificate;
rootcertList = new ArrayList<X509Certificate>();
rootcertList.add(defaultRootCertificate);
}
/**
* Creates a new OcspLookup class
*
* @param conf The configuration to use in this lookup
* @param defaultRootCertificateList The root certificate
*/
public OcspLookup(OcspConfig conf, ArrayList<X509Certificate> defaultRootCertificateList) {
configuration = conf;
rootcertList = defaultRootCertificateList;
}
/**
* Default constructor. Attempts to load configuration from configuration
* file.
*
* @throws ConfigurationException On error...
*/
public OcspLookup() throws ConfigurationException {
try {
configuration = ConfigurationHandler.getInstance().getOcspConfig();
} catch (URISyntaxException e) {
throw new ConfigurationException(e.getMessage());
}
}
/**
* Checks a certificate status on a ocsp server.
*
* @param certificate The certificate to check
* @return The RevocationResponse object that contains the result
* @throws RevocationException
*/
public RevocationResponse checkCertificate(X509Certificate certificate) throws RevocationException {
OutVariable<RevocationResponse> value = new OutVariable<RevocationResponse>();
RevocationResponse revocationResponse;
if (this.ocspCache.tryGetValue(certificate.getSubjectX500Principal(), value)) {
// response already in cache.
// Check if the response still is valid
Date now = new Date();
revocationResponse = value.getVariable();
if(revocationResponse.getNextUpdate().before(now))
{
// the cached value is to old
// remove it from the cache
this.ocspCache.remove(certificate.getSubjectX500Principal());
// get new value
revocationResponse = this.checkCertificateOnline(certificate);
if(revocationResponse != null)
{
// put revocationResponse in cache
ocspCache.set(certificate.getSubjectX500Principal(), revocationResponse);
}
}
else
{
// respons is not in cache
revocationResponse = this.checkCertificateOnline(certificate);
if(revocationResponse != null)
{
// put revocationResponse in cache
ocspCache.set(certificate.getSubjectX500Principal(), revocationResponse);
}
}
return revocationResponse;
}
public RevocationResponse checkCertificateOnline(X509Certificate certificate) throws RevocationException {
// The response was not in the cache - we must validete the certificate our self
RevocationResponse response = null;
log.debug("Setting up OCSP client:");
rootcertList = configuration.getDefaultOcesRootCertificateCollectionFromStore() ;
// no root certificate is defined
// we can't proceed
throw new RevocationException("No root certificate has been defined.");
}
if (configuration.getServerUrl() == null) {
// Use OCSP server specified in certificate
serverUrl = GetServerUriFromCertificate(certificate);
} else {
// Use OCSP server specified in configuration
// only valid when testing
// the url for the ocsp server, variate.
// It is based on the certificate version, and whether it is a live or test certificate
// oces1 test :
// oces1 live :
// oces2 test : http://ocsp.systemtest8.trust2408.com/responder
// oces2 live :
//serverUrl = configuration.getServerUrl();
throw new RevocationException("No root certificate has been defined.");
}
} catch (RevocationException e) {
throw e;
} catch (CertificateHandlingException e) {
log.error("Error handling the certificate" + e.getMessage());
throw new RevocationException(e);
} catch (Exception e) {
log.error("Error initailizing validator : " + e.getMessage(), e);
throw new RevocationException(e);
}
OcspCertificateResponse ocspCertificateResponse = null;
OcspClient client = null;
boolean validationNotFinish = true;
int rootCertificateIndex = 0;
while (validationNotFinish && rootCertificateIndex < rootcertList.size()) {
try {
log.debug("start validation certificate");
client = OcspRegistry.getOcspClient();
client.setCaCert(rootcertList.get(rootCertificateIndex));
// Try validation if this is the correct root certificate
log.debug("Getting certificate status for:" + serNos[0]);
ocspCertificateResponse = client.getCertificateStatus(serNos);
log.trace("Validating certificate...");
client.validateCerts(ocspCertificateResponse);
OcspCertificateStatus status = ocspCertificateResponse.getCertificateStatus(serNos[0]);
log.debug("Status of cert " + certificate.getSubjectDN() + " : [" + status + "]");
response = new RevocationResponse();
response.setIsValid(true);
response.setNextUpdate(ocspCertificateResponse.getNextUpdate());
// all okay, do not check other root certificate
validationNotFinish = false;
} else if ("Revoked".equals(status.toString())) {
log.debug("Status of cert " + certificate.getSubjectDN() + " : [" + status + "]");
response = new RevocationResponse();
response.setIsValid(false);
response.setNextUpdate(ocspCertificateResponse.getNextUpdate());
}
else {
// The used root certificate is not valid, check the next one
// The rootCertificateIndex is increased at the buttom of this try-catch
log.warn("Certificate validation failed " + serNos[0] + " : [" + status + "]");
}
// The certificateResponse is invalid
log.error("Response returned an error: " + ocspCertificateResponse.getError());
throw new RevocationException("Response returned an error: " + ocspCertificateResponse.getError());
}
log.debug("Error validating certificate : " + e.getMessage(), e);
//throw new RevocationException(e);
log.error("Error validating certificate : " + e.getMessage(), e);
throw new RevocationException(e);
} catch (Exception e) {
log.error("Error validator certificate : " + e.getMessage(), e);
throw new RevocationException(e);
}
rootCertificateIndex++;
}
log.debug("Validating X509 certificate finished.");
// if response is different from null, a validation was succesfull
if(response == null)
{
throw new RevocationException("Valid revocation response was not received.");
}
}
public String GetServerUriFromCertificate(X509Certificate certificate) throws RevocationException {
try {
byte[] ext = certificate.getExtensionValue(X509Extensions.AuthorityInfoAccess.getId());
ASN1InputStream aIn = new ASN1InputStream(ext);
aIn = new ASN1InputStream(((ASN1OctetString) aIn.readObject()).getOctets());
ASN1Encodable object = (ASN1Encodable) aIn.readObject();
AuthorityInformationAccess auth = AuthorityInformationAccess.getInstance(object);
AccessDescription[] acc = auth.getAccessDescriptions();
return acc[0].getAccessLocation().toString().substring(3);
} catch (IOException e) {
throw new RevocationException(e);
}
}
}