2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / Microsoft.Web.Services / Microsoft.Web.Services.Security.X509 / X509Certificate.cs
1 //
2 // X509Certificates.cs: Handles (a little better) X509 certificates.
3 //
4 // Author:
5 //      Sebastien Pouliot (spouliot@motus.com)
6 //
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 //
9
10 using System;
11 using System.ComponentModel;
12 using System.Security.Cryptography;
13 using System.Security.Cryptography.X509Certificates;
14
15 using Mono.Security;
16 using MX = Mono.Security.X509;
17 using Mono.Security.X509.Extensions;
18
19 namespace Microsoft.Web.Services.Security.X509 {
20
21         public sealed class X509Certificate : System.Security.Cryptography.X509Certificates.X509Certificate, IDisposable {
22
23                 // do not includes: KeyUsage.keyEncipherment and KeyUsage.keyAgreement (see "OWN.cer")
24                 private static KeyUsages[] dataEncryption = new KeyUsages [3] { KeyUsages.dataEncipherment, KeyUsages.decipherOnly, KeyUsages.encipherOnly };
25                 // do not includes KeyUsage.cRLSign, KeyUsage.keyCertSign
26                 private static KeyUsages[] digitalSignature = new KeyUsages [2] { KeyUsages.digitalSignature, KeyUsages.nonRepudiation };
27
28                 private MX.X509Certificate x509;
29                 private bool m_disposed;
30
31                 public X509Certificate (byte[] rawCertificate) : base (rawCertificate) 
32                 {
33                         x509 = new MX.X509Certificate (rawCertificate);
34                         m_disposed = false;
35                 }
36
37                 public X509Certificate (IntPtr handle) : base (handle) 
38                 {
39                         x509 = new MX.X509Certificate (base.GetRawCertData ());
40                         m_disposed = false;
41                 }
42
43                 ~X509Certificate () 
44                 {
45                         Dispose ();
46                 }
47
48                 // IDisposable
49                 public void Dispose () 
50                 {
51                         if (!m_disposed) {
52                                 // Finalization is now unnecessary
53                                 GC.SuppressFinalize (this);
54                         }
55                         // call base class 
56                         // no need as they all are abstract before us
57                         m_disposed = true;
58                 }
59
60                 // LAMESPEC: Do not confuse with CreateFromCertFile
61                 public static X509Certificate CreateCertFromFile (string fileName) 
62                 {
63                         System.Security.Cryptography.X509Certificates.X509Certificate x = System.Security.Cryptography.X509Certificates.X509Certificate.CreateFromCertFile (fileName);
64                         return new X509Certificate (x.GetRawCertData ());
65                 }
66
67                 // LAMESPEC: Do not confuse with CreateFromSignedFile 
68                 public static X509Certificate CreateCertFromSignedFile (string fileName) 
69                 {
70                         System.Security.Cryptography.X509Certificates.X509Certificate x = System.Security.Cryptography.X509Certificates.X509Certificate.CreateFromSignedFile (fileName);
71                         return new X509Certificate (x.GetRawCertData ());
72                 }
73
74                 // I knew that using string for date were a bad idea!
75                 public bool IsCurrent {
76                         get { return x509.IsCurrent; }
77                 }
78
79                 // Well it seems that DSA certificates aren't popular enough :-(
80                 // Note: Private key isn't available
81                 [MonoTODO("Private key isn't yet supported - requires cert stores")]
82                 public RSA Key {
83                         get {
84                                 throw new Win32Exception (0, "Private key is unavailable (TODO)");
85                         }
86                 }
87
88                 // Well it seems that DSA certificates aren't popular enough :-(
89                 public RSA PublicKey {
90                         get { return x509.RSA; }
91                 }
92
93                 // Just KeyUsage or also ExtendedKeyUsage ?
94                 // it doesn't seems to interpret NetscapeCertType
95                 private bool Supports (KeyUsages[] usages) 
96                 {
97                         // X.509 KeyUsage
98                         MX.X509Extension extn = x509.Extensions ["2.5.29.15"];
99                         if (extn != null) {
100                                 KeyUsageExtension keyUsage = new KeyUsageExtension (extn);
101                                 foreach (KeyUsages usage in usages) {
102                                         if (keyUsage.Support (usage))
103                                                 return true;
104                                 }
105                                 return false;
106                         }
107                         // DEPRECATED keyAttributes
108                         extn = x509.Extensions ["2.5.29.2"];
109                         if (extn != null) {
110                                 KeyAttributesExtension keyAttr = new KeyAttributesExtension (extn);
111                                 foreach (KeyUsages usage in usages) {
112                                         if (keyAttr.Support (usage))
113                                                 return true;
114                                 }
115                                 return false;
116                         }
117                         // key usage isn't specified (so it's not limited)
118                         return true;
119                 }
120
121                 public bool SupportsDataEncryption {
122                         get { 
123                                 // always true for older certificates
124                                 if (x509.Version < 3)
125                                         return true;
126                                 return Supports (dataEncryption); 
127                         }
128                 }
129
130                 public bool SupportsDigitalSignature {
131                         get { 
132                                 // always true for older certificates
133                                 if (x509.Version < 3)
134                                         return true;
135                                 return Supports (digitalSignature); 
136                         }
137                 }
138
139                 public static X509Certificate FromBase64String (string rawString) 
140                 {
141                         byte[] cert = Convert.FromBase64String (rawString);
142                         return new X509Certificate (cert);
143                 }
144
145                 public byte[] GetKeyIdentifier () 
146                 {
147                         // if present in certificate return value of the SubjectKeyIdentifier
148                         MX.X509Extension extn = x509.Extensions ["2.5.29.14"];
149                         if (extn != null) {
150                                 ASN1 bs = new ASN1 (extn.Value.Value);
151                                 return bs.Value;
152                         }
153                         // strangely DEPRECATED keyAttributes isn't used here (like KeyUsage)
154
155                         // if not then we must calculate the SubjectKeyIdentifier ourselve
156                         // Note: MS does that hash on the complete subjectPublicKeyInfo (unlike PKIX)
157                         // http://groups.google.ca/groups?selm=e7RqM%24plCHA.1488%40tkmsftngp02&oe=UTF-8&output=gplain
158                         ASN1 subjectPublicKeyInfo = new ASN1 (0x30);
159                         ASN1 algo = subjectPublicKeyInfo.Add (new ASN1 (0x30));
160                         algo.Add (new ASN1 (CryptoConfig.EncodeOID (x509.KeyAlgorithm)));
161                         // FIXME: does it work for DSA certs (without an 2.5.29.14 extension ?)
162                         algo.Add (new ASN1 (x509.KeyAlgorithmParameters)); 
163                         byte[] pubkey = x509.PublicKey;
164                         byte[] bsvalue = new byte [pubkey.Length + 1]; // add unused bits (0) before the public key
165                         Array.Copy (pubkey, 0, bsvalue, 1, pubkey.Length);
166                         subjectPublicKeyInfo.Add (new ASN1 (0x03, bsvalue));
167                         SHA1 sha = SHA1.Create ();
168                         return sha.ComputeHash (subjectPublicKeyInfo.GetBytes ());
169                 }
170 #if !WSE1
171                 public string GetSubjectAlternativeName () 
172                 {
173                         // if present in certificate return value of the SubjectAltName
174                         MX.X509Extension extn = x509.Extensions ["2.5.29.17"];
175                         if (extn != null)
176                                 return null;
177                         SubjectAltNameExtension altname = new SubjectAltNameExtension (extn);
178                         return ((altname.RFC822.Length > 0) ? altname.RFC822 [0] : null);
179                 }
180 #endif
181                 // overloaded but WHY ?
182                 public override int GetHashCode () 
183                 {
184                         return base.GetHashCode ();
185                 }
186
187                 public string ToBase64String () 
188                 {
189                         return Convert.ToBase64String (base.GetRawCertData ());
190                 }
191         }
192 }