/* * SD-DSS - Digital Signature Services * * Copyright (C) 2015 ARHS SpikeSeed S.A. (rue Nicolas Bové 2B, L-1253 Luxembourg) http://www.arhs-spikeseed.com * * Developed by: 2015 ARHS SpikeSeed S.A. (rue Nicolas Bové 2B, L-1253 Luxembourg) http://www.arhs-spikeseed.com * * This file is part of the "https://github.com/arhs/sd-dss" project. * * "DSS - Digital Signature Services" 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. * * DSS 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 * "SD-DSS - Digital Signature Services". If not, see <http://www.gnu.org/licenses/>. */ package eu.europa.ec.markt.dss.validation102853.crl; import java.security.cert.X509CRL; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import eu.europa.ec.markt.dss.DSSUtils; import eu.europa.ec.markt.dss.exception.DSSException; import eu.europa.ec.markt.dss.exception.DSSNullException; import eu.europa.ec.markt.dss.validation102853.CertificateToken; import eu.europa.ec.markt.dss.validation102853.RevocationToken; import eu.europa.ec.markt.dss.validation102853.loader.DataLoader; /** * To speed up the retrieval of the CRL(s) the {@code InMemoryCacheOnlineCRLSource} class allows to define the freshness of CRL. This latter is the period of time during which the * CRL can be reused. The freshness is defined as the difference between the thisUpdate field of the CRL and the current time. During the retrieval process the nextUpdate field of * the CRL is also checked. If its value is before the current time then the refresh is forced. * Note that to be efficient this class must relay on a {@code DataLoader} allowing the caching mechanism as {@link eu.europa.ec.markt.dss.validation102853.https.FileCacheDataLoader}. * * @author Robert Bielecki * @version $Revision$ - $Date$ */ public class InMemoryCacheOnlineCRLSource extends OnlineCRLSource { private static final Logger LOG = LoggerFactory.getLogger(InMemoryCacheOnlineCRLSource.class); /** * This field contains the freshness time unit to be used when dealing with the revocation freshness. The default value is {@code TimeUnit.DAYS} */ private TimeUnit freshnessTimeUnit = TimeUnit.DAYS; /** * This field contains the freshness value of the revocation data expressed in {@code freshnessTimeUnit}. The default value is {@code 1} */ private long freshnessValue = 1; /** * This {@code Map} contains for each CRL (represented by its URL) the pair of: thisUpdate and nextUpdate dates */ private Map<String, DatePair> crlFreshness = new HashMap<String, DatePair>(); /** * This constructor allows to set a specific {@code DataLoader}. * * @param dataLoader the component that allows to handle the caching mechanism as {@link eu.europa.ec.markt.dss.validation102853.https.FileCacheDataLoader} * @throws DSSNullException in the case of {@code null} parameter value */ public InMemoryCacheOnlineCRLSource(final DataLoader dataLoader) throws DSSNullException { super(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 = getCrlUrl(certificateToken, null); if (DSSUtils.isEmpty(crlUrls)) { return null; } for (final String crlUrl : crlUrls) { final boolean refresh = shouldRefresh(crlUrl); final byte[] crlData = dataLoader.get(crlUrl, refresh); if (crlData == null) { continue; } final X509CRL x509Crl = buildX509Crl(crlData); if (x509Crl == null) { return null; } final CRLValidity crlValidity = isValidCRL(x509Crl, issuerToken, crlUrls); final CRLToken crlToken = new CRLToken(certificateToken, crlValidity); crlToken.setSourceURL(crlUrl); if (refresh) { crlFreshness.put(crlUrl, new DatePair(crlToken.getThisUpdate(), crlToken.getNextUpdate())); } return crlToken; } return null; } /** * This method indicates if the cached CRL (if any) related to the given {@code crlUrl} should be refreshed or not. The nextUpdate of the CRL and the {@code freshnessValue} * are * checked. * * @param crlUrl the {@code String} representation of the CRL's URL * @return {@code true} if the cached CRL should be refreshed, {@code false} otherwise */ private boolean shouldRefresh(final String crlUrl) { final DatePair crlFreshnessInfo = crlFreshness.get(crlUrl); if (crlFreshnessInfo != null) { return !isFresh(crlFreshnessInfo.nextUpdate, crlFreshnessInfo.thisUpdate); } return true; } @Override public boolean isFresh(final RevocationToken revocationToken) { final Date nextUpdate = revocationToken.getNextUpdate(); final Date issuingTime = revocationToken.getIssuingTime(); return isFresh(nextUpdate, issuingTime); } private boolean isFresh(final Date nextUpdate, final Date issuingTime) { final Date now = new Date(); if (nextUpdate.after(now)) { final long freshness = DSSUtils.getDateDiff(now, issuingTime, freshnessTimeUnit); if (freshness <= freshnessValue) { return true; } } return false; } /** * The class representing the thisUpdate and nextUpdate fields of a CRL. */ static class DatePair { public final Date thisUpdate; public final Date nextUpdate; /** * Constructor for a {@code DatePair}. * * @param thisUpdate {@code Date} of the this update * @param nextUpdate {@code Date} of the next update */ public DatePair(final Date thisUpdate, final Date nextUpdate) { this.thisUpdate = thisUpdate; this.nextUpdate = nextUpdate; } } }