2 // PKCS7.cs: PKCS #7 - Cryptographic Message Syntax Standard
3 // http://www.rsasecurity.com/rsalabs/pkcs/pkcs-7/index.html
6 // Sebastien Pouliot (spouliot@motus.com)
8 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
13 using System.Collections;
14 using System.Security.Cryptography;
16 using Mono.Security.X509;
18 namespace Mono.Security {
23 public const string rsaEncryption = "1.2.840.113549.1.1.1";
25 public const string data = "1.2.840.113549.1.7.1";
26 public const string signedData = "1.2.840.113549.1.7.2";
28 public const string contentType = "1.2.840.113549.1.9.3";
29 public const string messageDigest = "1.2.840.113549.1.9.4";
30 public const string signingTime = "1.2.840.113549.1.9.5";
31 public const string countersignature = "1.2.840.113549.1.9.6";
35 static public ASN1 Attribute (string oid, ASN1 value)
37 ASN1 attr = new ASN1 (0x30);
38 attr.Add (ASN1Convert.FromOID (oid));
39 ASN1 aset = attr.Add (new ASN1 (0x31));
44 static public ASN1 AlgorithmIdentifier (string oid)
46 ASN1 ai = new ASN1 (0x30);
47 ai.Add (ASN1Convert.FromOID (oid));
48 ai.Add (new ASN1 (0x05)); // NULL
52 static public ASN1 AlgorithmIdentifier (string oid, ASN1 param)
54 ASN1 ai = new ASN1 (0x30);
55 ai.Add (ASN1Convert.FromOID (oid));
61 * IssuerAndSerialNumber ::= SEQUENCE {
63 * serialNumber CertificateSerialNumber
66 static public ASN1 IssuerAndSerialNumber (X509Certificate x509)
70 ASN1 cert = new ASN1 (x509.RawData);
73 while (tbs < cert[0].Count) {
74 ASN1 e = cert[0][tbs++];
77 else if (e.Tag == 0x30) {
85 ASN1 iasn = new ASN1 (0x30);
92 * ContentInfo ::= SEQUENCE {
93 * contentType ContentType,
94 * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
96 * ContentType ::= OBJECT IDENTIFIER
98 public class ContentInfo {
100 private string contentType;
101 private ASN1 content;
103 public ContentInfo ()
105 content = new ASN1 (0xA0);
108 public ContentInfo (string oid) : this ()
113 public ContentInfo (byte[] data)
114 : this (new ASN1 (data)) {}
116 public ContentInfo (ASN1 asn1)
118 // SEQUENCE with 1 or 2 elements
119 if ((asn1.Tag != 0x30) || ((asn1.Count < 1) && (asn1.Count > 2)))
120 throw new ArgumentException ("Invalid ASN1");
121 if (asn1[0].Tag != 0x06)
122 throw new ArgumentException ("Invalid contentType");
123 contentType = ASN1Convert.ToOID (asn1[0]);
124 if (asn1.Count > 1) {
125 if (asn1[1].Tag != 0xA0)
126 throw new ArgumentException ("Invalid content");
132 get { return GetASN1(); }
135 public ASN1 Content {
136 get { return content; }
139 public string ContentType {
140 get { return contentType; }
141 set { contentType = value; }
144 internal ASN1 GetASN1 ()
146 // ContentInfo ::= SEQUENCE {
147 ASN1 contentInfo = new ASN1 (0x30);
148 // contentType ContentType, -> ContentType ::= OBJECT IDENTIFIER
149 contentInfo.Add (ASN1Convert.FromOID (contentType));
150 // content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
151 if ((content != null) && (content.Count > 0))
152 contentInfo.Add (content);
156 public byte[] GetBytes ()
158 return GetASN1 ().GetBytes ();
163 * SignedData ::= SEQUENCE {
165 * digestAlgorithms DigestAlgorithmIdentifiers,
166 * contentInfo ContentInfo,
167 * certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
168 * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
169 * signerInfos SignerInfos
172 public class SignedData {
173 private byte version;
174 private string hashAlgorithm;
175 private ContentInfo contentInfo;
176 private X509CertificateCollection certs;
177 private ArrayList crls;
178 private SignerInfo signerInfo;
184 contentInfo = new ContentInfo ();
185 certs = new X509CertificateCollection ();
186 crls = new ArrayList ();
187 signerInfo = new SignerInfo ();
190 public SignedData (byte[] data)
191 : this (new ASN1 (data)) {}
193 public SignedData (ASN1 asn1)
195 if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 4))
196 throw new ArgumentException ("Invalid SignedData");
198 if (asn1[0][0].Tag != 0x02)
199 throw new ArgumentException ("Invalid version");
200 version = asn1[0][0].Value[0];
204 contentInfo = new ContentInfo (asn1[0][2]);
207 certs = new X509CertificateCollection ();
208 if (asn1[0][n].Tag == 0xA0) {
209 for (int i=0; i < asn1[0][n].Count; i++)
210 certs.Add (new X509Certificate (asn1[0][n][i].GetBytes ()));
214 crls = new ArrayList ();
215 if (asn1[0][n].Tag == 0xA1) {
216 for (int i=0; i < asn1[0][n].Count; i++)
217 crls.Add (asn1[0][n][i].GetBytes ());
221 if (asn1[0][n].Count > 0)
222 signerInfo = new SignerInfo (asn1[0][n]);
224 signerInfo = new SignerInfo ();
228 get { return GetASN1(); }
231 public X509CertificateCollection Certificates {
232 get { return certs; }
235 public ContentInfo ContentInfo {
236 get { return contentInfo; }
239 public ArrayList CRLs {
243 public string HashName {
244 get { return hashAlgorithm; }
245 // todo add validation
247 hashAlgorithm = value;
248 signerInfo.HashName = value;
252 public SignerInfo SignerInfo {
253 get { return signerInfo; }
256 public byte Version {
257 get { return version; }
258 set { version = value; }
261 internal ASN1 GetASN1 ()
263 // SignedData ::= SEQUENCE {
264 ASN1 signedData = new ASN1 (0x30);
265 // version Version -> Version ::= INTEGER
266 byte[] ver = { version };
267 signedData.Add (new ASN1 (0x02, ver));
268 // digestAlgorithms DigestAlgorithmIdentifiers -> DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
269 ASN1 digestAlgorithms = signedData.Add (new ASN1 (0x31));
270 if (hashAlgorithm != null) {
271 string hashOid = CryptoConfig.MapNameToOID (hashAlgorithm);
272 digestAlgorithms.Add (AlgorithmIdentifier (hashOid));
275 // contentInfo ContentInfo,
276 ASN1 ci = contentInfo.ASN1;
278 if ((mda == null) && (hashAlgorithm != null)) {
279 // automatically add the messageDigest authenticated attribute
280 HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm);
281 byte[] idcHash = ha.ComputeHash (ci[1][0].Value);
282 ASN1 md = new ASN1 (0x30);
283 mda = Attribute (messageDigest, md.Add (new ASN1 (0x04, idcHash)));
284 signerInfo.AuthenticatedAttributes.Add (mda);
287 // certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
288 if (certs.Count > 0) {
289 ASN1 a0 = signedData.Add (new ASN1 (0xA0));
290 foreach (X509Certificate x in certs)
291 a0.Add (new ASN1 (x.RawData));
293 // crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
294 if (crls.Count > 0) {
295 ASN1 a1 = signedData.Add (new ASN1 (0xA1));
296 foreach (byte[] crl in crls)
297 a1.Add (new ASN1 (crl));
299 // signerInfos SignerInfos -> SignerInfos ::= SET OF SignerInfo
300 ASN1 signerInfos = signedData.Add (new ASN1 (0x31));
301 if (signerInfo.Key != null)
302 signerInfos.Add (signerInfo.ASN1);
306 public byte[] GetBytes ()
308 return GetASN1 ().GetBytes ();
313 * SignerInfo ::= SEQUENCE {
315 * issuerAndSerialNumber IssuerAndSerialNumber,
316 * digestAlgorithm DigestAlgorithmIdentifier,
317 * authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
318 * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
319 * encryptedDigest EncryptedDigest,
320 * unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
323 public class SignerInfo {
325 private byte version;
326 private X509Certificate x509;
327 private string hashAlgorithm;
328 private AsymmetricAlgorithm key;
329 private ArrayList authenticatedAttributes;
330 private ArrayList unauthenticatedAttributes;
331 private byte[] signature;
332 private string issuer;
333 private byte[] serial;
338 authenticatedAttributes = new ArrayList ();
339 unauthenticatedAttributes = new ArrayList ();
342 public SignerInfo (byte[] data)
343 : this (new ASN1 (data)) {}
346 public SignerInfo (ASN1 asn1) : this ()
348 if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 5))
349 throw new ArgumentException ("Invalid SignedData");
352 if (asn1[0][0].Tag != 0x02)
353 throw new ArgumentException ("Invalid version");
354 version = asn1[0][0].Value[0];
356 // issuerAndSerialNumber IssuerAndSerialNumber
357 ASN1 issuerAndSerialNumber = asn1 [0][1];
358 issuer = X501.ToString (issuerAndSerialNumber [0]);
359 serial = issuerAndSerialNumber [1].Value;
361 // digestAlgorithm DigestAlgorithmIdentifier
362 ASN1 digestAlgorithm = asn1 [0][2];
363 hashAlgorithm = ASN1Convert.ToOID (digestAlgorithm [0]);
365 // authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL
367 ASN1 authAttributes = asn1 [0][n];
368 if (authAttributes.Tag == 0xA0) {
370 for (int i=0; i < authAttributes.Count; i++)
371 authenticatedAttributes.Add (authAttributes [i]);
374 // digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier
375 ASN1 digestEncryptionAlgorithm = asn1 [0][n++];
376 string digestEncryptionAlgorithmOid = ASN1Convert.ToOID (digestEncryptionAlgorithm [0]);
378 // encryptedDigest EncryptedDigest
379 ASN1 encryptedDigest = asn1 [0][n++];
380 if (encryptedDigest.Tag == 0x04)
381 signature = encryptedDigest.Value;
383 // unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
384 ASN1 unauthAttributes = asn1 [0][n];
385 if ((unauthAttributes != null) && (unauthAttributes.Tag == 0xA1)) {
386 for (int i=0; i < unauthAttributes.Count; i++)
387 unauthenticatedAttributes.Add (unauthAttributes [i]);
391 public string IssuerName {
392 get { return issuer; }
395 public byte[] SerialNumber {
396 get { return (byte[]) serial.Clone(); }
400 get { return GetASN1(); }
403 public ArrayList AuthenticatedAttributes {
404 get { return authenticatedAttributes; }
407 public X509Certificate Certificate {
409 set { x509 = value; }
412 public string HashName {
413 get { return hashAlgorithm; }
414 set { hashAlgorithm = value; }
417 public AsymmetricAlgorithm Key {
422 public byte[] Signature {
423 get { return (byte[]) signature.Clone (); }
426 public ArrayList UnauthenticatedAttributes {
427 get { return unauthenticatedAttributes; }
430 public byte Version {
431 get { return version; }
432 set { version = value; }
435 internal ASN1 GetASN1 ()
437 if ((key == null) || (hashAlgorithm == null))
439 byte[] ver = { version };
440 ASN1 signerInfo = new ASN1 (0x30);
441 // version Version -> Version ::= INTEGER
442 signerInfo.Add (new ASN1 (0x02, ver));
443 // issuerAndSerialNumber IssuerAndSerialNumber,
444 signerInfo.Add (PKCS7.IssuerAndSerialNumber (x509));
445 // digestAlgorithm DigestAlgorithmIdentifier,
446 string hashOid = CryptoConfig.MapNameToOID (hashAlgorithm);
447 signerInfo.Add (AlgorithmIdentifier (hashOid));
448 // authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
449 ASN1 aa = signerInfo.Add (new ASN1 (0xA0));
450 if (authenticatedAttributes.Count > 0) {
451 foreach (ASN1 attr in authenticatedAttributes)
454 // digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
456 signerInfo.Add (AlgorithmIdentifier (PKCS7.rsaEncryption));
458 RSAPKCS1SignatureFormatter r = new RSAPKCS1SignatureFormatter (key);
459 r.SetHashAlgorithm (hashAlgorithm);
460 byte[] tbs = aa.GetBytes ();
461 tbs [0] = 0x31; // not 0xA0 for signature
462 HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm);
463 byte[] tbsHash = ha.ComputeHash (tbs);
464 signature = r.CreateSignature (tbsHash);
466 else if (key is DSA) {
467 throw new NotImplementedException ("not yet");
470 throw new CryptographicException ("Unknown assymetric algorithm");
471 // encryptedDigest EncryptedDigest,
472 signerInfo.Add (new ASN1 (0x04, signature));
473 // unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
474 if (unauthenticatedAttributes.Count > 0) {
475 ASN1 ua = signerInfo.Add (new ASN1 (0xA1));
476 foreach (ASN1 attr in unauthenticatedAttributes)
482 public byte[] GetBytes ()
484 return GetASN1 ().GetBytes ();