// // PKCS7.cs: PKCS #7 - Cryptographic Message Syntax Standard // http://www.rsasecurity.com/rsalabs/pkcs/pkcs-7/index.html // // Authors: // Sebastien Pouliot // Daniel Granath // // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections; using System.Security.Cryptography; namespace MediaBrowser.Server.Mono.Security { public sealed class PKCS7 { public class Oid { // pkcs 1 public const string rsaEncryption = "1.2.840.113549.1.1.1"; // pkcs 7 public const string data = "1.2.840.113549.1.7.1"; public const string signedData = "1.2.840.113549.1.7.2"; public const string envelopedData = "1.2.840.113549.1.7.3"; public const string signedAndEnvelopedData = "1.2.840.113549.1.7.4"; public const string digestedData = "1.2.840.113549.1.7.5"; public const string encryptedData = "1.2.840.113549.1.7.6"; // pkcs 9 public const string contentType = "1.2.840.113549.1.9.3"; public const string messageDigest = "1.2.840.113549.1.9.4"; public const string signingTime = "1.2.840.113549.1.9.5"; public const string countersignature = "1.2.840.113549.1.9.6"; public Oid () { } } private PKCS7 () { } static public ASN1 Attribute (string oid, ASN1 value) { ASN1 attr = new ASN1 (0x30); attr.Add (ASN1Convert.FromOid (oid)); ASN1 aset = attr.Add (new ASN1 (0x31)); aset.Add (value); return attr; } static public ASN1 AlgorithmIdentifier (string oid) { ASN1 ai = new ASN1 (0x30); ai.Add (ASN1Convert.FromOid (oid)); ai.Add (new ASN1 (0x05)); // NULL return ai; } static public ASN1 AlgorithmIdentifier (string oid, ASN1 parameters) { ASN1 ai = new ASN1 (0x30); ai.Add (ASN1Convert.FromOid (oid)); ai.Add (parameters); return ai; } /* * IssuerAndSerialNumber ::= SEQUENCE { * issuer Name, * serialNumber CertificateSerialNumber * } */ static public ASN1 IssuerAndSerialNumber (X509Certificate x509) { ASN1 issuer = null; ASN1 serial = null; ASN1 cert = new ASN1 (x509.RawData); int tbs = 0; bool flag = false; while (tbs < cert[0].Count) { ASN1 e = cert[0][tbs++]; if (e.Tag == 0x02) serial = e; else if (e.Tag == 0x30) { if (flag) { issuer = e; break; } flag = true; } } ASN1 iasn = new ASN1 (0x30); iasn.Add (issuer); iasn.Add (serial); return iasn; } /* * ContentInfo ::= SEQUENCE { * contentType ContentType, * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL * } * ContentType ::= OBJECT IDENTIFIER */ public class ContentInfo { private string contentType; private ASN1 content; public ContentInfo () { content = new ASN1 (0xA0); } public ContentInfo (string oid) : this () { contentType = oid; } public ContentInfo (byte[] data) : this (new ASN1 (data)) {} public ContentInfo (ASN1 asn1) { // SEQUENCE with 1 or 2 elements if ((asn1.Tag != 0x30) || ((asn1.Count < 1) && (asn1.Count > 2))) throw new ArgumentException ("Invalid ASN1"); if (asn1[0].Tag != 0x06) throw new ArgumentException ("Invalid contentType"); contentType = ASN1Convert.ToOid (asn1[0]); if (asn1.Count > 1) { if (asn1[1].Tag != 0xA0) throw new ArgumentException ("Invalid content"); content = asn1[1]; } } public ASN1 ASN1 { get { return GetASN1(); } } public ASN1 Content { get { return content; } set { content = value; } } public string ContentType { get { return contentType; } set { contentType = value; } } internal ASN1 GetASN1 () { // ContentInfo ::= SEQUENCE { ASN1 contentInfo = new ASN1 (0x30); // contentType ContentType, -> ContentType ::= OBJECT IDENTIFIER contentInfo.Add (ASN1Convert.FromOid (contentType)); // content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL if ((content != null) && (content.Count > 0)) contentInfo.Add (content); return contentInfo; } public byte[] GetBytes () { return GetASN1 ().GetBytes (); } } /* * EncryptedData ::= SEQUENCE { * version INTEGER {edVer0(0)} (edVer0), * encryptedContentInfo EncryptedContentInfo * } */ public class EncryptedData { private byte _version; private ContentInfo _content; private ContentInfo _encryptionAlgorithm; private byte[] _encrypted; public EncryptedData () { _version = 0; } public EncryptedData (byte[] data) : this (new ASN1 (data)) { } public EncryptedData (ASN1 asn1) : this () { if ((asn1.Tag != 0x30) || (asn1.Count < 2)) throw new ArgumentException ("Invalid EncryptedData"); if (asn1 [0].Tag != 0x02) throw new ArgumentException ("Invalid version"); _version = asn1 [0].Value [0]; ASN1 encryptedContentInfo = asn1 [1]; if (encryptedContentInfo.Tag != 0x30) throw new ArgumentException ("missing EncryptedContentInfo"); ASN1 contentType = encryptedContentInfo [0]; if (contentType.Tag != 0x06) throw new ArgumentException ("missing EncryptedContentInfo.ContentType"); _content = new ContentInfo (ASN1Convert.ToOid (contentType)); ASN1 contentEncryptionAlgorithm = encryptedContentInfo [1]; if (contentEncryptionAlgorithm.Tag != 0x30) throw new ArgumentException ("missing EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier"); _encryptionAlgorithm = new ContentInfo (ASN1Convert.ToOid (contentEncryptionAlgorithm [0])); _encryptionAlgorithm.Content = contentEncryptionAlgorithm [1]; ASN1 encryptedContent = encryptedContentInfo [2]; if (encryptedContent.Tag != 0x80) throw new ArgumentException ("missing EncryptedContentInfo.EncryptedContent"); _encrypted = encryptedContent.Value; } public ASN1 ASN1 { get { return GetASN1(); } } public ContentInfo ContentInfo { get { return _content; } } public ContentInfo EncryptionAlgorithm { get { return _encryptionAlgorithm; } } public byte[] EncryptedContent { get { if (_encrypted == null) return null; return (byte[]) _encrypted.Clone (); } } public byte Version { get { return _version; } set { _version = value; } } // methods internal ASN1 GetASN1 () { return null; } public byte[] GetBytes () { return GetASN1 ().GetBytes (); } } /* * EnvelopedData ::= SEQUENCE { * version Version, * recipientInfos RecipientInfos, * encryptedContentInfo EncryptedContentInfo * } * * RecipientInfos ::= SET OF RecipientInfo * * EncryptedContentInfo ::= SEQUENCE { * contentType ContentType, * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL * } * * EncryptedContent ::= OCTET STRING * */ public class EnvelopedData { private byte _version; private ContentInfo _content; private ContentInfo _encryptionAlgorithm; private ArrayList _recipientInfos; private byte[] _encrypted; public EnvelopedData () { _version = 0; _content = new ContentInfo (); _encryptionAlgorithm = new ContentInfo (); _recipientInfos = new ArrayList (); } public EnvelopedData (byte[] data) : this (new ASN1 (data)) { } public EnvelopedData (ASN1 asn1) : this () { if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 3)) throw new ArgumentException ("Invalid EnvelopedData"); if (asn1[0][0].Tag != 0x02) throw new ArgumentException ("Invalid version"); _version = asn1[0][0].Value[0]; // recipientInfos ASN1 recipientInfos = asn1 [0][1]; if (recipientInfos.Tag != 0x31) throw new ArgumentException ("missing RecipientInfos"); for (int i=0; i < recipientInfos.Count; i++) { ASN1 recipientInfo = recipientInfos [i]; _recipientInfos.Add (new RecipientInfo (recipientInfo)); } ASN1 encryptedContentInfo = asn1[0][2]; if (encryptedContentInfo.Tag != 0x30) throw new ArgumentException ("missing EncryptedContentInfo"); ASN1 contentType = encryptedContentInfo [0]; if (contentType.Tag != 0x06) throw new ArgumentException ("missing EncryptedContentInfo.ContentType"); _content = new ContentInfo (ASN1Convert.ToOid (contentType)); ASN1 contentEncryptionAlgorithm = encryptedContentInfo [1]; if (contentEncryptionAlgorithm.Tag != 0x30) throw new ArgumentException ("missing EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier"); _encryptionAlgorithm = new ContentInfo (ASN1Convert.ToOid (contentEncryptionAlgorithm [0])); _encryptionAlgorithm.Content = contentEncryptionAlgorithm [1]; ASN1 encryptedContent = encryptedContentInfo [2]; if (encryptedContent.Tag != 0x80) throw new ArgumentException ("missing EncryptedContentInfo.EncryptedContent"); _encrypted = encryptedContent.Value; } public ArrayList RecipientInfos { get { return _recipientInfos; } } public ASN1 ASN1 { get { return GetASN1(); } } public ContentInfo ContentInfo { get { return _content; } } public ContentInfo EncryptionAlgorithm { get { return _encryptionAlgorithm; } } public byte[] EncryptedContent { get { if (_encrypted == null) return null; return (byte[]) _encrypted.Clone (); } } public byte Version { get { return _version; } set { _version = value; } } internal ASN1 GetASN1 () { // SignedData ::= SEQUENCE { ASN1 signedData = new ASN1 (0x30); // version Version -> Version ::= INTEGER /* byte[] ver = { _version }; signedData.Add (new ASN1 (0x02, ver)); // digestAlgorithms DigestAlgorithmIdentifiers -> DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier ASN1 digestAlgorithms = signedData.Add (new ASN1 (0x31)); if (hashAlgorithm != null) { string hashOid = CryptoConfig.MapNameToOid (hashAlgorithm); digestAlgorithms.Add (AlgorithmIdentifier (hashOid)); } // contentInfo ContentInfo, ASN1 ci = contentInfo.ASN1; signedData.Add (ci); if ((mda == null) && (hashAlgorithm != null)) { // automatically add the messageDigest authenticated attribute HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm); byte[] idcHash = ha.ComputeHash (ci[1][0].Value); ASN1 md = new ASN1 (0x30); mda = Attribute (messageDigest, md.Add (new ASN1 (0x04, idcHash))); signerInfo.AuthenticatedAttributes.Add (mda); } // certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, if (certs.Count > 0) { ASN1 a0 = signedData.Add (new ASN1 (0xA0)); foreach (X509Certificate x in certs) a0.Add (new ASN1 (x.RawData)); } // crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, if (crls.Count > 0) { ASN1 a1 = signedData.Add (new ASN1 (0xA1)); foreach (byte[] crl in crls) a1.Add (new ASN1 (crl)); } // signerInfos SignerInfos -> SignerInfos ::= SET OF SignerInfo ASN1 signerInfos = signedData.Add (new ASN1 (0x31)); if (signerInfo.Key != null) signerInfos.Add (signerInfo.ASN1);*/ return signedData; } public byte[] GetBytes () { return GetASN1 ().GetBytes (); } } /* RecipientInfo ::= SEQUENCE { * version Version, * issuerAndSerialNumber IssuerAndSerialNumber, * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, * encryptedKey EncryptedKey * } * * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier * * EncryptedKey ::= OCTET STRING */ public class RecipientInfo { private int _version; private string _oid; private byte[] _key; private byte[] _ski; private string _issuer; private byte[] _serial; public RecipientInfo () {} public RecipientInfo (ASN1 data) { if (data.Tag != 0x30) throw new ArgumentException ("Invalid RecipientInfo"); ASN1 version = data [0]; if (version.Tag != 0x02) throw new ArgumentException ("missing Version"); _version = version.Value [0]; // issuerAndSerialNumber IssuerAndSerialNumber ASN1 subjectIdentifierType = data [1]; if ((subjectIdentifierType.Tag == 0x80) && (_version == 3)) { _ski = subjectIdentifierType.Value; } else { _issuer = X501.ToString (subjectIdentifierType [0]); _serial = subjectIdentifierType [1].Value; } ASN1 keyEncryptionAlgorithm = data [2]; _oid = ASN1Convert.ToOid (keyEncryptionAlgorithm [0]); ASN1 encryptedKey = data [3]; _key = encryptedKey.Value; } public string Oid { get { return _oid; } } public byte[] Key { get { if (_key == null) return null; return (byte[]) _key.Clone (); } } public byte[] SubjectKeyIdentifier { get { if (_ski == null) return null; return (byte[]) _ski.Clone (); } } public string Issuer { get { return _issuer; } } public byte[] Serial { get { if (_serial == null) return null; return (byte[]) _serial.Clone (); } } public int Version { get { return _version; } } } /* * SignedData ::= SEQUENCE { * version Version, * digestAlgorithms DigestAlgorithmIdentifiers, * contentInfo ContentInfo, * certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, * signerInfos SignerInfos * } */ public class SignedData { private byte version; private string hashAlgorithm; private ContentInfo contentInfo; private X509CertificateCollection certs; private ArrayList crls; private SignerInfo signerInfo; private bool mda; private bool signed; public SignedData () { version = 1; contentInfo = new ContentInfo (); certs = new X509CertificateCollection (); crls = new ArrayList (); signerInfo = new SignerInfo (); mda = true; signed = false; } public SignedData (byte[] data) : this (new ASN1 (data)) { } public SignedData (ASN1 asn1) { if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 4)) throw new ArgumentException ("Invalid SignedData"); if (asn1[0][0].Tag != 0x02) throw new ArgumentException ("Invalid version"); version = asn1[0][0].Value[0]; contentInfo = new ContentInfo (asn1[0][2]); int n = 3; certs = new X509CertificateCollection (); if (asn1[0][n].Tag == 0xA0) { for (int i=0; i < asn1[0][n].Count; i++) certs.Add (new X509Certificate (asn1[0][n][i].GetBytes ())); n++; } crls = new ArrayList (); if (asn1[0][n].Tag == 0xA1) { for (int i=0; i < asn1[0][n].Count; i++) crls.Add (asn1[0][n][i].GetBytes ()); n++; } if (asn1[0][n].Count > 0) signerInfo = new SignerInfo (asn1[0][n]); else signerInfo = new SignerInfo (); // Exchange hash algorithm Oid from SignerInfo if (signerInfo.HashName != null) { HashName = OidToName(signerInfo.HashName); } // Check if SignerInfo has authenticated attributes mda = (signerInfo.AuthenticatedAttributes.Count > 0); } public ASN1 ASN1 { get { return GetASN1(); } } public X509CertificateCollection Certificates { get { return certs; } } public ContentInfo ContentInfo { get { return contentInfo; } } public ArrayList Crls { get { return crls; } } public string HashName { get { return hashAlgorithm; } // todo add validation set { hashAlgorithm = value; signerInfo.HashName = value; } } public SignerInfo SignerInfo { get { return signerInfo; } } public byte Version { get { return version; } set { version = value; } } public bool UseAuthenticatedAttributes { get { return mda; } set { mda = value; } } public bool VerifySignature (AsymmetricAlgorithm aa) { if (aa == null) { return false; } RSAPKCS1SignatureDeformatter r = new RSAPKCS1SignatureDeformatter (aa); r.SetHashAlgorithm (hashAlgorithm); HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm); byte[] signature = signerInfo.Signature; byte[] hash = null; if (mda) { ASN1 asn = new ASN1 (0x31); foreach (ASN1 attr in signerInfo.AuthenticatedAttributes) asn.Add (attr); hash = ha.ComputeHash (asn.GetBytes ()); } else { hash = ha.ComputeHash (contentInfo.Content[0].Value); } if (hash != null && signature != null) { return r.VerifySignature (hash, signature); } return false; } internal string OidToName (string oid) { switch (oid) { case "1.3.14.3.2.26" : return "SHA1"; case "1.2.840.113549.2.2" : return "MD2"; case "1.2.840.113549.2.5" : return "MD5"; case "2.16.840.1.101.3.4.1" : return "SHA256"; case "2.16.840.1.101.3.4.2" : return "SHA384"; case "2.16.840.1.101.3.4.3" : return "SHA512"; default : break; } // Unknown Oid return oid; } internal ASN1 GetASN1 () { // SignedData ::= SEQUENCE { ASN1 signedData = new ASN1 (0x30); // version Version -> Version ::= INTEGER byte[] ver = { version }; signedData.Add (new ASN1 (0x02, ver)); // digestAlgorithms DigestAlgorithmIdentifiers -> DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier ASN1 digestAlgorithms = signedData.Add (new ASN1 (0x31)); if (hashAlgorithm != null) { string hashOid = CryptoConfig.MapNameToOID (hashAlgorithm); digestAlgorithms.Add (AlgorithmIdentifier (hashOid)); } // contentInfo ContentInfo, ASN1 ci = contentInfo.ASN1; signedData.Add (ci); if (!signed && (hashAlgorithm != null)) { if (mda) { // Use authenticated attributes for signature // Automatically add the contentType authenticated attribute ASN1 ctattr = Attribute (Oid.contentType, ci[0]); signerInfo.AuthenticatedAttributes.Add (ctattr); // Automatically add the messageDigest authenticated attribute HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm); byte[] idcHash = ha.ComputeHash (ci[1][0].Value); ASN1 md = new ASN1 (0x30); ASN1 mdattr = Attribute (Oid.messageDigest, md.Add (new ASN1 (0x04, idcHash))); signerInfo.AuthenticatedAttributes.Add (mdattr); } else { // Don't use authenticated attributes for signature -- signature is content RSAPKCS1SignatureFormatter r = new RSAPKCS1SignatureFormatter (signerInfo.Key); r.SetHashAlgorithm (hashAlgorithm); HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm); byte[] sig = ha.ComputeHash (ci[1][0].Value); signerInfo.Signature = r.CreateSignature (sig); } signed = true; } // certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, if (certs.Count > 0) { ASN1 a0 = signedData.Add (new ASN1 (0xA0)); foreach (X509Certificate x in certs) a0.Add (new ASN1 (x.RawData)); } // crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, if (crls.Count > 0) { ASN1 a1 = signedData.Add (new ASN1 (0xA1)); foreach (byte[] crl in crls) a1.Add (new ASN1 (crl)); } // signerInfos SignerInfos -> SignerInfos ::= SET OF SignerInfo ASN1 signerInfos = signedData.Add (new ASN1 (0x31)); if (signerInfo.Key != null) signerInfos.Add (signerInfo.ASN1); return signedData; } public byte[] GetBytes () { return GetASN1 ().GetBytes (); } } /* * SignerInfo ::= SEQUENCE { * version Version, * issuerAndSerialNumber IssuerAndSerialNumber, * digestAlgorithm DigestAlgorithmIdentifier, * authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, * encryptedDigest EncryptedDigest, * unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL * } * * For version == 3 issuerAndSerialNumber may be replaced by ... */ public class SignerInfo { private byte version; private X509Certificate x509; private string hashAlgorithm; private AsymmetricAlgorithm key; private ArrayList authenticatedAttributes; private ArrayList unauthenticatedAttributes; private byte[] signature; private string issuer; private byte[] serial; private byte[] ski; public SignerInfo () { version = 1; authenticatedAttributes = new ArrayList (); unauthenticatedAttributes = new ArrayList (); } public SignerInfo (byte[] data) : this (new ASN1 (data)) {} // TODO: INCOMPLETE public SignerInfo (ASN1 asn1) : this () { if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 5)) throw new ArgumentException ("Invalid SignedData"); // version Version if (asn1[0][0].Tag != 0x02) throw new ArgumentException ("Invalid version"); version = asn1[0][0].Value[0]; // issuerAndSerialNumber IssuerAndSerialNumber ASN1 subjectIdentifierType = asn1 [0][1]; if ((subjectIdentifierType.Tag == 0x80) && (version == 3)) { ski = subjectIdentifierType.Value; } else { issuer = X501.ToString (subjectIdentifierType [0]); serial = subjectIdentifierType [1].Value; } // digestAlgorithm DigestAlgorithmIdentifier ASN1 digestAlgorithm = asn1 [0][2]; hashAlgorithm = ASN1Convert.ToOid (digestAlgorithm [0]); // authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL int n = 3; ASN1 authAttributes = asn1 [0][n]; if (authAttributes.Tag == 0xA0) { n++; for (int i=0; i < authAttributes.Count; i++) authenticatedAttributes.Add (authAttributes [i]); } // digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier n++; // ASN1 digestEncryptionAlgorithm = asn1 [0][n++]; // string digestEncryptionAlgorithmOid = ASN1Convert.ToOid (digestEncryptionAlgorithm [0]); // encryptedDigest EncryptedDigest ASN1 encryptedDigest = asn1 [0][n++]; if (encryptedDigest.Tag == 0x04) signature = encryptedDigest.Value; // unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL ASN1 unauthAttributes = asn1 [0][n]; if ((unauthAttributes != null) && (unauthAttributes.Tag == 0xA1)) { for (int i=0; i < unauthAttributes.Count; i++) unauthenticatedAttributes.Add (unauthAttributes [i]); } } public string IssuerName { get { return issuer; } } public byte[] SerialNumber { get { if (serial == null) return null; return (byte[]) serial.Clone (); } } public byte[] SubjectKeyIdentifier { get { if (ski == null) return null; return (byte[]) ski.Clone (); } } public ASN1 ASN1 { get { return GetASN1(); } } public ArrayList AuthenticatedAttributes { get { return authenticatedAttributes; } } public X509Certificate Certificate { get { return x509; } set { x509 = value; } } public string HashName { get { return hashAlgorithm; } set { hashAlgorithm = value; } } public AsymmetricAlgorithm Key { get { return key; } set { key = value; } } public byte[] Signature { get { if (signature == null) return null; return (byte[]) signature.Clone (); } set { if (value != null) { signature = (byte[]) value.Clone (); } } } public ArrayList UnauthenticatedAttributes { get { return unauthenticatedAttributes; } } public byte Version { get { return version; } set { version = value; } } internal ASN1 GetASN1 () { if ((key == null) || (hashAlgorithm == null)) return null; byte[] ver = { version }; ASN1 signerInfo = new ASN1 (0x30); // version Version -> Version ::= INTEGER signerInfo.Add (new ASN1 (0x02, ver)); // issuerAndSerialNumber IssuerAndSerialNumber, signerInfo.Add (PKCS7.IssuerAndSerialNumber (x509)); // digestAlgorithm DigestAlgorithmIdentifier, string hashOid = CryptoConfig.MapNameToOID (hashAlgorithm); signerInfo.Add (AlgorithmIdentifier (hashOid)); // authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, ASN1 aa = null; if (authenticatedAttributes.Count > 0) { aa = signerInfo.Add (new ASN1 (0xA0)); authenticatedAttributes.Sort(new SortedSet ()); foreach (ASN1 attr in authenticatedAttributes) aa.Add (attr); } // digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, if (key is RSA) { signerInfo.Add (AlgorithmIdentifier (PKCS7.Oid.rsaEncryption)); if (aa != null) { // Calculate the signature here; otherwise it must be set from SignedData RSAPKCS1SignatureFormatter r = new RSAPKCS1SignatureFormatter (key); r.SetHashAlgorithm (hashAlgorithm); byte[] tbs = aa.GetBytes (); tbs [0] = 0x31; // not 0xA0 for signature HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm); byte[] tbsHash = ha.ComputeHash (tbs); signature = r.CreateSignature (tbsHash); } } else if (key is DSA) { throw new NotImplementedException ("not yet"); } else throw new CryptographicException ("Unknown assymetric algorithm"); // encryptedDigest EncryptedDigest, signerInfo.Add (new ASN1 (0x04, signature)); // unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL if (unauthenticatedAttributes.Count > 0) { ASN1 ua = signerInfo.Add (new ASN1 (0xA1)); unauthenticatedAttributes.Sort(new SortedSet ()); foreach (ASN1 attr in unauthenticatedAttributes) ua.Add (attr); } return signerInfo; } public byte[] GetBytes () { return GetASN1 ().GetBytes (); } } internal class SortedSet : IComparer { public int Compare (object x, object y) { if (x == null) return (y == null) ? 0 : -1; else if (y == null) return 1; ASN1 xx = x as ASN1; ASN1 yy = y as ASN1; if ((xx == null) || (yy == null)) { throw new ArgumentException (("Invalid objects.")); } byte[] xb = xx.GetBytes (); byte[] yb = yy.GetBytes (); for (int i = 0; i < xb.Length; i++) { if (i == yb.Length) break; if (xb[i] == yb[i]) continue; return (xb[i] < yb[i]) ? -1 : 1; } // The arrays are equal up to the shortest of them. if (xb.Length > yb.Length) return 1; else if (xb.Length < yb.Length) return -1; return 0; } } } }