Merge pull request #819 from brendanzagaeski/patch-1
[mono.git] / mcs / class / corlib / System.Security.Cryptography.X509Certificates / X509Certificate.cs
1 //
2 // X509Certificate.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 // Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System.IO;
31 using System.Runtime.InteropServices;
32 using System.Security.Permissions;
33 using System.Text;
34
35 using Mono.Security;
36 using Mono.Security.X509;
37
38 using System.Runtime.Serialization;
39 using Mono.Security.Authenticode;
40
41 namespace System.Security.Cryptography.X509Certificates {
42
43         // References:
44         // a.   Internet X.509 Public Key Infrastructure Certificate and CRL Profile
45         //      http://www.ietf.org/rfc/rfc3280.txt
46         
47         // LAMESPEC: the MSDN docs always talks about X509v3 certificates
48         // and/or Authenticode certs. However this class works with older
49         // X509v1 certificates and non-authenticode (code signing) certs.
50         [Serializable]
51 #if NET_2_1
52         public partial class X509Certificate {
53 #else
54         public partial class X509Certificate : IDeserializationCallback, ISerializable {
55 #endif
56                 // typedef struct _CERT_CONTEXT {
57                 //      DWORD                   dwCertEncodingType;
58                 //      BYTE                    *pbCertEncoded;
59                 //      DWORD                   cbCertEncoded;
60                 //      PCERT_INFO              pCertInfo;
61                 //      HCERTSTORE              hCertStore;
62                 // } CERT_CONTEXT, *PCERT_CONTEXT;
63                 // typedef const CERT_CONTEXT *PCCERT_CONTEXT;
64                 [StructLayout (LayoutKind.Sequential)]
65                 internal struct CertificateContext {
66                         public UInt32 dwCertEncodingType;
67                         public IntPtr pbCertEncoded;
68                         public UInt32 cbCertEncoded;
69                         public IntPtr pCertInfo;
70                         public IntPtr hCertStore;
71                 }
72                 // NOTE: We only define the CryptoAPI structure (from WINCRYPT.H)
73                 // so we don't create any dependencies on Windows DLL in corlib
74
75                 private Mono.Security.X509.X509Certificate x509;
76                 private bool hideDates;
77                 private byte[] cachedCertificateHash;
78         
79                 // almost every byte[] returning function has a string equivalent
80                 // sadly the BitConverter insert dash between bytes :-(
81                 private string tostr (byte[] data) 
82                 {
83                         if (data != null) {
84                                 StringBuilder sb = new StringBuilder ();
85                                 for (int i = 0; i < data.Length; i++)
86                                         sb.Append (data[i].ToString ("X2"));
87                                 return sb.ToString ();
88                         }
89                         else
90                                 return null;
91                 }
92         
93                 // static methods
94         
95                 public static X509Certificate CreateFromCertFile (string filename) 
96                 {
97                         byte[] data = File.ReadAllBytes (filename);
98                         return new X509Certificate (data);
99                 }
100
101                 [MonoTODO ("Incomplete - minimal validation in this version")]
102                 public static X509Certificate CreateFromSignedFile (string filename)
103                 {
104                         try {
105                                 AuthenticodeDeformatter a = new AuthenticodeDeformatter (filename);
106                                 if (a.SigningCertificate != null) {
107                                         return new X509Certificate (a.SigningCertificate.RawData);
108                                 }
109                         }
110                         catch (SecurityException) {
111                                 // don't wrap SecurityException into a COMException
112                                 throw;
113                         }
114                         catch (Exception e) {
115                                 string msg = Locale.GetText ("Couldn't extract digital signature from {0}.", filename);
116                                 throw new COMException (msg, e);
117                         }
118                         throw new CryptographicException (Locale.GetText ("{0} isn't signed.", filename));
119                 }
120
121                 // constructors
122         
123                 // special constructor for Publisher (and related classes).
124                 // Dates strings are null
125                 internal X509Certificate (byte[] data, bool dates) 
126                 {
127                         if (data != null) {
128                                 Import (data, (string)null, X509KeyStorageFlags.DefaultKeySet);
129                                 hideDates = !dates;
130                         }
131                 }
132         
133                 public X509Certificate (byte[] data) : this (data, true)
134                 {
135                 }
136         
137                 public X509Certificate (IntPtr handle) 
138                 {
139                         if (handle == IntPtr.Zero)
140                                 throw new ArgumentException ("Invalid handle.");
141 #if NET_2_1
142                         // this works on Windows-only so it's of no use for Moonlight
143                         // even more since this ctor is [SecurityCritical]
144                         throw new NotSupportedException ();
145 #else
146                         InitFromHandle (handle);
147 #endif
148                 }
149
150                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
151                 private void InitFromHandle (IntPtr handle)
152                 {
153                         if (handle != IntPtr.Zero) {
154                                 // both Marshal.PtrToStructure and Marshal.Copy use LinkDemand (so they will always success from here)
155                                 CertificateContext cc = (CertificateContext) Marshal.PtrToStructure (handle, typeof (CertificateContext));
156                                 byte[] data = new byte [cc.cbCertEncoded];
157                                 Marshal.Copy (cc.pbCertEncoded, data, 0, (int)cc.cbCertEncoded);
158                                 x509 = new Mono.Security.X509.X509Certificate (data);
159                         }
160                         // for 1.x IntPtr.Zero results in an "empty" certificate instance
161                 }
162         
163                 public X509Certificate (System.Security.Cryptography.X509Certificates.X509Certificate cert) 
164                 {
165                         if (cert == null)
166                                 throw new ArgumentNullException ("cert");
167
168                         if (cert != null) {
169                                 byte[] data = cert.GetRawCertData ();
170                                 if (data != null)
171                                         x509 = new Mono.Security.X509.X509Certificate (data);
172                                 hideDates = false;
173                         }
174                 }
175
176
177                 // public methods
178         
179                 public virtual bool Equals (System.Security.Cryptography.X509Certificates.X509Certificate other)
180                 {
181                         if (other == null) {
182                                 return false;
183                         } else {
184                                 if (other.x509 == null) {
185                                         if (x509 == null)
186                                                 return true;
187                                         throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
188                                 }
189
190                                 byte[] raw = other.x509.RawData;
191                                 if (raw != null) {
192                                         if (x509 == null)
193                                                 return false;
194                                         if (x509.RawData == null)
195                                                 return false;
196                                         if (raw.Length == x509.RawData.Length) {
197                                                 for (int i = 0; i < raw.Length; i++) {
198                                                         if (raw[i] != x509.RawData [i])
199                                                                 return false;
200                                                 }
201                                                 // well no choice must be equals!
202                                                 return true;
203                                         }
204                                         else
205                                                 return false;
206                                 }
207                         }
208                         return ((x509 == null) || (x509.RawData == null));
209                 }
210         
211                 // LAMESPEC: This is the equivalent of the "thumbprint" that can be seen
212                 // in the certificate viewer of Windows. This is ALWAYS the SHA1 hash of
213                 // the certificate (i.e. it has nothing to do with the actual hash 
214                 // algorithm used to sign the certificate).
215                 public virtual byte[] GetCertHash () 
216                 {
217                         if (x509 == null)
218                                 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
219                         // we'll hash the cert only once and only if required
220                         if ((cachedCertificateHash == null) && (x509 != null)) {
221                                 SHA1 sha = SHA1.Create ();
222                                 cachedCertificateHash = sha.ComputeHash (x509.RawData);
223                         }
224                         return cachedCertificateHash;
225                 }
226         
227                 public virtual string GetCertHashString () 
228                 {
229                         // must call GetCertHash (not variable) or optimization wont work
230                         return tostr (GetCertHash ());
231                 }
232         
233                 // strangly there are no DateTime returning function
234                 public virtual string GetEffectiveDateString ()
235                 {
236                         if (hideDates)
237                                 return null;
238                         if (x509 == null)
239                                 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
240
241                         return x509.ValidFrom.ToLocalTime ().ToString ();
242                 }
243         
244                 // strangly there are no DateTime returning function
245                 public virtual string GetExpirationDateString () 
246                 {
247                         if (hideDates)
248                                 return null;
249                         if (x509 == null)
250                                 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
251
252                         return x509.ValidUntil.ToLocalTime ().ToString ();
253                 }
254         
255                 // well maybe someday there'll be support for PGP or SPKI ?
256                 public virtual string GetFormat () 
257                 {
258                         return "X509";  // DO NOT TRANSLATE
259                 }
260         
261                 public override int GetHashCode ()
262                 {
263                         if (x509 == null)
264                                 return 0;
265                         // the cert hash may not be (yet) calculated
266                         if (cachedCertificateHash == null)
267                                 GetCertHash();
268                 
269                         // return the integer of the first 4 bytes of the cert hash
270                         if ((cachedCertificateHash != null) && (cachedCertificateHash.Length >= 4))
271                                 return ((cachedCertificateHash[0] << 24) |(cachedCertificateHash[1] << 16) |
272                                         (cachedCertificateHash[2] << 8) | cachedCertificateHash[3]);
273                         else
274                                 return 0;
275                 }
276
277                 [Obsolete ("Use the Issuer property.")]
278                 public virtual string GetIssuerName () 
279                 {
280                         if (x509 == null)
281                                 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
282                         return x509.IssuerName;
283                 }
284         
285                 public virtual string GetKeyAlgorithm () 
286                 {
287                         if (x509 == null)
288                                 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
289                         return x509.KeyAlgorithm;
290                 }
291         
292                 public virtual byte[] GetKeyAlgorithmParameters () 
293                 {
294                         if (x509 == null)
295                                 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
296
297                         byte[] kap = x509.KeyAlgorithmParameters;
298                         if (kap == null)
299                                 throw new CryptographicException (Locale.GetText ("Parameters not part of the certificate"));
300
301                         return kap;
302                 }
303         
304                 public virtual string GetKeyAlgorithmParametersString () 
305                 {
306                         return tostr (GetKeyAlgorithmParameters ());
307                 }
308         
309                 [Obsolete ("Use the Subject property.")]
310                 public virtual string GetName ()
311                 {
312                         if (x509 == null)
313                                 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
314                         return x509.SubjectName;
315                 }
316         
317                 public virtual byte[] GetPublicKey () 
318                 {
319                         if (x509 == null)
320                                 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
321                         return x509.PublicKey;
322                 }
323         
324                 public virtual string GetPublicKeyString () 
325                 {
326                         return tostr (GetPublicKey ());
327                 }
328         
329                 public virtual byte[] GetRawCertData () 
330                 {
331                         if (x509 == null)
332                                 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
333                         return x509.RawData;
334                 }
335         
336                 public virtual string GetRawCertDataString () 
337                 {
338                         if (x509 == null)
339                                 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
340                         return tostr (x509.RawData);
341                 }
342         
343                 public virtual byte[] GetSerialNumber () 
344                 {
345                         if (x509 == null)
346                                 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
347                         return x509.SerialNumber;
348                 }
349         
350                 public virtual string GetSerialNumberString () 
351                 {
352                         byte[] sn = GetSerialNumber ();
353                         Array.Reverse (sn);
354                         return tostr (sn);
355                 }
356         
357                 // to please corcompare ;-)
358                 public override string ToString () 
359                 {
360                         return base.ToString ();
361                 }
362         
363                 public virtual string ToString (bool fVerbose) 
364                 {
365                         if (!fVerbose || (x509 == null))
366                                 return base.ToString ();
367
368                         string nl = Environment.NewLine;
369                         StringBuilder sb = new StringBuilder ();
370                         sb.AppendFormat ("[Subject]{0}  {1}{0}{0}", nl, Subject);
371                         sb.AppendFormat ("[Issuer]{0}  {1}{0}{0}", nl, Issuer);
372                         sb.AppendFormat ("[Not Before]{0}  {1}{0}{0}", nl, GetEffectiveDateString ());
373                         sb.AppendFormat ("[Not After]{0}  {1}{0}{0}", nl, GetExpirationDateString ());
374                         sb.AppendFormat ("[Thumbprint]{0}  {1}{0}", nl, GetCertHashString ());
375                         sb.Append (nl);
376                         return sb.ToString ();
377                 }
378
379 #if NET_4_0
380                 protected static string FormatDate (DateTime date)
381                 {
382                         throw new NotImplementedException ();
383                 }
384 #endif
385         }
386 }