/**
* DSS - Digital Signature Services
* Copyright (C) 2015 European Commission, provided under the CEF programme
*
* This file is part of the "DSS - Digital Signature Services" project.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package eu.europa.esig.dss.client.crl;
import java.security.cert.X509CRL;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.europa.esig.dss.DSSASN1Utils;
import eu.europa.esig.dss.DSSException;
import eu.europa.esig.dss.DSSUtils;
import eu.europa.esig.dss.client.http.DataLoader;
import eu.europa.esig.dss.client.http.Protocol;
import eu.europa.esig.dss.client.http.commons.CommonsDataLoader;
import eu.europa.esig.dss.utils.Utils;
import eu.europa.esig.dss.x509.CertificateToken;
import eu.europa.esig.dss.x509.crl.CRLSource;
import eu.europa.esig.dss.x509.crl.CRLToken;
import eu.europa.esig.dss.x509.crl.CRLUtils;
import eu.europa.esig.dss.x509.crl.CRLValidity;
/**
* Online CRL repository. This CRL repository implementation will download the CRLs from the given CRL URIs.
* Note that for the HTTP kind of URLs you can provide dedicated data loader. If the data loader is not provided the
* standard load from URI is
* provided. For FTP the standard load from URI is provided. For LDAP kind of URLs an internal implementation using
* apache-ldap-api is provided.
*
*
*/
public class OnlineCRLSource implements CRLSource {
private static final Logger LOG = LoggerFactory.getLogger(OnlineCRLSource.class);
/**
* If the multiple protocols are available to retrieve the revocation data, then that indicated by this variable is
* used first.
*/
private Protocol preferredProtocol;
/**
* The component that allows to retrieve the data using any protocol: HTTP, HTTPS, FTP, LDAP.
*/
private DataLoader dataLoader;
/**
* The default constructor. A {@code CommonsDataLoader is created}.
*/
public OnlineCRLSource() {
dataLoader = new CommonsDataLoader();
LOG.trace("+OnlineCRLSource with the default data loader.");
}
/**
* This constructor allows to set a specific {@code DataLoader}.
*
* @param dataLoader
* the component that allows to retrieve the data using any protocol: HTTP, HTTPS, FTP, LDAP.
*/
public OnlineCRLSource(final DataLoader dataLoader) {
this.dataLoader = dataLoader;
LOG.trace("+OnlineCRLSource with the specific data loader.");
}
/**
* This method allows to set the preferred protocol. This parameter is used used when retrieving the CRL to choose
* the canal.<br/>
* Possible values are: http, ldap, ftp
*
* @param preferredProtocol
* {@code Protocol} that is used first to retrieve the revocation data
*/
public void setPreferredProtocol(final Protocol preferredProtocol) {
this.preferredProtocol = preferredProtocol;
}
/**
* Set the DataLoader to use for querying the CRL server
*
* @param dataLoader
* the component that allows to retrieve the data using any protocol: HTTP, HTTPS, FTP, LDAP.
*/
public void setDataLoader(final DataLoader dataLoader) {
this.dataLoader = dataLoader;
}
@Override
public CRLToken findCrl(final CertificateToken certificateToken) throws DSSException {
if (certificateToken == null) {
return null;
}
final CertificateToken issuerToken = certificateToken.getIssuerToken();
if (issuerToken == null) {
return null;
}
final List<String> crlUrls = DSSASN1Utils.getCrlUrls(certificateToken);
LOG.info("CRL's URL for " + certificateToken.getAbbreviation() + " : " + crlUrls);
if (Utils.isCollectionEmpty(crlUrls)) {
return null;
}
prioritize(crlUrls);
final DataLoader.DataAndUrl dataAndUrl = downloadCrl(crlUrls);
if (dataAndUrl == null) {
return null;
}
final X509CRL crl;
try {
crl = DSSUtils.loadCRL(dataAndUrl.data);
} catch (Exception e) {
LOG.warn("Unable to load the CRL (url:" + dataAndUrl.urlString + ") : " + e.getMessage(), e);
return null;
}
final CRLValidity crlValidity = CRLUtils.isValidCRL(crl, issuerToken);
final CRLToken crlToken = new CRLToken(certificateToken, crlValidity);
crlToken.setSourceURL(dataAndUrl.urlString);
crlToken.setAvailable(true);
return crlToken;
}
/**
* Download a CRL from any location with any protocol.
*
* @param downloadUrls
* the {@code List} of urls to be used to obtain the revocation data through the CRL canal.
* @return {@code X509CRL} or null if it was not possible to download the CRL
*/
private DataLoader.DataAndUrl downloadCrl(final List<String> downloadUrls) {
if (Utils.isCollectionEmpty(downloadUrls)) {
return null;
}
try {
final DataLoader.DataAndUrl dataAndUrl = dataLoader.get(downloadUrls);
return dataAndUrl;
} catch (DSSException e) {
LOG.warn("", e);
}
return null;
}
/**
* if {@code preferredProtocol} is set then the list of urls is prioritize.
* NOTE: This is not standard conformant! However in the major number of cases LDAP is much slower then HTTP!
*
* @param urls
* {@code List} of urls to prioritize
*/
private void prioritize(final List<String> urls) {
if (preferredProtocol != null) {
final List<String> priorityUrls = new ArrayList<String>();
for (final String url : urls) {
if (preferredProtocol.isTheSame(url)) {
priorityUrls.add(url);
}
}
urls.removeAll(priorityUrls);
for (int ii = priorityUrls.size() - 1; ii >= 0; ii--) {
urls.add(0, priorityUrls.get(ii));
}
}
}
}