/* * DSS - Digital Signature Services * * Copyright (C) 2013 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel * * Developed by: 2013 ARHS Developments S.A. (rue Nicolas Bové 2B, L-1253 Luxembourg) http://www.arhs-developments.com * * This file is part of the "DSS - Digital Signature Services" 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 * "DSS - Digital Signature Services". If not, see <http://www.gnu.org/licenses/>. */ package eu.europa.ec.markt.dss.validation102853.tsp; import java.math.BigInteger; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.tsp.TimeStampRequest; import org.bouncycastle.tsp.TimeStampRequestGenerator; import org.bouncycastle.tsp.TimeStampResponse; import org.bouncycastle.tsp.TimeStampToken; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import eu.europa.ec.markt.dss.DSSUtils; import eu.europa.ec.markt.dss.DigestAlgorithm; import eu.europa.ec.markt.dss.exception.DSSException; import eu.europa.ec.markt.dss.validation102853.https.CommonDataLoader; import eu.europa.ec.markt.dss.validation102853.loader.DataLoader; /** * Class encompassing a RFC 3161 TSA, accessed through HTTP(S) to a given URI * * @version $Revision$ - $Date$ */ public class OnlineTSPSource implements TSPSource { private static final Logger LOG = LoggerFactory.getLogger(OnlineTSPSource.class); public static final String CONTENT_TYPE = "application/timestamp-query"; public static final String ACCEPT = "application/timestamp-reply"; /** * A {@code String} representation of a URL of the timestamp server. */ protected String tspServerUrl; /** * The reqPolicy field, if included, indicates the TSA policy under which the TimeStampToken SHOULD be provided. */ protected ASN1ObjectIdentifier reqPolicyOid; /** * If the certReq field is present and set to true, the TSA's public key certificate that is referenced by the ESSCertID identifier inside a * SigningCertificate attribute in the response MUST be provided by the TSA in the certificates field from the SignedData structure in that response. */ protected boolean certReq = true; /** * Replay attack detection: large random number with a high probability that it is generated only once. */ protected TSPNonceSource tspNonceSource; /** * The data loader used to retrieve the timestamp. */ protected DataLoader dataLoader; /** * The default constructor for OnlineTSPSource. */ public OnlineTSPSource() { } /** * Build a OnlineTSPSource that will query the specified URL * * @param tspServerUrl */ public OnlineTSPSource(final String tspServerUrl) { this.tspServerUrl = tspServerUrl; } /** * Set the URL to access the TSA * * @param tspServerUrl */ public void setTspServerUrl(final String tspServerUrl) { this.tspServerUrl = tspServerUrl; } /** * @return the URL to access the TSA */ public String getTspServerUrl() { return tspServerUrl; } @Override public void setReqPolicyOid(final String reqPolicyOid) { this.reqPolicyOid = new ASN1ObjectIdentifier(reqPolicyOid); } /** * @return the request policy OID */ public String getReqPolicyOid() { return reqPolicyOid.toString(); } @Override public String getUniqueId(final byte[] digestValue) { final byte[] digest = DSSUtils.digest(DigestAlgorithm.MD5, digestValue, tspNonceSource.getNonce().toByteArray()); return DSSUtils.encodeHexString(digest); } public void setTspNonceSource(final TSPNonceSource tspNonceSource) { this.tspNonceSource = tspNonceSource; } public TSPNonceSource getTspNonceSource() { return tspNonceSource; } /** * Allows to indicate if the signing certificate MUST be provided by the TSA in the response. * * @param certReq if true the signing certificate is provided in the response */ public void setCertReq(boolean certReq) { this.certReq = certReq; } /** * @return indicates if the signing certificate MUST be provided by the TSA in the response */ public boolean isCertReq() { return certReq; } /** * This method allows to set the {@code DataLoader} to be used to communicate with the TSA. * * @param dataLoader {@code DataLoader} */ public void setDataLoader(final DataLoader dataLoader) { this.dataLoader = dataLoader; } /** * This method returns the underlying {@code DataLoader}. Note that when the loader is not set an instance of a default loader is created. * * @return {@code DataLoader} */ public DataLoader getDataLoader() { if (dataLoader == null) { dataLoader = getDefaultDataLoader(); } return dataLoader; } /** * This method provides a default {@code DataLoader} to be used when communicating with the timestamp server. This method can be overloaded. * * @return {@code CommonsDataLoader} */ protected CommonDataLoader getDefaultDataLoader() { final CommonDataLoader commonDataLoader = new CommonDataLoader(CONTENT_TYPE); return commonDataLoader; } @Override public TimeStampToken getTimeStampResponse(final DigestAlgorithm digestAlgorithm, final byte[] digest) throws DSSException { traceTimestampRequest(digestAlgorithm, digest); final byte[] requestBytes = generateTimestampRequest(digestAlgorithm, digest); final DataLoader currentDataLoader = getDataLoader(); final byte[] respBytes = currentDataLoader.post(tspServerUrl, requestBytes); final TimeStampResponse timeStampResponse = DSSUtils.newTimeStampResponse(respBytes); traceTimestampResponse(timeStampResponse); final TimeStampToken timeStampToken = timeStampResponse.getTimeStampToken(); return timeStampToken; } /** * Setup the time stamp request * * @param digestAlgorithm {@code DigestAlgorithm} used to generate the message imprint * @param digest digest value as byte array * @return array of bytes representing the {@code TimeStampRequest} * @throws DSSException */ private byte[] generateTimestampRequest(final DigestAlgorithm digestAlgorithm, final byte[] digest) throws DSSException { final TimeStampRequestGenerator tsqGenerator = new TimeStampRequestGenerator(); tsqGenerator.setCertReq(certReq); if (reqPolicyOid != null) { tsqGenerator.setReqPolicy(reqPolicyOid); } final ASN1ObjectIdentifier asn1ObjectIdentifier = digestAlgorithm.getOid(); final BigInteger nonce = getNonce(); final TimeStampRequest request = tsqGenerator.generate(asn1ObjectIdentifier, digest, nonce); return DSSUtils.getEncoded(request); } private static void traceTimestampResponse(final TimeStampResponse timeStampResponse) { if (LOG.isTraceEnabled()) { final int status = timeStampResponse.getStatus(); LOG.trace("Status: " + (status == 0 ? "granted (0) --> you got exactly what you asked for." : timeStampResponse.getStatusString())); } } private static void traceTimestampRequest(final DigestAlgorithm digestAlgorithm, final byte[] digest) { if (LOG.isTraceEnabled()) { LOG.trace("Timestamp digest algorithm: " + digestAlgorithm.getName()); LOG.trace("Timestamp digest value : " + DSSUtils.toHex(digest)); } } private BigInteger getNonce() { if (tspNonceSource == null) { tspNonceSource = new TSPNonceSource(); } return tspNonceSource.getNonce(); } }