001package org.apache.commons.ssl.org.bouncycastle.asn1.cms;
002
003import java.util.Enumeration;
004
005import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1EncodableVector;
006import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Integer;
007import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Object;
008import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1ObjectIdentifier;
009import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Primitive;
010import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Sequence;
011import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Set;
012import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1TaggedObject;
013import org.apache.commons.ssl.org.bouncycastle.asn1.BERSequence;
014import org.apache.commons.ssl.org.bouncycastle.asn1.BERSet;
015import org.apache.commons.ssl.org.bouncycastle.asn1.BERTaggedObject;
016import org.apache.commons.ssl.org.bouncycastle.asn1.DERTaggedObject;
017
018/**
019 * <a href="http://tools.ietf.org/html/rfc5652#section-5.1">RFC 5652</a>:
020 * <p>
021 * A signed data object containing multitude of {@link SignerInfo}s.
022 * <pre>
023 * SignedData ::= SEQUENCE {
024 *     version CMSVersion,
025 *     digestAlgorithms DigestAlgorithmIdentifiers,
026 *     encapContentInfo EncapsulatedContentInfo,
027 *     certificates [0] IMPLICIT CertificateSet OPTIONAL,
028 *     crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
029 *     signerInfos SignerInfos
030 *   }
031 * 
032 * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
033 * 
034 * SignerInfos ::= SET OF SignerInfo
035 * </pre>
036 * <p>
037 * The version calculation uses following ruleset from RFC 3852 section 5.1:
038 * <pre>
039 * IF ((certificates is present) AND
040 *    (any certificates with a type of other are present)) OR
041 *    ((crls is present) AND
042 *    (any crls with a type of other are present))
043 * THEN version MUST be 5
044 * ELSE
045 *    IF (certificates is present) AND
046 *       (any version 2 attribute certificates are present)
047 *    THEN version MUST be 4
048 *    ELSE
049 *       IF ((certificates is present) AND
050 *          (any version 1 attribute certificates are present)) OR
051 *          (any SignerInfo structures are version 3) OR
052 *          (encapContentInfo eContentType is other than id-data)
053 *       THEN version MUST be 3
054 *       ELSE version MUST be 1
055 * </pre>
056 * <p>
057 * @todo Check possible update for this to RFC 5652 level
058 */
059public class SignedData
060    extends ASN1Object
061{
062    private static final ASN1Integer VERSION_1 = new ASN1Integer(1);
063    private static final ASN1Integer VERSION_3 = new ASN1Integer(3);
064    private static final ASN1Integer VERSION_4 = new ASN1Integer(4);
065    private static final ASN1Integer VERSION_5 = new ASN1Integer(5);
066
067    private ASN1Integer version;
068    private ASN1Set     digestAlgorithms;
069    private ContentInfo contentInfo;
070    private ASN1Set     certificates;
071    private ASN1Set     crls;
072    private ASN1Set     signerInfos;
073    private boolean certsBer;
074    private boolean        crlsBer;
075
076    /**
077     * Return a SignedData object from the given object.
078     * <p>
079     * Accepted inputs:
080     * <ul>
081     * <li> null &rarr; null
082     * <li> {@link SignedData} object
083     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with SignedData structure inside
084     * </ul>
085     *
086     * @param o the object we want converted.
087     * @exception IllegalArgumentException if the object cannot be converted.
088     */
089    public static SignedData getInstance(
090        Object  o)
091    {
092        if (o instanceof SignedData)
093        {
094            return (SignedData)o;
095        }
096        else if (o != null)
097        {
098            return new SignedData(ASN1Sequence.getInstance(o));
099        }
100
101        return null;
102    }
103
104    public SignedData(
105        ASN1Set     digestAlgorithms,
106        ContentInfo contentInfo,
107        ASN1Set     certificates,
108        ASN1Set     crls,
109        ASN1Set     signerInfos)
110    {
111        this.version = calculateVersion(contentInfo.getContentType(), certificates, crls, signerInfos);
112        this.digestAlgorithms = digestAlgorithms;
113        this.contentInfo = contentInfo;
114        this.certificates = certificates;
115        this.crls = crls;
116        this.signerInfos = signerInfos;
117        this.crlsBer = crls instanceof BERSet;
118        this.certsBer = certificates instanceof BERSet;
119    }
120
121
122    private ASN1Integer calculateVersion(
123        ASN1ObjectIdentifier contentOid,
124        ASN1Set certs,
125        ASN1Set crls,
126        ASN1Set signerInfs)
127    {
128        boolean otherCert = false;
129        boolean otherCrl = false;
130        boolean attrCertV1Found = false;
131        boolean attrCertV2Found = false;
132
133        if (certs != null)
134        {
135            for (Enumeration en = certs.getObjects(); en.hasMoreElements();)
136            {
137                Object obj = en.nextElement();
138                if (obj instanceof ASN1TaggedObject)
139                {
140                    ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(obj);
141
142                    if (tagged.getTagNo() == 1)
143                    {
144                        attrCertV1Found = true;
145                    }
146                    else if (tagged.getTagNo() == 2)
147                    {
148                        attrCertV2Found = true;
149                    }
150                    else if (tagged.getTagNo() == 3)
151                    {
152                        otherCert = true;
153                    }
154                }
155            }
156        }
157
158        if (otherCert)
159        {
160            return new ASN1Integer(5);
161        }
162
163        if (crls != null)         // no need to check if otherCert is true
164        {
165            for (Enumeration en = crls.getObjects(); en.hasMoreElements();)
166            {
167                Object obj = en.nextElement();
168                if (obj instanceof ASN1TaggedObject)
169                {
170                    otherCrl = true;
171                }
172            }
173        }
174
175        if (otherCrl)
176        {
177            return VERSION_5;
178        }
179
180        if (attrCertV2Found)
181        {
182            return VERSION_4;
183        }
184
185        if (attrCertV1Found)
186        {
187            return VERSION_3;
188        }
189
190        if (checkForVersion3(signerInfs))
191        {
192            return VERSION_3;
193        }
194
195        if (!CMSObjectIdentifiers.data.equals(contentOid))
196        {
197            return VERSION_3;
198        }
199
200        return VERSION_1;
201    }
202
203    private boolean checkForVersion3(ASN1Set signerInfs)
204    {
205        for (Enumeration e = signerInfs.getObjects(); e.hasMoreElements();)
206        {
207            SignerInfo s = SignerInfo.getInstance(e.nextElement());
208
209            if (s.getVersion().getValue().intValue() == 3)
210            {
211                return true;
212            }
213        }
214
215        return false;
216    }
217
218    private SignedData(
219        ASN1Sequence seq)
220    {
221        Enumeration     e = seq.getObjects();
222
223        version = ASN1Integer.getInstance(e.nextElement());
224        digestAlgorithms = ((ASN1Set)e.nextElement());
225        contentInfo = ContentInfo.getInstance(e.nextElement());
226
227        while (e.hasMoreElements())
228        {
229            ASN1Primitive o = (ASN1Primitive)e.nextElement();
230
231            //
232            // an interesting feature of SignedData is that there appear
233            // to be varying implementations...
234            // for the moment we ignore anything which doesn't fit.
235            //
236            if (o instanceof ASN1TaggedObject)
237            {
238                ASN1TaggedObject tagged = (ASN1TaggedObject)o;
239
240                switch (tagged.getTagNo())
241                {
242                case 0:
243                    certsBer = tagged instanceof BERTaggedObject;
244                    certificates = ASN1Set.getInstance(tagged, false);
245                    break;
246                case 1:
247                    crlsBer = tagged instanceof BERTaggedObject;
248                    crls = ASN1Set.getInstance(tagged, false);
249                    break;
250                default:
251                    throw new IllegalArgumentException("unknown tag value " + tagged.getTagNo());
252                }
253            }
254            else
255            {
256                signerInfos = (ASN1Set)o;
257            }
258        }
259    }
260
261    public ASN1Integer getVersion()
262    {
263        return version;
264    }
265
266    public ASN1Set getDigestAlgorithms()
267    {
268        return digestAlgorithms;
269    }
270
271    public ContentInfo getEncapContentInfo()
272    {
273        return contentInfo;
274    }
275
276    public ASN1Set getCertificates()
277    {
278        return certificates;
279    }
280
281    public ASN1Set getCRLs()
282    {
283        return crls;
284    }
285
286    public ASN1Set getSignerInfos()
287    {
288        return signerInfos;
289    }
290
291    /**
292     * Produce an object suitable for an ASN1OutputStream.
293     */
294    public ASN1Primitive toASN1Primitive()
295    {
296        ASN1EncodableVector  v = new ASN1EncodableVector();
297
298        v.add(version);
299        v.add(digestAlgorithms);
300        v.add(contentInfo);
301
302        if (certificates != null)
303        {
304            if (certsBer)
305            {
306                v.add(new BERTaggedObject(false, 0, certificates));
307            }
308            else
309            {
310                v.add(new DERTaggedObject(false, 0, certificates));
311            }
312        }
313
314        if (crls != null)
315        {
316            if (crlsBer)
317            {
318                v.add(new BERTaggedObject(false, 1, crls));
319            }
320            else
321            {
322                v.add(new DERTaggedObject(false, 1, crls));
323            }
324        }
325
326        v.add(signerInfos);
327
328        return new BERSequence(v);
329    }
330}