001/* 002 * Copyright 2014 Florent Daigniere 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package net.jsign.timestamp; 018 019import java.io.IOException; 020import java.util.HashMap; 021import java.util.Map; 022 023import org.bouncycastle.asn1.DERSet; 024import org.bouncycastle.asn1.cms.Attribute; 025import org.bouncycastle.cms.CMSException; 026import org.bouncycastle.cms.CMSSignedData; 027import org.bouncycastle.tsp.TimeStampRequest; 028import org.bouncycastle.tsp.TimeStampRequestGenerator; 029import org.bouncycastle.tsp.TimeStampResponse; 030 031import net.jsign.DigestAlgorithm; 032 033import static net.jsign.asn1.authenticode.AuthenticodeObjectIdentifiers.*; 034import static org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers.*; 035 036/** 037 * RFC 3161 timestamping. 038 * 039 * @author Florent Daigniere 040 * @see <a href="https://www.ietf.org/rfc/rfc3161.txt">Internet X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)</a> 041 * @since 1.3 042 */ 043public class RFC3161Timestamper extends Timestamper { 044 045 /** 046 * Tells if the timestamp should use the standard Signature Time-stamp attribute 047 * defined in RFC 3161 or the Authenticode specific attribute SPC_RFC3161_OBJID. 048 */ 049 private boolean standardAttribute = false; 050 051 public RFC3161Timestamper() { 052 setURL("http://timestamp.sectigo.com"); 053 } 054 055 @Override 056 public CMSSignedData timestamp(DigestAlgorithm algo, CMSSignedData sigData) throws TimestampingException, IOException, CMSException { 057 standardAttribute = !isAuthenticode(sigData.getSignedContentTypeOID()); 058 return super.timestamp(algo, sigData); 059 } 060 061 protected CMSSignedData timestamp(DigestAlgorithm algo, byte[] encryptedDigest) throws IOException, TimestampingException { 062 TimeStampRequestGenerator reqgen = new TimeStampRequestGenerator(); 063 reqgen.setCertReq(true); 064 TimeStampRequest req = reqgen.generate(algo.oid, algo.getMessageDigest().digest(encryptedDigest)); 065 byte[] request = req.getEncoded(); 066 067 Map<String, String> headers = new HashMap<>(); 068 headers.put("Content-Type", "application/timestamp-query"); 069 headers.put("Accept", "application/timestamp-reply"); 070 byte[] response = post(tsaurl, request, headers); 071 072 try { 073 TimeStampResponse resp = new TimeStampResponse(response); 074 resp.validate(req); 075 if (resp.getStatus() != 0) { 076 throw new IOException("Unable to complete the timestamping due to an invalid response (" + resp.getStatusString() + ")"); 077 } 078 079 return resp.getTimeStampToken().toCMSSignedData(); 080 081 } catch (Exception e) { 082 throw new TimestampingException("Unable to complete the timestamping", e); 083 } 084 } 085 086 @Override 087 protected Attribute getCounterSignature(CMSSignedData token) { 088 return new Attribute(standardAttribute ? id_aa_signatureTimeStampToken : SPC_RFC3161_OBJID, new DERSet(token.toASN1Structure())); 089 } 090}