1package org.bouncycastle.cms;
2
3import org.bouncycastle.asn1.DEROctetString;
4import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
5import org.bouncycastle.asn1.cms.SignerIdentifier;
6import org.bouncycastle.cert.X509CertificateHolder;
7import org.bouncycastle.operator.ContentSigner;
8import org.bouncycastle.operator.DigestCalculatorProvider;
9import org.bouncycastle.operator.OperatorCreationException;
10
11/**
12 * Builder for SignerInfo generator objects.
13 */
14public class SignerInfoGeneratorBuilder
15{
16    private DigestCalculatorProvider digestProvider;
17    private boolean directSignature;
18    private CMSAttributeTableGenerator signedGen;
19    private CMSAttributeTableGenerator unsignedGen;
20    private CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder;
21
22    /**
23     *  Base constructor.
24     *
25     * @param digestProvider  a provider of digest calculators for the algorithms required in the signature and attribute calculations.
26     */
27    public SignerInfoGeneratorBuilder(DigestCalculatorProvider digestProvider)
28    {
29        this(digestProvider, new DefaultCMSSignatureEncryptionAlgorithmFinder());
30    }
31
32        /**
33     *  Base constructor.
34     *
35     * @param digestProvider  a provider of digest calculators for the algorithms required in the signature and attribute calculations.
36     */
37    public SignerInfoGeneratorBuilder(DigestCalculatorProvider digestProvider, CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder)
38    {
39        this.digestProvider = digestProvider;
40        this.sigEncAlgFinder = sigEncAlgFinder;
41    }
42
43    /**
44     * If the passed in flag is true, the signer signature will be based on the data, not
45     * a collection of signed attributes, and no signed attributes will be included.
46     *
47     * @return the builder object
48     */
49    public SignerInfoGeneratorBuilder setDirectSignature(boolean hasNoSignedAttributes)
50    {
51        this.directSignature = hasNoSignedAttributes;
52
53        return this;
54    }
55
56    /**
57     *  Provide a custom signed attribute generator.
58     *
59     * @param signedGen a generator of signed attributes.
60     * @return the builder object
61     */
62    public SignerInfoGeneratorBuilder setSignedAttributeGenerator(CMSAttributeTableGenerator signedGen)
63    {
64        this.signedGen = signedGen;
65
66        return this;
67    }
68
69    /**
70     * Provide a generator of unsigned attributes.
71     *
72     * @param unsignedGen  a generator for signed attributes.
73     * @return the builder object
74     */
75    public SignerInfoGeneratorBuilder setUnsignedAttributeGenerator(CMSAttributeTableGenerator unsignedGen)
76    {
77        this.unsignedGen = unsignedGen;
78
79        return this;
80    }
81
82    /**
83     * Build a generator with the passed in certHolder issuer and serial number as the signerIdentifier.
84     *
85     * @param contentSigner  operator for generating the final signature in the SignerInfo with.
86     * @param certHolder  carrier for the X.509 certificate related to the contentSigner.
87     * @return  a SignerInfoGenerator
88     * @throws OperatorCreationException   if the generator cannot be built.
89     */
90    public SignerInfoGenerator build(ContentSigner contentSigner, X509CertificateHolder certHolder)
91        throws OperatorCreationException
92    {
93        SignerIdentifier sigId = new SignerIdentifier(new IssuerAndSerialNumber(certHolder.toASN1Structure()));
94
95        SignerInfoGenerator sigInfoGen = createGenerator(contentSigner, sigId);
96
97        sigInfoGen.setAssociatedCertificate(certHolder);
98
99        return sigInfoGen;
100    }
101
102    /**
103     * Build a generator with the passed in subjectKeyIdentifier as the signerIdentifier. If used  you should
104     * try to follow the calculation described in RFC 5280 section 4.2.1.2.
105     *
106     * @param contentSigner  operator for generating the final signature in the SignerInfo with.
107     * @param subjectKeyIdentifier    key identifier to identify the public key for verifying the signature.
108     * @return  a SignerInfoGenerator
109     * @throws OperatorCreationException if the generator cannot be built.
110     */
111    public SignerInfoGenerator build(ContentSigner contentSigner, byte[] subjectKeyIdentifier)
112        throws OperatorCreationException
113    {
114        SignerIdentifier sigId = new SignerIdentifier(new DEROctetString(subjectKeyIdentifier));
115
116        return createGenerator(contentSigner, sigId);
117    }
118
119    private SignerInfoGenerator createGenerator(ContentSigner contentSigner, SignerIdentifier sigId)
120        throws OperatorCreationException
121    {
122        if (directSignature)
123        {
124            return new SignerInfoGenerator(sigId, contentSigner, digestProvider, sigEncAlgFinder, true);
125        }
126
127        if (signedGen != null || unsignedGen != null)
128        {
129            if (signedGen == null)
130            {
131                signedGen = new DefaultSignedAttributeTableGenerator();
132            }
133
134            return new SignerInfoGenerator(sigId, contentSigner, digestProvider, sigEncAlgFinder, signedGen, unsignedGen);
135        }
136
137        return new SignerInfoGenerator(sigId, contentSigner, digestProvider, sigEncAlgFinder);
138    }
139}
140