2003-09-01 Sebastien Pouliot <spouliot@videotron.ca>
[mono.git] / mcs / class / Mono.Security / Mono.Security / PKCS7.cs
1 //
2 // PKCS7.cs: PKCS #7 - Cryptographic Message Syntax Standard 
3 //      http://www.rsasecurity.com/rsalabs/pkcs/pkcs-7/index.html
4 //
5 // Author:
6 //      Sebastien Pouliot (spouliot@motus.com)
7 //
8 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
9 //
10
11
12 using System;
13 using System.Collections;
14 using System.Security.Cryptography;
15
16 using Mono.Security.X509;
17
18 namespace Mono.Security {
19
20         public class PKCS7 {
21
22                 // pkcs 1
23                 public const string rsaEncryption = "1.2.840.113549.1.1.1";
24                 // pkcs 7
25                 public const string data = "1.2.840.113549.1.7.1";
26                 public const string signedData = "1.2.840.113549.1.7.2";
27                 // pkcs 9
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";
32
33                 public PKCS7 () {}
34
35                 static public ASN1 Attribute (string oid, ASN1 value) 
36                 {
37                         ASN1 attr = new ASN1 (0x30);
38                         attr.Add (ASN1Convert.FromOID (oid));
39                         ASN1 aset = attr.Add (new ASN1 (0x31));
40                         aset.Add (value);
41                         return attr;
42                 }
43
44                 static public ASN1 AlgorithmIdentifier (string oid)
45                 {
46                         ASN1 ai = new ASN1 (0x30);
47                         ai.Add (ASN1Convert.FromOID (oid));
48                         ai.Add (new ASN1 (0x05));       // NULL
49                         return ai;
50                 }
51
52                 static public ASN1 AlgorithmIdentifier (string oid, ASN1 param) 
53                 {
54                         ASN1 ai = new ASN1 (0x30);
55                         ai.Add (ASN1Convert.FromOID (oid));
56                         ai.Add (param);
57                         return ai;
58                 }
59
60                 /*
61                  * IssuerAndSerialNumber ::= SEQUENCE {
62                  *      issuer Name,
63                  *      serialNumber CertificateSerialNumber 
64                  * }
65                  */
66                 static public ASN1 IssuerAndSerialNumber (X509Certificate x509) 
67                 {
68                         ASN1 issuer = null;
69                         ASN1 serial = null;
70                         ASN1 cert = new ASN1 (x509.RawData);
71                         int tbs = 0;
72                         bool flag = false;
73                         while (tbs < cert[0].Count) {
74                                 ASN1 e = cert[0][tbs++];
75                                 if (e.Tag == 0x02)
76                                         serial = e;
77                                 else if (e.Tag == 0x30) {
78                                         if (flag) {
79                                                 issuer = e;
80                                                 break;
81                                         }
82                                         flag = true;
83                                 }
84                         }
85                         ASN1 iasn = new ASN1 (0x30);
86                         iasn.Add (issuer);
87                         iasn.Add (serial);
88                         return iasn;
89                 }
90
91                 /*
92                  * ContentInfo ::= SEQUENCE {
93                  *      contentType ContentType,
94                  *      content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL 
95                  * }
96                  * ContentType ::= OBJECT IDENTIFIER
97                  */
98                 public class ContentInfo {
99
100                         private string contentType;
101                         private ASN1 content;
102
103                         public ContentInfo () 
104                         {
105                                 content = new ASN1 (0xA0);
106                         }
107
108                         public ContentInfo (string oid) : this ()
109                         {
110                                 contentType = oid;
111                         }
112
113                         public ContentInfo (byte[] data) 
114                                 : this (new ASN1 (data)) {}
115
116                         public ContentInfo (ASN1 asn1) 
117                         {
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");
127                                         content = asn1[1];
128                                 }
129                         }
130
131                         public ASN1 ASN1 {
132                                 get { return GetASN1(); }
133                         }
134
135                         public ASN1 Content {
136                                 get { return content; }
137                         }
138
139                         public string ContentType {
140                                 get { return contentType; }
141                                 set { contentType = value; }
142                         }
143
144                         internal ASN1 GetASN1 () 
145                         {
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);
153                                 return contentInfo;
154                         }
155
156                         public byte[] GetBytes () 
157                         {
158                                 return GetASN1 ().GetBytes ();
159                         }
160                 }
161
162                 /*
163                  * SignedData ::= SEQUENCE {
164                  *      version Version,
165                  *      digestAlgorithms DigestAlgorithmIdentifiers,
166                  *      contentInfo ContentInfo,
167                  *      certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
168                  *      crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
169                  *      signerInfos SignerInfos 
170                  * }
171                  */
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;
179                         private ASN1 mda;
180
181                         public SignedData () 
182                         {
183                                 version = 1;
184                                 contentInfo = new ContentInfo ();
185                                 certs = new X509CertificateCollection ();
186                                 crls = new ArrayList ();
187                                 signerInfo = new SignerInfo ();
188                         }
189
190                         public SignedData (byte[] data) 
191                                 : this (new ASN1 (data)) {}
192
193                         public SignedData (ASN1 asn1) 
194                         {
195                                 if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 4))
196                                         throw new ArgumentException ("Invalid SignedData");
197
198                                 if (asn1[0][0].Tag != 0x02)
199                                         throw new ArgumentException ("Invalid version");
200                                 version = asn1[0][0].Value[0];
201
202                                 // digestInfo
203
204                                 contentInfo = new ContentInfo (asn1[0][2]);
205
206                                 int n = 3;
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 ()));
211                                         n++;
212                                 }
213
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 ());
218                                         n++;
219                                 }
220
221                                 if (asn1[0][n].Count > 0)
222                                         signerInfo = new SignerInfo (asn1[0][n]);
223                                 else
224                                         signerInfo = new SignerInfo ();
225                         }
226
227                         public ASN1 ASN1 {
228                                 get { return GetASN1(); }
229                         }
230
231                         public X509CertificateCollection Certificates {
232                                 get { return certs; }
233                         }
234
235                         public ContentInfo ContentInfo {
236                                 get { return contentInfo; }
237                         }
238
239                         public ArrayList CRLs {
240                                 get { return crls; }
241                         }
242
243                         public string HashName {
244                                 get { return hashAlgorithm; }
245                                 // todo add validation
246                                 set { 
247                                         hashAlgorithm = value; 
248                                         signerInfo.HashName = value;
249                                 }
250                         }
251
252                         public SignerInfo SignerInfo {
253                                 get { return signerInfo; }
254                         }
255
256                         public byte Version {
257                                 get { return version; }
258                                 set { version = value; }
259                         }
260
261                         internal ASN1 GetASN1 () 
262                         {
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));
273                                 }
274
275                                 // contentInfo ContentInfo,
276                                 ASN1 ci = contentInfo.ASN1;
277                                 signedData.Add (ci);
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);
285                                 }
286
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));
292                                 }
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));
298                                 }
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);
303                                 return signedData;
304                         }
305
306                         public byte[] GetBytes () 
307                         {
308                                 return GetASN1 ().GetBytes ();
309                         }
310                 }
311
312                 /*
313                  * SignerInfo ::= SEQUENCE {
314                  *      version Version,
315                  *      issuerAndSerialNumber IssuerAndSerialNumber,
316                  *      digestAlgorithm DigestAlgorithmIdentifier,
317                  *      authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
318                  *      digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
319                  *      encryptedDigest EncryptedDigest,
320                  *      unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL 
321                  * }
322                  */
323                 public class SignerInfo {
324
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;
334
335                         public SignerInfo () 
336                         {
337                                 version = 1;
338                                 authenticatedAttributes = new ArrayList ();
339                                 unauthenticatedAttributes = new ArrayList ();
340                         }
341
342                         public SignerInfo (byte[] data) 
343                                 : this (new ASN1 (data)) {}
344
345                         // TODO: INCOMPLETE
346                         public SignerInfo (ASN1 asn1) : this () 
347                         {
348                                 if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 5))
349                                         throw new ArgumentException ("Invalid SignedData");
350
351                                 // version Version
352                                 if (asn1[0][0].Tag != 0x02)
353                                         throw new ArgumentException ("Invalid version");
354                                 version = asn1[0][0].Value[0];
355
356                                 // issuerAndSerialNumber IssuerAndSerialNumber
357                                 ASN1 issuerAndSerialNumber = asn1 [0][1];
358                                 issuer = X501.ToString (issuerAndSerialNumber [0]);
359                                 serial = issuerAndSerialNumber [1].Value;
360
361                                 // digestAlgorithm DigestAlgorithmIdentifier
362                                 ASN1 digestAlgorithm = asn1 [0][2];
363                                 hashAlgorithm = ASN1Convert.ToOID (digestAlgorithm [0]);
364
365                                 // authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL
366                                 int n = 3;
367                                 ASN1 authAttributes = asn1 [0][n];
368                                 if (authAttributes.Tag == 0xA0) {
369                                         n++;
370                                         for (int i=0; i < authAttributes.Count; i++)
371                                                 authenticatedAttributes.Add (authAttributes [i]);
372                                 }
373
374                                 // digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier
375                                 ASN1 digestEncryptionAlgorithm = asn1 [0][n++];
376                                 string digestEncryptionAlgorithmOid = ASN1Convert.ToOID (digestEncryptionAlgorithm [0]);
377
378                                 // encryptedDigest EncryptedDigest
379                                 ASN1 encryptedDigest = asn1 [0][n++];
380                                 if (encryptedDigest.Tag == 0x04)
381                                         signature = encryptedDigest.Value;
382
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]);
388                                 }
389                         }
390
391                         public string IssuerName {
392                                 get { return issuer; }
393                         }
394
395                         public byte[] SerialNumber {
396                                 get { return (byte[]) serial.Clone(); }
397                         }
398
399                         public ASN1 ASN1 {
400                                 get { return GetASN1(); }
401                         }
402
403                         public ArrayList AuthenticatedAttributes {
404                                 get { return authenticatedAttributes; }
405                         }
406
407                         public X509Certificate Certificate {
408                                 get { return x509; }
409                                 set { x509 = value; }
410                         }
411
412                         public string HashName {
413                                 get { return hashAlgorithm; }
414                                 set { hashAlgorithm = value; }
415                         }
416
417                         public AsymmetricAlgorithm Key {
418                                 get { return key; }
419                                 set { key = value; }
420                         }
421
422                         public byte[] Signature {
423                                 get { return (byte[]) signature.Clone (); }
424                         }
425
426                         public ArrayList UnauthenticatedAttributes {
427                                 get { return unauthenticatedAttributes; }
428                         }
429
430                         public byte Version {
431                                 get { return version; }
432                                 set { version = value; }
433                         }
434
435                         internal ASN1 GetASN1 () 
436                         {
437                                 if ((key == null) || (hashAlgorithm == null))
438                                         return 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)
452                                                 aa.Add (attr);
453                                 }
454                                 // digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
455                                 if (key is RSA) {
456                                         signerInfo.Add (AlgorithmIdentifier (PKCS7.rsaEncryption));
457
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);
465                                 }
466                                 else if (key is DSA) {
467                                         throw new NotImplementedException ("not yet");
468                                 }
469                                 else
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)
477                                                 ua.Add (attr);
478                                 }
479                                 return signerInfo;
480                         }
481
482                         public byte[] GetBytes () 
483                         {
484                                 return GetASN1 ().GetBytes ();
485                         }
486                 }
487         }
488 }