This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[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                                 ASN1 signatureAlgo = tbsCertificate.Element (tbs++, 0x30); 
131                 
132                                 ASN1 issuer = tbsCertificate.Element (tbs++, 0x30); 
133                                 m_issuername = X501.ToString (issuer);
134                 
135                                 ASN1 validity = tbsCertificate.Element (tbs++, 0x30);
136                                 ASN1 notBefore = validity [0];
137                                 m_from = ASN1Convert.ToDateTime (notBefore);
138                                 ASN1 notAfter = validity [1];
139                                 m_until = ASN1Convert.ToDateTime (notAfter);
140                 
141                                 ASN1 subject = tbsCertificate.Element (tbs++, 0x30);
142                                 m_subject = X501.ToString (subject);
143                 
144                                 ASN1 subjectPublicKeyInfo = tbsCertificate.Element (tbs++, 0x30);
145                 
146                                 ASN1 algorithm = subjectPublicKeyInfo.Element (0, 0x30);
147                                 ASN1 algo = algorithm.Element (0, 0x06);
148                                 m_keyalgo = ASN1Convert.ToOid (algo);
149                                 // parameters ANY DEFINED BY algorithm OPTIONAL
150                                 // so we dont ask for a specific (Element) type and return DER
151                                 ASN1 parameters = algorithm [1];
152                                 m_keyalgoparams = ((algorithm.Count > 1) ? parameters.GetBytes () : null);
153                 
154                                 ASN1 subjectPublicKey = subjectPublicKeyInfo.Element (1, 0x03); 
155                                 // we must drop th first byte (which is the number of unused bits
156                                 // in the BITSTRING)
157                                 int n = subjectPublicKey.Length - 1;
158                                 m_publickey = new byte [n];
159                                 Buffer.BlockCopy (subjectPublicKey.Value, 1, m_publickey, 0, n);
160
161                                 // signature processing
162                                 byte[] bitstring = decoder [2].Value;
163                                 // first byte contains unused bits in first byte
164                                 signature = new byte [bitstring.Length - 1];
165                                 Buffer.BlockCopy (bitstring, 1, signature, 0, signature.Length);
166
167                                 algorithm = decoder [1];
168                                 algo = algorithm.Element (0, 0x06);
169                                 m_signaturealgo = ASN1Convert.ToOid (algo);
170                                 parameters = algorithm [1];
171                                 if (parameters != null)
172                                         m_signaturealgoparams = parameters.GetBytes ();
173                                 else
174                                         m_signaturealgoparams = null;
175
176                                 // Certificate / TBSCertificate / issuerUniqueID
177                                 ASN1 issuerUID = tbsCertificate.Element (tbs, 0xA1);
178                                 if (issuerUID != null) {
179                                         tbs++;
180                                         issuerUniqueID = issuerUID.Value;
181                                 }
182
183                                 // Certificate / TBSCertificate / subjectUniqueID
184                                 ASN1 subjectUID = tbsCertificate.Element (tbs, 0xA2);
185                                 if (subjectUID != null) {
186                                         tbs++;
187                                         subjectUniqueID = subjectUID.Value;
188                                 }
189
190                                 // Certificate / TBSCertificate / Extensions
191                                 ASN1 extns = tbsCertificate.Element (tbs, 0xA3);
192                                 if ((extns != null) && (extns.Count == 1))
193                                         extensions = new X509ExtensionCollection (extns [0]);
194                                 else
195                                         extensions = new X509ExtensionCollection (null);
196
197                                 // keep a copy of the original data
198                                 m_encodedcert = (byte[]) data.Clone ();
199                         }
200                         catch (Exception ex) {
201                                 throw new CryptographicException (e, ex);
202                         }
203                 }
204
205                 // constructors
206
207                 public X509Certificate (byte[] data) 
208                 {
209                         if (data != null)
210                                 Parse (data);
211                 }
212
213                 private byte[] GetUnsignedBigInteger (byte[] integer) 
214                 {
215                         if (integer [0] == 0x00) {
216                                 // this first byte is added so we're sure it's an unsigned integer
217                                 // however we can't feed it into RSAParameters or DSAParameters
218                                 int length = integer.Length - 1;
219                                 byte[] uinteger = new byte [length];
220                                 Buffer.BlockCopy (integer, 1, uinteger, 0, length);
221                                 return uinteger;
222                         }
223                         else
224                                 return integer;
225                 }
226
227                 // public methods
228
229                 public DSA DSA {
230                         get { 
231                                 DSAParameters dsaParams = new DSAParameters ();
232                                 // for DSA m_publickey contains 1 ASN.1 integer - Y
233                                 ASN1 pubkey = new ASN1 (m_publickey);
234                                 if ((pubkey == null) || (pubkey.Tag != 0x02))
235                                         return null;
236                                 dsaParams.Y = GetUnsignedBigInteger (pubkey.Value);
237
238                                 ASN1 param = new ASN1 (m_keyalgoparams);
239                                 if ((param == null) || (param.Tag != 0x30) || (param.Count < 3))
240                                         return null;
241                                 if ((param [0].Tag != 0x02) || (param [1].Tag != 0x02) || (param [2].Tag != 0x02))
242                                         return null;
243                                 dsaParams.P = GetUnsignedBigInteger (param [0].Value);
244                                 dsaParams.Q = GetUnsignedBigInteger (param [1].Value);
245                                 dsaParams.G = GetUnsignedBigInteger (param [2].Value);
246
247                                 // BUG: MS BCL 1.0 can't import a key which 
248                                 // isn't the same size as the one present in
249                                 // the container.
250                                 DSACryptoServiceProvider dsa = new DSACryptoServiceProvider (dsaParams.Y.Length << 3);
251                                 dsa.ImportParameters (dsaParams);
252                                 return (DSA) dsa; 
253                         }
254                 }
255
256                 public X509ExtensionCollection Extensions {
257                         get { return extensions; }
258                 }
259
260                 public byte[] Hash {
261                         get {
262                                 if (certhash == null) {
263                                         HashAlgorithm hash = null;
264                                         switch (m_signaturealgo) {
265                                                 case "1.2.840.113549.1.1.2":    // MD2 with RSA encryption 
266                                                         // maybe someone installed MD2 ?
267                                                         hash = HashAlgorithm.Create ("MD2");
268                                                         break;
269                                                 case "1.2.840.113549.1.1.4":    // MD5 with RSA encryption 
270                                                         hash = MD5.Create ();
271                                                         break;
272                                                 case "1.2.840.113549.1.1.5":    // SHA-1 with RSA Encryption 
273                                                 case "1.3.14.3.2.29":           // SHA1 with RSA signature 
274                                                 case "1.2.840.10040.4.3":       // SHA1-1 with DSA
275                                                         hash = SHA1.Create ();
276                                                         break;
277                                                 default:
278                                                         return null;
279                                         }
280                                         if ((decoder == null) || (decoder.Count < 1))
281                                                 return null;
282                                         byte[] toBeSigned = decoder [0].GetBytes ();
283                                         certhash = hash.ComputeHash (toBeSigned, 0, toBeSigned.Length);
284                                 }
285                                 return (byte[]) certhash.Clone ();
286                         }
287                 }
288
289                 public virtual string IssuerName {
290                         get { return m_issuername; }
291                 }
292
293                 public virtual string KeyAlgorithm {
294                         get { return m_keyalgo; }
295                 }
296
297                 public virtual byte[] KeyAlgorithmParameters {
298                         get {
299                                 if (m_keyalgoparams == null)
300                                         return null;
301                                 return (byte[]) m_keyalgoparams.Clone (); 
302                         }
303                 }
304
305                 public virtual byte[] PublicKey {
306                         get { 
307                                 if (m_publickey == null)
308                                         return null;
309                                 return (byte[]) m_publickey.Clone ();
310                         }
311                 }
312
313                 public virtual RSA RSA {
314                         get { 
315                                 RSAParameters rsaParams = new RSAParameters ();
316                                 // for RSA m_publickey contains 2 ASN.1 integers
317                                 // the modulus and the public exponent
318                                 ASN1 pubkey = new ASN1 (m_publickey);
319                                 ASN1 modulus = pubkey [0];
320                                 if ((modulus == null) || (modulus.Tag != 0x02))
321                                         return null;
322                                 ASN1 exponent = pubkey [1];
323                                 if (exponent.Tag != 0x02)
324                                         return null;
325
326                                 rsaParams.Modulus = GetUnsignedBigInteger (modulus.Value);
327                                 rsaParams.Exponent = exponent.Value;
328
329                                 // BUG: MS BCL 1.0 can't import a key which 
330                                 // isn't the same size as the one present in
331                                 // the container.
332                                 int keySize = (rsaParams.Modulus.Length << 3);
333                                 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider (keySize);
334                                 rsa.ImportParameters (rsaParams);
335                                 return (RSA)rsa; 
336                         }
337                 }
338                 
339                 public virtual byte[] RawData {
340                         get { return (byte[]) m_encodedcert.Clone (); }
341                 }
342
343                 public virtual byte[] SerialNumber {
344                         get { 
345                                 if (serialnumber == null)
346                                         return null;
347                                 return (byte[]) serialnumber.Clone (); 
348                         }
349                 }
350
351                 public virtual byte[] Signature {
352                         get { 
353                                 if (signature == null)
354                                         return null;
355
356                                 switch (m_signaturealgo) {
357                                         case "1.2.840.113549.1.1.2":    // MD2 with RSA encryption 
358                                         case "1.2.840.113549.1.1.4":    // MD5 with RSA encryption 
359                                         case "1.2.840.113549.1.1.5":    // SHA-1 with RSA Encryption 
360                                         case "1.3.14.3.2.29":           // SHA1 with RSA signature
361                                                 return (byte[]) signature.Clone ();
362
363                                         case "1.2.840.10040.4.3":       // SHA-1 with DSA
364                                                 ASN1 sign = new ASN1 (signature);
365                                                 if ((sign == null) || (sign.Count != 2))
366                                                         return null;
367                                                 // parts may be less than 20 bytes (i.e. first bytes were 0x00)
368                                                 byte[] part1 = sign [0].Value;
369                                                 byte[] part2 = sign [1].Value;
370                                                 byte[] sig = new byte [40];
371                                                 Buffer.BlockCopy (part1, 0, sig, (20 - part1.Length), part1.Length);
372                                                 Buffer.BlockCopy (part2, 0, sig, (40 - part2.Length), part2.Length);
373                                                 return sig;
374
375                                         default:
376                                                 throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo);
377                                 }
378                         }
379                 }
380
381                 public virtual string SignatureAlgorithm {
382                         get { return m_signaturealgo; }
383                 }
384
385                 public virtual byte[] SignatureAlgorithmParameters {
386                         get { 
387                                 if (m_signaturealgoparams == null)
388                                         return m_signaturealgoparams;
389                                 return (byte[]) m_signaturealgoparams.Clone ();
390                         }
391                 }
392
393                 public virtual string SubjectName {
394                         get { return m_subject; }
395                 }
396
397                 public virtual DateTime ValidFrom {
398                         get { return m_from; }
399                 }
400
401                 public virtual DateTime ValidUntil {
402                         get { return m_until; }
403                 }
404
405                 public int Version {
406                         get { return version; }
407                 }
408
409                 public bool IsCurrent {
410                         get { return WasCurrent (DateTime.UtcNow); }
411                 }
412
413                 public bool WasCurrent (DateTime instant) 
414                 {
415                         return ((instant > ValidFrom) && (instant <= ValidUntil));
416                 }
417
418                 internal bool VerifySignature (DSA dsa) 
419                 {
420                         // signatureOID is check by both this.Hash and this.Signature
421                         DSASignatureDeformatter v = new DSASignatureDeformatter (dsa);
422                         // only SHA-1 is supported
423                         v.SetHashAlgorithm ("SHA1");
424                         return v.VerifySignature (this.Hash, this.Signature);
425                 }
426
427                 internal bool VerifySignature (RSA rsa) 
428                 {
429                         RSAPKCS1SignatureDeformatter v = new RSAPKCS1SignatureDeformatter (rsa);
430                         switch (m_signaturealgo) {
431                                 // MD2 with RSA encryption 
432                                 case "1.2.840.113549.1.1.2":
433                                         // maybe someone installed MD2 ?
434                                         v.SetHashAlgorithm ("MD2");
435                                         break;
436                                 // MD5 with RSA encryption 
437                                 case "1.2.840.113549.1.1.4":
438                                         v.SetHashAlgorithm ("MD5");
439                                         break;
440                                 // SHA-1 with RSA Encryption 
441                                 case "1.2.840.113549.1.1.5":
442                                 case "1.3.14.3.2.29":
443                                         v.SetHashAlgorithm ("SHA1");
444                                         break;
445                                 default:
446                                         throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo);
447                         }
448                         return v.VerifySignature (this.Hash, this.Signature);
449                 }
450
451                 public bool VerifySignature (AsymmetricAlgorithm aa) 
452                 {
453                         if (aa == null)
454                                 throw new ArgumentNullException ("aa");
455
456                         if (aa is RSA)
457                                 return VerifySignature (aa as RSA);
458                         else if (aa is DSA)
459                                 return VerifySignature (aa as DSA);
460                         else 
461                                 throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa.ToString ());
462                 }
463
464                 public bool CheckSignature (byte[] hash, string hashAlgorithm, byte[] signature) 
465                 {
466                         RSACryptoServiceProvider r = (RSACryptoServiceProvider) RSA;
467                         return r.VerifyHash (hash, hashAlgorithm, signature);
468                 }
469
470                 public bool IsSelfSigned {
471                         get { 
472                                 if (m_issuername == m_subject)
473                                         return VerifySignature (RSA); 
474                                 else
475                                         return false;
476                         }
477                 }
478         }
479 }