2005-01-10 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / corlib / Mono.Security.X509 / X509Certificate.cs
1 //
2 // X509Certificates.cs: Handles X.509 certificates.
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 // (C) 2004 Novell (http://www.novell.com) 
9 //
10
11 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System;
35 using System.Security.Cryptography;
36 using SSCX = System.Security.Cryptography.X509Certificates;
37 using System.Text;
38
39 namespace Mono.Security.X509 {
40
41         // References:
42         // a.   Internet X.509 Public Key Infrastructure Certificate and CRL Profile
43         //      http://www.ietf.org/rfc/rfc3280.txt
44         // b.   ITU ASN.1 standards (free download)
45         //      http://www.itu.int/ITU-T/studygroups/com17/languages/
46
47 #if INSIDE_CORLIB
48         internal
49 #else
50         public 
51 #endif
52         class X509Certificate {
53
54                 private ASN1 decoder;
55
56                 private byte[] m_encodedcert;
57                 private DateTime m_from;
58                 private DateTime m_until;
59                 private string m_issuername;
60                 private string m_keyalgo;
61                 private byte[] m_keyalgoparams;
62                 private string m_subject;
63                 private byte[] m_publickey;
64                 private byte[] signature;
65                 private string m_signaturealgo;
66                 private byte[] m_signaturealgoparams;
67                 private byte[] certhash;
68                 
69                 // from http://www.ietf.org/rfc/rfc2459.txt
70                 //
71                 //Certificate  ::=  SEQUENCE  {
72                 //     tbsCertificate       TBSCertificate,
73                 //     signatureAlgorithm   AlgorithmIdentifier,
74                 //     signature            BIT STRING  }
75                 //
76                 //TBSCertificate  ::=  SEQUENCE  {
77                 //     version         [0]  Version DEFAULT v1,
78                 //     serialNumber         CertificateSerialNumber,
79                 //     signature            AlgorithmIdentifier,
80                 //     issuer               Name,
81                 //     validity             Validity,
82                 //     subject              Name,
83                 //     subjectPublicKeyInfo SubjectPublicKeyInfo,
84                 //     issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
85                 //                          -- If present, version shall be v2 or v3
86                 //     subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
87                 //                          -- If present, version shall be v2 or v3
88                 //     extensions      [3]  Extensions OPTIONAL
89                 //                          -- If present, version shall be v3 --  }
90                 private int version;
91                 private byte[] serialnumber;
92
93                 private byte[] issuerUniqueID;
94                 private byte[] subjectUniqueID;
95                 private X509ExtensionCollection extensions;
96
97                 // that's were the real job is!
98                 private void Parse (byte[] data) 
99                 {
100                         string e = "Input data cannot be coded as a valid certificate.";
101                         try {
102                                 decoder = new ASN1 (data);
103                                 // Certificate 
104                                 if (decoder.Tag != 0x30)
105                                         throw new CryptographicException (e);
106                                 // Certificate / TBSCertificate
107                                 if (decoder [0].Tag != 0x30)
108                                         throw new CryptographicException (e);
109
110                                 ASN1 tbsCertificate = decoder [0];
111
112                                 int tbs = 0;
113                                 // Certificate / TBSCertificate / Version
114                                 ASN1 v = decoder [0][tbs];
115                                 version = 1;                    // DEFAULT v1
116                                 if (v.Tag == 0xA0) {
117                                         // version (optional) is present only in v2+ certs
118                                         version += v.Value [0]; // zero based
119                                         tbs++;
120                                 }
121
122                                 // Certificate / TBSCertificate / CertificateSerialNumber
123                                 ASN1 sn = decoder [0][tbs++];
124                                 if (sn.Tag != 0x02) 
125                                         throw new CryptographicException (e);
126                                 serialnumber = sn.Value;
127                                 Array.Reverse (serialnumber, 0, serialnumber.Length);
128                 
129                                 // Certificate / TBSCertificate / AlgorithmIdentifier
130                                 tbs++;
131                                 // ASN1 signatureAlgo = tbsCertificate.Element (tbs++, 0x30); 
132                 
133                                 ASN1 issuer = tbsCertificate.Element (tbs++, 0x30); 
134                                 m_issuername = X501.ToString (issuer);
135                 
136                                 ASN1 validity = tbsCertificate.Element (tbs++, 0x30);
137                                 ASN1 notBefore = validity [0];
138                                 m_from = ASN1Convert.ToDateTime (notBefore);
139                                 ASN1 notAfter = validity [1];
140                                 m_until = ASN1Convert.ToDateTime (notAfter);
141                 
142                                 ASN1 subject = tbsCertificate.Element (tbs++, 0x30);
143                                 m_subject = X501.ToString (subject);
144                 
145                                 ASN1 subjectPublicKeyInfo = tbsCertificate.Element (tbs++, 0x30);
146                 
147                                 ASN1 algorithm = subjectPublicKeyInfo.Element (0, 0x30);
148                                 ASN1 algo = algorithm.Element (0, 0x06);
149                                 m_keyalgo = ASN1Convert.ToOid (algo);
150                                 // parameters ANY DEFINED BY algorithm OPTIONAL
151                                 // so we dont ask for a specific (Element) type and return DER
152                                 ASN1 parameters = algorithm [1];
153                                 m_keyalgoparams = ((algorithm.Count > 1) ? parameters.GetBytes () : null);
154                 
155                                 ASN1 subjectPublicKey = subjectPublicKeyInfo.Element (1, 0x03); 
156                                 // we must drop th first byte (which is the number of unused bits
157                                 // in the BITSTRING)
158                                 int n = subjectPublicKey.Length - 1;
159                                 m_publickey = new byte [n];
160                                 Buffer.BlockCopy (subjectPublicKey.Value, 1, m_publickey, 0, n);
161
162                                 // signature processing
163                                 byte[] bitstring = decoder [2].Value;
164                                 // first byte contains unused bits in first byte
165                                 signature = new byte [bitstring.Length - 1];
166                                 Buffer.BlockCopy (bitstring, 1, signature, 0, signature.Length);
167
168                                 algorithm = decoder [1];
169                                 algo = algorithm.Element (0, 0x06);
170                                 m_signaturealgo = ASN1Convert.ToOid (algo);
171                                 parameters = algorithm [1];
172                                 if (parameters != null)
173                                         m_signaturealgoparams = parameters.GetBytes ();
174                                 else
175                                         m_signaturealgoparams = null;
176
177                                 // Certificate / TBSCertificate / issuerUniqueID
178                                 ASN1 issuerUID = tbsCertificate.Element (tbs, 0xA1);
179                                 if (issuerUID != null) {
180                                         tbs++;
181                                         issuerUniqueID = issuerUID.Value;
182                                 }
183
184                                 // Certificate / TBSCertificate / subjectUniqueID
185                                 ASN1 subjectUID = tbsCertificate.Element (tbs, 0xA2);
186                                 if (subjectUID != null) {
187                                         tbs++;
188                                         subjectUniqueID = subjectUID.Value;
189                                 }
190
191                                 // Certificate / TBSCertificate / Extensions
192                                 ASN1 extns = tbsCertificate.Element (tbs, 0xA3);
193                                 if ((extns != null) && (extns.Count == 1))
194                                         extensions = new X509ExtensionCollection (extns [0]);
195                                 else
196                                         extensions = new X509ExtensionCollection (null);
197
198                                 // keep a copy of the original data
199                                 m_encodedcert = (byte[]) data.Clone ();
200                         }
201                         catch (Exception ex) {
202                                 throw new CryptographicException (e, ex);
203                         }
204                 }
205
206                 // constructors
207
208                 public X509Certificate (byte[] data) 
209                 {
210                         if (data != null)
211                                 Parse (data);
212                 }
213
214                 private byte[] GetUnsignedBigInteger (byte[] integer) 
215                 {
216                         if (integer [0] == 0x00) {
217                                 // this first byte is added so we're sure it's an unsigned integer
218                                 // however we can't feed it into RSAParameters or DSAParameters
219                                 int length = integer.Length - 1;
220                                 byte[] uinteger = new byte [length];
221                                 Buffer.BlockCopy (integer, 1, uinteger, 0, length);
222                                 return uinteger;
223                         }
224                         else
225                                 return integer;
226                 }
227
228                 // public methods
229
230                 public DSA DSA {
231                         get { 
232                                 DSAParameters dsaParams = new DSAParameters ();
233                                 // for DSA m_publickey contains 1 ASN.1 integer - Y
234                                 ASN1 pubkey = new ASN1 (m_publickey);
235                                 if ((pubkey == null) || (pubkey.Tag != 0x02))
236                                         return null;
237                                 dsaParams.Y = GetUnsignedBigInteger (pubkey.Value);
238
239                                 ASN1 param = new ASN1 (m_keyalgoparams);
240                                 if ((param == null) || (param.Tag != 0x30) || (param.Count < 3))
241                                         return null;
242                                 if ((param [0].Tag != 0x02) || (param [1].Tag != 0x02) || (param [2].Tag != 0x02))
243                                         return null;
244                                 dsaParams.P = GetUnsignedBigInteger (param [0].Value);
245                                 dsaParams.Q = GetUnsignedBigInteger (param [1].Value);
246                                 dsaParams.G = GetUnsignedBigInteger (param [2].Value);
247
248                                 // BUG: MS BCL 1.0 can't import a key which 
249                                 // isn't the same size as the one present in
250                                 // the container.
251                                 DSACryptoServiceProvider dsa = new DSACryptoServiceProvider (dsaParams.Y.Length << 3);
252                                 dsa.ImportParameters (dsaParams);
253                                 return (DSA) dsa; 
254                         }
255                 }
256
257                 public X509ExtensionCollection Extensions {
258                         get { return extensions; }
259                 }
260
261                 public byte[] Hash {
262                         get {
263                                 if (certhash == null) {
264                                         HashAlgorithm hash = null;
265                                         switch (m_signaturealgo) {
266                                                 case "1.2.840.113549.1.1.2":    // MD2 with RSA encryption 
267                                                         // maybe someone installed MD2 ?
268                                                         hash = HashAlgorithm.Create ("MD2");
269                                                         break;
270                                                 case "1.2.840.113549.1.1.4":    // MD5 with RSA encryption 
271                                                         hash = MD5.Create ();
272                                                         break;
273                                                 case "1.2.840.113549.1.1.5":    // SHA-1 with RSA Encryption 
274                                                 case "1.3.14.3.2.29":           // SHA1 with RSA signature 
275                                                 case "1.2.840.10040.4.3":       // SHA1-1 with DSA
276                                                         hash = SHA1.Create ();
277                                                         break;
278                                                 default:
279                                                         return null;
280                                         }
281                                         if ((decoder == null) || (decoder.Count < 1))
282                                                 return null;
283                                         byte[] toBeSigned = decoder [0].GetBytes ();
284                                         certhash = hash.ComputeHash (toBeSigned, 0, toBeSigned.Length);
285                                 }
286                                 return (byte[]) certhash.Clone ();
287                         }
288                 }
289
290                 public virtual string IssuerName {
291                         get { return m_issuername; }
292                 }
293
294                 public virtual string KeyAlgorithm {
295                         get { return m_keyalgo; }
296                 }
297
298                 public virtual byte[] KeyAlgorithmParameters {
299                         get {
300                                 if (m_keyalgoparams == null)
301                                         return null;
302                                 return (byte[]) m_keyalgoparams.Clone (); 
303                         }
304                 }
305
306                 public virtual byte[] PublicKey {
307                         get { 
308                                 if (m_publickey == null)
309                                         return null;
310                                 return (byte[]) m_publickey.Clone ();
311                         }
312                 }
313
314                 public virtual RSA RSA {
315                         get { 
316                                 RSAParameters rsaParams = new RSAParameters ();
317                                 // for RSA m_publickey contains 2 ASN.1 integers
318                                 // the modulus and the public exponent
319                                 ASN1 pubkey = new ASN1 (m_publickey);
320                                 ASN1 modulus = pubkey [0];
321                                 if ((modulus == null) || (modulus.Tag != 0x02))
322                                         return null;
323                                 ASN1 exponent = pubkey [1];
324                                 if (exponent.Tag != 0x02)
325                                         return null;
326
327                                 rsaParams.Modulus = GetUnsignedBigInteger (modulus.Value);
328                                 rsaParams.Exponent = exponent.Value;
329
330                                 // BUG: MS BCL 1.0 can't import a key which 
331                                 // isn't the same size as the one present in
332                                 // the container.
333                                 int keySize = (rsaParams.Modulus.Length << 3);
334                                 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider (keySize);
335                                 rsa.ImportParameters (rsaParams);
336                                 return (RSA)rsa; 
337                         }
338                 }
339                 
340                 public virtual byte[] RawData {
341                         get {
342                                 if (m_encodedcert == null)
343                                         return null;
344                                 return (byte[]) m_encodedcert.Clone ();
345                         }
346                 }
347
348                 public virtual byte[] SerialNumber {
349                         get { 
350                                 if (serialnumber == null)
351                                         return null;
352                                 return (byte[]) serialnumber.Clone (); 
353                         }
354                 }
355
356                 public virtual byte[] Signature {
357                         get { 
358                                 if (signature == null)
359                                         return null;
360
361                                 switch (m_signaturealgo) {
362                                         case "1.2.840.113549.1.1.2":    // MD2 with RSA encryption 
363                                         case "1.2.840.113549.1.1.4":    // MD5 with RSA encryption 
364                                         case "1.2.840.113549.1.1.5":    // SHA-1 with RSA Encryption 
365                                         case "1.3.14.3.2.29":           // SHA1 with RSA signature
366                                                 return (byte[]) signature.Clone ();
367
368                                         case "1.2.840.10040.4.3":       // SHA-1 with DSA
369                                                 ASN1 sign = new ASN1 (signature);
370                                                 if ((sign == null) || (sign.Count != 2))
371                                                         return null;
372                                                 // parts may be less than 20 bytes (i.e. first bytes were 0x00)
373                                                 byte[] part1 = sign [0].Value;
374                                                 byte[] part2 = sign [1].Value;
375                                                 byte[] sig = new byte [40];
376                                                 Buffer.BlockCopy (part1, 0, sig, (20 - part1.Length), part1.Length);
377                                                 Buffer.BlockCopy (part2, 0, sig, (40 - part2.Length), part2.Length);
378                                                 return sig;
379
380                                         default:
381                                                 throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo);
382                                 }
383                         }
384                 }
385
386                 public virtual string SignatureAlgorithm {
387                         get { return m_signaturealgo; }
388                 }
389
390                 public virtual byte[] SignatureAlgorithmParameters {
391                         get { 
392                                 if (m_signaturealgoparams == null)
393                                         return m_signaturealgoparams;
394                                 return (byte[]) m_signaturealgoparams.Clone ();
395                         }
396                 }
397
398                 public virtual string SubjectName {
399                         get { return m_subject; }
400                 }
401
402                 public virtual DateTime ValidFrom {
403                         get { return m_from; }
404                 }
405
406                 public virtual DateTime ValidUntil {
407                         get { return m_until; }
408                 }
409
410                 public int Version {
411                         get { return version; }
412                 }
413
414                 public bool IsCurrent {
415                         get { return WasCurrent (DateTime.UtcNow); }
416                 }
417
418                 public bool WasCurrent (DateTime instant) 
419                 {
420                         return ((instant > ValidFrom) && (instant <= ValidUntil));
421                 }
422
423                 internal bool VerifySignature (DSA dsa) 
424                 {
425                         // signatureOID is check by both this.Hash and this.Signature
426                         DSASignatureDeformatter v = new DSASignatureDeformatter (dsa);
427                         // only SHA-1 is supported
428                         v.SetHashAlgorithm ("SHA1");
429                         return v.VerifySignature (this.Hash, this.Signature);
430                 }
431
432                 internal bool VerifySignature (RSA rsa) 
433                 {
434                         RSAPKCS1SignatureDeformatter v = new RSAPKCS1SignatureDeformatter (rsa);
435                         switch (m_signaturealgo) {
436                                 // MD2 with RSA encryption 
437                                 case "1.2.840.113549.1.1.2":
438                                         // maybe someone installed MD2 ?
439                                         v.SetHashAlgorithm ("MD2");
440                                         break;
441                                 // MD5 with RSA encryption 
442                                 case "1.2.840.113549.1.1.4":
443                                         v.SetHashAlgorithm ("MD5");
444                                         break;
445                                 // SHA-1 with RSA Encryption 
446                                 case "1.2.840.113549.1.1.5":
447                                 case "1.3.14.3.2.29":
448                                         v.SetHashAlgorithm ("SHA1");
449                                         break;
450                                 default:
451                                         throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo);
452                         }
453                         return v.VerifySignature (this.Hash, this.Signature);
454                 }
455
456                 public bool VerifySignature (AsymmetricAlgorithm aa) 
457                 {
458                         if (aa == null)
459                                 throw new ArgumentNullException ("aa");
460
461                         if (aa is RSA)
462                                 return VerifySignature (aa as RSA);
463                         else if (aa is DSA)
464                                 return VerifySignature (aa as DSA);
465                         else 
466                                 throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa.ToString ());
467                 }
468
469                 public bool CheckSignature (byte[] hash, string hashAlgorithm, byte[] signature) 
470                 {
471                         RSACryptoServiceProvider r = (RSACryptoServiceProvider) RSA;
472                         return r.VerifyHash (hash, hashAlgorithm, signature);
473                 }
474
475                 public bool IsSelfSigned {
476                         get { 
477                                 if (m_issuername == m_subject)
478                                         return VerifySignature (RSA); 
479                                 else
480                                         return false;
481                         }
482                 }
483         }
484 }