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.net.HttpURLConnection; 021 022import net.jsign.DigestAlgorithm; 023import net.jsign.asn1.authenticode.AuthenticodeObjectIdentifiers; 024 025import org.bouncycastle.asn1.ASN1InputStream; 026import org.bouncycastle.asn1.DERSet; 027import org.bouncycastle.asn1.cms.Attribute; 028import org.bouncycastle.asn1.cms.AttributeTable; 029import org.bouncycastle.asn1.tsp.TimeStampResp; 030import org.bouncycastle.cms.CMSSignedData; 031import org.bouncycastle.tsp.TimeStampRequest; 032import org.bouncycastle.tsp.TimeStampRequestGenerator; 033import org.bouncycastle.tsp.TimeStampResponse; 034 035/** 036 * RFC 3161 timestamping. 037 * 038 * @author Florent Daigniere 039 * @see <a href="https://www.ietf.org/rfc/rfc3161.txt">Internet X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)</a> 040 * @since 1.3 041 */ 042public class RFC3161Timestamper extends Timestamper { 043 044 public RFC3161Timestamper() { 045 setURL("http://timestamp.sectigo.com"); 046 } 047 048 protected CMSSignedData timestamp(DigestAlgorithm algo, byte[] encryptedDigest) throws IOException, TimestampingException { 049 TimeStampRequestGenerator reqgen = new TimeStampRequestGenerator(); 050 reqgen.setCertReq(true); 051 TimeStampRequest req = reqgen.generate(algo.oid, algo.getMessageDigest().digest(encryptedDigest)); 052 byte[] request = req.getEncoded(); 053 054 HttpURLConnection conn = (HttpURLConnection) tsaurl.openConnection(); 055 conn.setConnectTimeout(10000); 056 conn.setReadTimeout(10000); 057 conn.setDoOutput(true); 058 conn.setDoInput(true); 059 conn.setUseCaches(false); 060 conn.setRequestMethod("POST"); 061 conn.setRequestProperty("Content-type", "application/timestamp-query"); 062 conn.setRequestProperty("Content-length", String.valueOf(request.length)); 063 conn.setRequestProperty("Accept", "application/timestamp-reply"); 064 conn.setRequestProperty("User-Agent", "Transport"); 065 066 conn.getOutputStream().write(request); 067 conn.getOutputStream().flush(); 068 069 if (conn.getResponseCode() >= 400) { 070 throw new IOException("Unable to complete the timestamping due to HTTP error: " + conn.getResponseCode() + " - " + conn.getResponseMessage()); 071 } 072 073 try { 074 TimeStampResp resp = TimeStampResp.getInstance(new ASN1InputStream(conn.getInputStream()).readObject()); 075 TimeStampResponse response = new TimeStampResponse(resp); 076 response.validate(req); 077 if (response.getStatus() != 0) { 078 throw new IOException("Unable to complete the timestamping due to an invalid response (" + response.getStatusString() + ")"); 079 } 080 081 return response.getTimeStampToken().toCMSSignedData(); 082 083 } catch (Exception e) { 084 throw new TimestampingException("Unable to complete the timestamping", e); 085 } 086 } 087 088 @Override 089 protected AttributeTable getUnsignedAttributes(CMSSignedData token) { 090 Attribute rfc3161CounterSignature = new Attribute(AuthenticodeObjectIdentifiers.SPC_RFC3161_OBJID, new DERSet(token.toASN1Structure())); 091 return new AttributeTable(rfc3161CounterSignature); 092 } 093}