diff options
Diffstat (limited to 'libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/SignedData.java')
-rw-r--r-- | libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/SignedData.java | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/SignedData.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/SignedData.java new file mode 100644 index 000000000..c564b3917 --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/asn1/cms/SignedData.java @@ -0,0 +1,330 @@ +package org.spongycastle.asn1.cms; + +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1Object; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.ASN1TaggedObject; +import org.spongycastle.asn1.BERSequence; +import org.spongycastle.asn1.BERSet; +import org.spongycastle.asn1.BERTaggedObject; +import org.spongycastle.asn1.DERTaggedObject; + +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-5.1">RFC 5652</a>: + * <p> + * A signed data object containing multitude of {@link SignerInfo}s. + * <pre> + * SignedData ::= SEQUENCE { + * version CMSVersion, + * digestAlgorithms DigestAlgorithmIdentifiers, + * encapContentInfo EncapsulatedContentInfo, + * certificates [0] IMPLICIT CertificateSet OPTIONAL, + * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, + * signerInfos SignerInfos + * } + * + * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier + * + * SignerInfos ::= SET OF SignerInfo + * </pre> + * <p> + * The version calculation uses following ruleset from RFC 3852 section 5.1: + * <pre> + * IF ((certificates is present) AND + * (any certificates with a type of other are present)) OR + * ((crls is present) AND + * (any crls with a type of other are present)) + * THEN version MUST be 5 + * ELSE + * IF (certificates is present) AND + * (any version 2 attribute certificates are present) + * THEN version MUST be 4 + * ELSE + * IF ((certificates is present) AND + * (any version 1 attribute certificates are present)) OR + * (any SignerInfo structures are version 3) OR + * (encapContentInfo eContentType is other than id-data) + * THEN version MUST be 3 + * ELSE version MUST be 1 + * </pre> + * <p> + * @todo Check possible update for this to RFC 5652 level + */ +public class SignedData + extends ASN1Object +{ + private static final ASN1Integer VERSION_1 = new ASN1Integer(1); + private static final ASN1Integer VERSION_3 = new ASN1Integer(3); + private static final ASN1Integer VERSION_4 = new ASN1Integer(4); + private static final ASN1Integer VERSION_5 = new ASN1Integer(5); + + private ASN1Integer version; + private ASN1Set digestAlgorithms; + private ContentInfo contentInfo; + private ASN1Set certificates; + private ASN1Set crls; + private ASN1Set signerInfos; + private boolean certsBer; + private boolean crlsBer; + + /** + * Return a SignedData object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link SignedData} object + * <li> {@link org.spongycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with SignedData structure inside + * </ul> + * + * @param o the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static SignedData getInstance( + Object o) + { + if (o instanceof SignedData) + { + return (SignedData)o; + } + else if (o != null) + { + return new SignedData(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public SignedData( + ASN1Set digestAlgorithms, + ContentInfo contentInfo, + ASN1Set certificates, + ASN1Set crls, + ASN1Set signerInfos) + { + this.version = calculateVersion(contentInfo.getContentType(), certificates, crls, signerInfos); + this.digestAlgorithms = digestAlgorithms; + this.contentInfo = contentInfo; + this.certificates = certificates; + this.crls = crls; + this.signerInfos = signerInfos; + this.crlsBer = crls instanceof BERSet; + this.certsBer = certificates instanceof BERSet; + } + + + private ASN1Integer calculateVersion( + ASN1ObjectIdentifier contentOid, + ASN1Set certs, + ASN1Set crls, + ASN1Set signerInfs) + { + boolean otherCert = false; + boolean otherCrl = false; + boolean attrCertV1Found = false; + boolean attrCertV2Found = false; + + if (certs != null) + { + for (Enumeration en = certs.getObjects(); en.hasMoreElements();) + { + Object obj = en.nextElement(); + if (obj instanceof ASN1TaggedObject) + { + ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(obj); + + if (tagged.getTagNo() == 1) + { + attrCertV1Found = true; + } + else if (tagged.getTagNo() == 2) + { + attrCertV2Found = true; + } + else if (tagged.getTagNo() == 3) + { + otherCert = true; + } + } + } + } + + if (otherCert) + { + return new ASN1Integer(5); + } + + if (crls != null) // no need to check if otherCert is true + { + for (Enumeration en = crls.getObjects(); en.hasMoreElements();) + { + Object obj = en.nextElement(); + if (obj instanceof ASN1TaggedObject) + { + otherCrl = true; + } + } + } + + if (otherCrl) + { + return VERSION_5; + } + + if (attrCertV2Found) + { + return VERSION_4; + } + + if (attrCertV1Found) + { + return VERSION_3; + } + + if (checkForVersion3(signerInfs)) + { + return VERSION_3; + } + + if (!CMSObjectIdentifiers.data.equals(contentOid)) + { + return VERSION_3; + } + + return VERSION_1; + } + + private boolean checkForVersion3(ASN1Set signerInfs) + { + for (Enumeration e = signerInfs.getObjects(); e.hasMoreElements();) + { + SignerInfo s = SignerInfo.getInstance(e.nextElement()); + + if (s.getVersion().getValue().intValue() == 3) + { + return true; + } + } + + return false; + } + + private SignedData( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + version = ASN1Integer.getInstance(e.nextElement()); + digestAlgorithms = ((ASN1Set)e.nextElement()); + contentInfo = ContentInfo.getInstance(e.nextElement()); + + while (e.hasMoreElements()) + { + ASN1Primitive o = (ASN1Primitive)e.nextElement(); + + // + // an interesting feature of SignedData is that there appear + // to be varying implementations... + // for the moment we ignore anything which doesn't fit. + // + if (o instanceof ASN1TaggedObject) + { + ASN1TaggedObject tagged = (ASN1TaggedObject)o; + + switch (tagged.getTagNo()) + { + case 0: + certsBer = tagged instanceof BERTaggedObject; + certificates = ASN1Set.getInstance(tagged, false); + break; + case 1: + crlsBer = tagged instanceof BERTaggedObject; + crls = ASN1Set.getInstance(tagged, false); + break; + default: + throw new IllegalArgumentException("unknown tag value " + tagged.getTagNo()); + } + } + else + { + signerInfos = (ASN1Set)o; + } + } + } + + public ASN1Integer getVersion() + { + return version; + } + + public ASN1Set getDigestAlgorithms() + { + return digestAlgorithms; + } + + public ContentInfo getEncapContentInfo() + { + return contentInfo; + } + + public ASN1Set getCertificates() + { + return certificates; + } + + public ASN1Set getCRLs() + { + return crls; + } + + public ASN1Set getSignerInfos() + { + return signerInfos; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(version); + v.add(digestAlgorithms); + v.add(contentInfo); + + if (certificates != null) + { + if (certsBer) + { + v.add(new BERTaggedObject(false, 0, certificates)); + } + else + { + v.add(new DERTaggedObject(false, 0, certificates)); + } + } + + if (crls != null) + { + if (crlsBer) + { + v.add(new BERTaggedObject(false, 1, crls)); + } + else + { + v.add(new DERTaggedObject(false, 1, crls)); + } + } + + v.add(signerInfos); + + return new BERSequence(v); + } +} |