001/*
002 * Copyright 2014 Emmanuel Bourg
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.Collection;
021import java.util.HashMap;
022import java.util.Map;
023
024import org.bouncycastle.asn1.DERSet;
025import org.bouncycastle.asn1.cms.Attribute;
026import org.bouncycastle.asn1.cms.CMSAttributes;
027import org.bouncycastle.cert.X509CertificateHolder;
028import org.bouncycastle.cms.CMSSignedData;
029import org.bouncycastle.cms.SignerInformation;
030import org.bouncycastle.util.encoders.Base64;
031
032import net.jsign.DigestAlgorithm;
033import net.jsign.asn1.authenticode.AuthenticodeTimeStampRequest;
034
035/**
036 * Legacy Authenticode timestamping.
037 *
038 * @author Emmanuel Bourg
039 * @since 1.3
040 */
041public class AuthenticodeTimestamper extends Timestamper {
042
043    public AuthenticodeTimestamper() {
044        setURL("http://timestamp.sectigo.com");
045    }
046
047    protected CMSSignedData timestamp(DigestAlgorithm algo, byte[] encryptedDigest) throws IOException, TimestampingException {
048        AuthenticodeTimeStampRequest timestampRequest = new AuthenticodeTimeStampRequest(encryptedDigest);
049
050        byte[] request = Base64.encode(timestampRequest.getEncoded("DER"));
051        Map<String, String> headers = new HashMap<>();
052        headers.put("Content-Type", "application/octet-stream");
053        headers.put("Accept", "application/octet-stream");
054        byte[] response = post(tsaurl, request, headers);
055
056        try {
057            return new CMSSignedData(Base64.decode(response));
058        } catch (Exception e) {
059            throw new TimestampingException("Unable to complete the timestamping", e);
060        }
061    }
062
063    @Override
064    protected Collection<X509CertificateHolder> getExtraCertificates(CMSSignedData token) {
065        return token.getCertificates().getMatches(null);
066    }
067
068    @Override
069    protected Attribute getCounterSignature(CMSSignedData token) {
070        SignerInformation timestampSignerInformation = token.getSignerInfos().getSigners().iterator().next();
071        return new Attribute(CMSAttributes.counterSignature, new DERSet(timestampSignerInformation.toASN1Structure()));
072    }
073}