/*
* Copyright (c) 2011-2012 ICM Uniwersytet Warszawski All rights reserved.
* See LICENCE.txt file for licensing information.
*/
package eu.emi.security.authn.x509.helpers.crl;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.security.InvalidAlgorithmParameterException;
import java.security.cert.CRLException;
import java.security.cert.CRLSelector;
import java.security.cert.X509CRL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import javax.security.auth.x500.X500Principal;
import eu.emi.security.authn.x509.StoreUpdateListener.Severity;
import eu.emi.security.authn.x509.helpers.CachedElement;
import eu.emi.security.authn.x509.helpers.ObserversHandler;
import eu.emi.security.authn.x509.helpers.trust.OpensslTruststoreHelper;
import eu.emi.security.authn.x509.impl.CRLParameters;
import eu.emi.security.authn.x509.impl.X500NameUtils;
/**
* Handles an Openssl-like CRL store. CRLs are loaded on demand from disk and cached in memory,
* for no longer then updateInterval parameter.
* <p>
* This class is thread safe.
* </p>
*
* @author K. Benedyczak
*/
public class LazyOpensslCRLStoreSpi extends AbstractCRLStoreSPI
{
private static final String SUFFIX = "\\.r[0-9]+";
//constant state
private final File directory;
private final boolean openssl1Mode;
//variable state
private Map<String, CachedElement<List<X509CRL>>> cachedCRLsByHash;
/**
* Creates a new CRL store.
* @param path path
* @param crlUpdateInterval crl update interval
* @param observers observers handler
* @param openssl1Mode openssl 1 mode
* @throws InvalidAlgorithmParameterException invalid algorithm parameter exception
*/
public LazyOpensslCRLStoreSpi(String path, long crlUpdateInterval, ObserversHandler observers,
boolean openssl1Mode) throws InvalidAlgorithmParameterException
{
super(new CRLParameters(Collections.singletonList(path),
crlUpdateInterval, 0, null), observers);
this.directory = new File(path);
this.openssl1Mode = openssl1Mode;
cachedCRLsByHash = new WeakHashMap<String, CachedElement<List<X509CRL>>>();
}
protected X509CRL loadCRL(File file) throws IOException, CRLException, URISyntaxException
{
InputStream is = new BufferedInputStream(new FileInputStream(file));
try
{
X509CRL ret = (X509CRL)factory.generateCRL(is);
if (ret == null)
throw new CRLException("Unknown problem when parsing/loading the CRL");
return ret;
} finally
{
is.close();
}
}
@Override
public synchronized void setUpdateInterval(long newInterval)
{
this.updateInterval = newInterval;
}
public synchronized long getUpdateInterval()
{
return updateInterval;
}
@Override
public void dispose()
{
}
protected X509CRL reloadCRL(File location)
{
X509CRL crl;
try
{
crl = loadCRL(location);
notifyObservers(location.getAbsolutePath(), Severity.NOTIFICATION, null);
} catch (Exception e)
{
notifyObservers(location.getAbsolutePath(), Severity.ERROR, e);
return null;
}
return crl;
}
private Collection<X509CRL> filterByIssuer(X500Principal issuer, Collection<X509CRL> all)
{
List<X509CRL> ret = new ArrayList<X509CRL>(all.size());
for (X509CRL crl: all)
if (X500NameUtils.rfc3280Equal(issuer, crl.getIssuerX500Principal()))
ret.add(crl);
return ret;
}
@Override
protected synchronized Collection<X509CRL> getCRLForIssuer(X500Principal issuer)
{
String issuerHash = OpensslTruststoreHelper.getOpenSSLCAHash(issuer, openssl1Mode);
CachedElement<List<X509CRL>> cached = cachedCRLsByHash.get(issuerHash);
if (cached != null && !cached.isExpired(updateInterval))
{
return filterByIssuer(issuer, cached.getElement());
}
Collection<File> crls = OpensslTruststoreHelper.getFilesWithRegexp(issuerHash+SUFFIX, directory);
List<X509CRL> ret = new ArrayList<X509CRL>(crls.size());
for (File location: crls)
{
X509CRL crl = reloadCRL(location);
if (crl != null)
ret.add(crl);
}
cachedCRLsByHash.put(issuerHash, new CachedElement<List<X509CRL>>(ret));
return filterByIssuer(issuer, ret);
}
@Override
protected synchronized Collection<X509CRL> getCRLWithMatcher(CRLSelector selectorRaw)
{
Collection<File> crls = OpensslTruststoreHelper.getFilesWithRegexp(".*" + SUFFIX, directory);
List<X509CRL> ret = new ArrayList<X509CRL>();
for (File location: crls)
{
X509CRL crl = reloadCRL(location);
if (crl != null && selectorRaw.match(crl))
ret.add(crl);
}
return ret;
}
}