1 package org.bouncycastle.cms;
2 
3 import java.io.IOException;
4 import java.io.OutputStream;
5 import java.util.Collections;
6 import java.util.HashMap;
7 import java.util.Map;
8 
9 import org.bouncycastle.asn1.ASN1Encoding;
10 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
11 import org.bouncycastle.asn1.ASN1Set;
12 import org.bouncycastle.asn1.DEROctetString;
13 import org.bouncycastle.asn1.DERSet;
14 import org.bouncycastle.asn1.cms.AttributeTable;
15 import org.bouncycastle.asn1.cms.SignerIdentifier;
16 import org.bouncycastle.asn1.cms.SignerInfo;
17 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
18 import org.bouncycastle.cert.X509CertificateHolder;
19 import org.bouncycastle.operator.ContentSigner;
20 import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
21 import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
22 import org.bouncycastle.operator.DigestCalculator;
23 import org.bouncycastle.operator.DigestCalculatorProvider;
24 import org.bouncycastle.operator.OperatorCreationException;
25 import org.bouncycastle.util.Arrays;
26 import org.bouncycastle.util.io.TeeOutputStream;
27 
28 public class SignerInfoGenerator
29 {
30     private final SignerIdentifier signerIdentifier;
31     private final CMSAttributeTableGenerator sAttrGen;
32     private final CMSAttributeTableGenerator unsAttrGen;
33     private final ContentSigner signer;
34     private final DigestCalculator digester;
35     private final DigestAlgorithmIdentifierFinder digAlgFinder = new DefaultDigestAlgorithmIdentifierFinder();
36     private final CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder;
37 
38     private byte[] calculatedDigest = null;
39     private X509CertificateHolder certHolder;
40 
SignerInfoGenerator( SignerIdentifier signerIdentifier, ContentSigner signer, DigestCalculatorProvider digesterProvider, CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder)41     SignerInfoGenerator(
42         SignerIdentifier signerIdentifier,
43         ContentSigner signer,
44         DigestCalculatorProvider digesterProvider,
45         CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder)
46         throws OperatorCreationException
47     {
48         this(signerIdentifier, signer, digesterProvider, sigEncAlgFinder, false);
49     }
50 
SignerInfoGenerator( SignerIdentifier signerIdentifier, ContentSigner signer, DigestCalculatorProvider digesterProvider, CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder, boolean isDirectSignature)51     SignerInfoGenerator(
52         SignerIdentifier signerIdentifier,
53         ContentSigner signer,
54         DigestCalculatorProvider digesterProvider,
55         CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder,
56         boolean isDirectSignature)
57         throws OperatorCreationException
58     {
59         this.signerIdentifier = signerIdentifier;
60         this.signer = signer;
61 
62         if (digesterProvider != null)
63         {
64             this.digester = digesterProvider.get(digAlgFinder.find(signer.getAlgorithmIdentifier()));
65         }
66         else
67         {
68             this.digester = null;
69         }
70 
71         if (isDirectSignature)
72         {
73             this.sAttrGen = null;
74             this.unsAttrGen = null;
75         }
76         else
77         {
78             this.sAttrGen = new DefaultSignedAttributeTableGenerator();
79             this.unsAttrGen = null;
80         }
81 
82         this.sigEncAlgFinder = sigEncAlgFinder;
83     }
84 
SignerInfoGenerator( SignerInfoGenerator original, CMSAttributeTableGenerator sAttrGen, CMSAttributeTableGenerator unsAttrGen)85     public SignerInfoGenerator(
86         SignerInfoGenerator original,
87         CMSAttributeTableGenerator sAttrGen,
88         CMSAttributeTableGenerator unsAttrGen)
89     {
90         this.signerIdentifier = original.signerIdentifier;
91         this.signer = original.signer;
92         this.digester = original.digester;
93         this.sigEncAlgFinder = original.sigEncAlgFinder;
94         this.sAttrGen = sAttrGen;
95         this.unsAttrGen = unsAttrGen;
96     }
97 
SignerInfoGenerator( SignerIdentifier signerIdentifier, ContentSigner signer, DigestCalculatorProvider digesterProvider, CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder, CMSAttributeTableGenerator sAttrGen, CMSAttributeTableGenerator unsAttrGen)98     SignerInfoGenerator(
99         SignerIdentifier signerIdentifier,
100         ContentSigner signer,
101         DigestCalculatorProvider digesterProvider,
102         CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder,
103         CMSAttributeTableGenerator sAttrGen,
104         CMSAttributeTableGenerator unsAttrGen)
105         throws OperatorCreationException
106     {
107         this.signerIdentifier = signerIdentifier;
108         this.signer = signer;
109 
110         if (digesterProvider != null)
111         {
112             this.digester = digesterProvider.get(digAlgFinder.find(signer.getAlgorithmIdentifier()));
113         }
114         else
115         {
116             this.digester = null;
117         }
118 
119         this.sAttrGen = sAttrGen;
120         this.unsAttrGen = unsAttrGen;
121         this.sigEncAlgFinder = sigEncAlgFinder;
122     }
123 
getSID()124     public SignerIdentifier getSID()
125     {
126         return signerIdentifier;
127     }
128 
getGeneratedVersion()129     public int getGeneratedVersion()
130     {
131         return signerIdentifier.isTagged() ? 3 : 1;
132     }
133 
hasAssociatedCertificate()134     public boolean hasAssociatedCertificate()
135     {
136         return certHolder != null;
137     }
138 
getAssociatedCertificate()139     public X509CertificateHolder getAssociatedCertificate()
140     {
141         return certHolder;
142     }
143 
getDigestAlgorithm()144     public AlgorithmIdentifier getDigestAlgorithm()
145     {
146         if (digester != null)
147         {
148             return digester.getAlgorithmIdentifier();
149         }
150 
151         return digAlgFinder.find(signer.getAlgorithmIdentifier());
152     }
153 
getCalculatingOutputStream()154     public OutputStream getCalculatingOutputStream()
155     {
156         if (digester != null)
157         {
158             if (sAttrGen == null)
159             {
160                 return new TeeOutputStream(digester.getOutputStream(), signer.getOutputStream());
161             }
162             return digester.getOutputStream();
163         }
164         else
165         {
166             return signer.getOutputStream();
167         }
168     }
169 
generate(ASN1ObjectIdentifier contentType)170     public SignerInfo generate(ASN1ObjectIdentifier contentType)
171         throws CMSException
172     {
173         try
174         {
175             /* RFC 3852 5.4
176              * The result of the message digest calculation process depends on
177              * whether the signedAttrs field is present.  When the field is absent,
178              * the result is just the message digest of the content as described
179              *
180              * above.  When the field is present, however, the result is the message
181              * digest of the complete DER encoding of the SignedAttrs value
182              * contained in the signedAttrs field.
183              */
184             ASN1Set signedAttr = null;
185 
186             AlgorithmIdentifier digestEncryptionAlgorithm = sigEncAlgFinder.findEncryptionAlgorithm(signer.getAlgorithmIdentifier());
187 
188             AlgorithmIdentifier digestAlg = null;
189 
190             if (sAttrGen != null)
191             {
192                 digestAlg = digester.getAlgorithmIdentifier();
193                 calculatedDigest = digester.getDigest();
194                 Map parameters = getBaseParameters(contentType, digester.getAlgorithmIdentifier(), digestEncryptionAlgorithm, calculatedDigest);
195                 AttributeTable signed = sAttrGen.getAttributes(Collections.unmodifiableMap(parameters));
196 
197                 signedAttr = getAttributeSet(signed);
198 
199                 // sig must be composed from the DER encoding.
200                 OutputStream sOut = signer.getOutputStream();
201 
202                 sOut.write(signedAttr.getEncoded(ASN1Encoding.DER));
203 
204                 sOut.close();
205             }
206             else
207             {
208                 if (digester != null)
209                 {
210                     digestAlg = digester.getAlgorithmIdentifier();
211                     calculatedDigest = digester.getDigest();
212                 }
213                 else
214                 {
215                     digestAlg = digAlgFinder.find(signer.getAlgorithmIdentifier());
216                     calculatedDigest = null;
217                 }
218             }
219 
220             byte[] sigBytes = signer.getSignature();
221 
222             ASN1Set unsignedAttr = null;
223             if (unsAttrGen != null)
224             {
225                 Map parameters = getBaseParameters(contentType, digestAlg, digestEncryptionAlgorithm, calculatedDigest);
226                 parameters.put(CMSAttributeTableGenerator.SIGNATURE, Arrays.clone(sigBytes));
227 
228                 AttributeTable unsigned = unsAttrGen.getAttributes(Collections.unmodifiableMap(parameters));
229 
230                 unsignedAttr = getAttributeSet(unsigned);
231             }
232 
233             return new SignerInfo(signerIdentifier, digestAlg,
234                 signedAttr, digestEncryptionAlgorithm, new DEROctetString(sigBytes), unsignedAttr);
235         }
236         catch (IOException e)
237         {
238             throw new CMSException("encoding error.", e);
239         }
240     }
241 
setAssociatedCertificate(X509CertificateHolder certHolder)242     void setAssociatedCertificate(X509CertificateHolder certHolder)
243     {
244         this.certHolder = certHolder;
245     }
246 
getAttributeSet( AttributeTable attr)247     private ASN1Set getAttributeSet(
248         AttributeTable attr)
249     {
250         if (attr != null)
251         {
252             return new DERSet(attr.toASN1EncodableVector());
253         }
254 
255         return null;
256     }
257 
getBaseParameters(ASN1ObjectIdentifier contentType, AlgorithmIdentifier digAlgId, AlgorithmIdentifier sigAlgId, byte[] hash)258     private Map getBaseParameters(ASN1ObjectIdentifier contentType, AlgorithmIdentifier digAlgId, AlgorithmIdentifier sigAlgId, byte[] hash)
259     {
260         Map param = new HashMap();
261 
262         if (contentType != null)
263         {
264             param.put(CMSAttributeTableGenerator.CONTENT_TYPE, contentType);
265         }
266 
267         param.put(CMSAttributeTableGenerator.DIGEST_ALGORITHM_IDENTIFIER, digAlgId);
268         param.put(CMSAttributeTableGenerator.SIGNATURE_ALGORITHM_IDENTIFIER, sigAlgId);
269         param.put(CMSAttributeTableGenerator.DIGEST,  Arrays.clone(hash));
270 
271         return param;
272     }
273 
getCalculatedDigest()274     public byte[] getCalculatedDigest()
275     {
276         if (calculatedDigest != null)
277         {
278             return Arrays.clone(calculatedDigest);
279         }
280 
281         return null;
282     }
283 
getSignedAttributeTableGenerator()284     public CMSAttributeTableGenerator getSignedAttributeTableGenerator()
285     {
286         return sAttrGen;
287     }
288 
getUnsignedAttributeTableGenerator()289     public CMSAttributeTableGenerator getUnsignedAttributeTableGenerator()
290     {
291         return unsAttrGen;
292     }
293 }
294