Merge pull request #3626 from lateralusX/jlorenss/win-api-family-support-eglib
[mono.git] / mcs / class / System / System.Security.Cryptography.X509Certificates / X509Certificate2.cs
1 //
2 // System.Security.Cryptography.X509Certificate2 class
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // (C) 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 #if SECURITY_DEP
31
32 #if MONO_SECURITY_ALIAS
33 extern alias MonoSecurity;
34 using MonoSecurity::Mono.Security;
35 using MonoSecurity::Mono.Security.Cryptography;
36 using MX = MonoSecurity::Mono.Security.X509;
37 #else
38 using Mono.Security;
39 using Mono.Security.Cryptography;
40 using MX = Mono.Security.X509;
41 #endif
42
43 #endif
44
45 using System.IO;
46 using System.Text;
47 using System.Collections;
48 using System.Runtime.Serialization;
49
50 namespace System.Security.Cryptography.X509Certificates {
51
52         [Serializable]
53         public class X509Certificate2 : X509Certificate {
54         
55 #if !SECURITY_DEP
56                 // Used in Mono.Security HttpsClientStream
57                 public X509Certificate2 (byte[] rawData)
58                 {
59                 }
60 #endif
61 #if SECURITY_DEP
62                 new internal X509Certificate2Impl Impl {
63                         get {
64                                 var impl2 = base.Impl as X509Certificate2Impl;
65                                 X509Helper2.ThrowIfContextInvalid (impl2);
66                                 return impl2;
67                         }
68                 }
69
70                 string friendlyName = string.Empty;
71
72                 // constructors
73
74                 public X509Certificate2 ()
75                 {
76                 }
77
78                 public X509Certificate2 (byte[] rawData)
79                 {
80                         Import (rawData, (string)null, X509KeyStorageFlags.DefaultKeySet);
81                 }
82
83                 public X509Certificate2 (byte[] rawData, string password)
84                 {
85                         Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
86                 }
87
88                 public X509Certificate2 (byte[] rawData, SecureString password)
89                 {
90                         Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
91                 }
92
93                 public X509Certificate2 (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
94                 {
95                         Import (rawData, password, keyStorageFlags);
96                 }
97
98                 public X509Certificate2 (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
99                 {
100                         Import (rawData, password, keyStorageFlags);
101                 }
102
103                 public X509Certificate2 (string fileName)
104                 {
105                         Import (fileName, String.Empty, X509KeyStorageFlags.DefaultKeySet);
106                 }
107
108                 public X509Certificate2 (string fileName, string password)
109                 {
110                         Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
111                 }
112
113                 public X509Certificate2 (string fileName, SecureString password)
114                 {
115                         Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
116                 }
117
118                 public X509Certificate2 (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
119                 {
120                         Import (fileName, password, keyStorageFlags);
121                 }
122
123                 public X509Certificate2 (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
124                 {
125                         Import (fileName, password, keyStorageFlags);
126                 }
127
128                 public X509Certificate2 (IntPtr handle) : base (handle) 
129                 {
130                         throw new NotImplementedException ();
131                 }
132
133                 public X509Certificate2 (X509Certificate certificate) 
134                         : base (X509Helper2.Import (certificate))
135                 {
136                 }
137
138                 protected X509Certificate2 (SerializationInfo info, StreamingContext context) : base (info, context)
139                 {
140                 }
141
142                 internal X509Certificate2 (X509Certificate2Impl impl)
143                         : base (impl)
144                 {
145                 }
146
147                 // properties
148
149                 public bool Archived {
150                         get { return Impl.Archived; }
151                         set { Impl.Archived = true; }
152                 }
153
154                 public X509ExtensionCollection Extensions {
155                         get { return Impl.Extensions; }
156                 }
157
158                 public string FriendlyName {
159                         get {
160                                 ThrowIfContextInvalid ();
161                                 return friendlyName;
162                         }
163                         set {
164                                 ThrowIfContextInvalid ();
165                                 friendlyName = value;
166                         }
167                 }
168
169                 public bool HasPrivateKey {
170                         get { return Impl.HasPrivateKey; }
171                 }
172
173                 public X500DistinguishedName IssuerName {
174                         get { return Impl.IssuerName; }
175                 } 
176
177                 public DateTime NotAfter {
178                         get { return Impl.GetValidUntil ().ToLocalTime (); }
179                 }
180
181                 public DateTime NotBefore {
182                         get { return Impl.GetValidFrom ().ToLocalTime (); }
183                 }
184
185                 public AsymmetricAlgorithm PrivateKey {
186                         get { return Impl.PrivateKey; }
187                         set { Impl.PrivateKey = value; }
188                 } 
189
190                 public PublicKey PublicKey {
191                         get { return Impl.PublicKey; }
192                 } 
193
194                 public byte[] RawData {
195                         get { return GetRawCertData (); }
196                 }
197
198                 public string SerialNumber {
199                         get { return GetSerialNumberString (); }
200                 } 
201
202                 public Oid SignatureAlgorithm {
203                         get { return Impl.SignatureAlgorithm; }
204                 } 
205
206                 public X500DistinguishedName SubjectName {
207                         get { return Impl.SubjectName; }
208                 } 
209
210                 public string Thumbprint {
211                         get { return GetCertHashString (); }
212                 } 
213
214                 public int Version {
215                         get { return Impl.Version; }
216                 }
217
218                 // methods
219
220                 [MonoTODO ("always return String.Empty for UpnName, DnsFromAlternativeName and UrlName")]
221                 public string GetNameInfo (X509NameType nameType, bool forIssuer) 
222                 {
223                         return Impl.GetNameInfo (nameType, forIssuer);
224                 }
225
226                 public override void Import (byte[] rawData) 
227                 {
228                         Import (rawData, (string)null, X509KeyStorageFlags.DefaultKeySet);
229                 }
230
231                 [MonoTODO ("missing KeyStorageFlags support")]
232                 public override void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
233                 {
234                         var impl = X509Helper2.Import (rawData, password, keyStorageFlags);
235                         ImportHandle (impl);
236                 }
237
238                 [MonoTODO ("SecureString is incomplete")]
239                 public override void Import (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
240                 {
241                         Import (rawData, (string) null, keyStorageFlags);
242                 }
243
244                 public override void Import (string fileName) 
245                 {
246                         byte[] rawData = File.ReadAllBytes (fileName);
247                         Import (rawData, (string)null, X509KeyStorageFlags.DefaultKeySet);
248                 }
249
250                 [MonoTODO ("missing KeyStorageFlags support")]
251                 public override void Import (string fileName, string password, X509KeyStorageFlags keyStorageFlags) 
252                 {
253                         byte[] rawData = File.ReadAllBytes (fileName);
254                         Import (rawData, password, keyStorageFlags);
255                 }
256
257                 [MonoTODO ("SecureString is incomplete")]
258                 public override void Import (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags) 
259                 {
260                         byte[] rawData = File.ReadAllBytes (fileName);
261                         Import (rawData, (string)null, keyStorageFlags);
262                 }
263
264                 [MonoTODO ("X509ContentType.SerializedCert is not supported")]
265                 public override byte[] Export (X509ContentType contentType, string password)
266                 {
267                         return Impl.Export (contentType, password);
268                 }
269
270                 public override void Reset () 
271                 {
272                         friendlyName = string.Empty;
273                         base.Reset ();
274                 }
275
276                 public override string ToString ()
277                 {
278                         if (!IsValid)
279                                 return "System.Security.Cryptography.X509Certificates.X509Certificate2";
280                         return base.ToString (true);
281                 }
282
283                 public override string ToString (bool verbose)
284                 {
285                         if (!IsValid)
286                                 return "System.Security.Cryptography.X509Certificates.X509Certificate2";
287
288                         // the non-verbose X509Certificate2 == verbose X509Certificate
289                         if (!verbose)
290                                 return base.ToString (true);
291
292                         string nl = Environment.NewLine;
293                         StringBuilder sb = new StringBuilder ();
294                         sb.AppendFormat ("[Version]{0}  V{1}{0}{0}", nl, Version);
295                         sb.AppendFormat ("[Subject]{0}  {1}{0}{0}", nl, Subject);
296                         sb.AppendFormat ("[Issuer]{0}  {1}{0}{0}", nl, Issuer);
297                         sb.AppendFormat ("[Serial Number]{0}  {1}{0}{0}", nl, SerialNumber);
298                         sb.AppendFormat ("[Not Before]{0}  {1}{0}{0}", nl, NotBefore);
299                         sb.AppendFormat ("[Not After]{0}  {1}{0}{0}", nl, NotAfter);
300                         sb.AppendFormat ("[Thumbprint]{0}  {1}{0}{0}", nl, Thumbprint);
301                         sb.AppendFormat ("[Signature Algorithm]{0}  {1}({2}){0}{0}", nl, SignatureAlgorithm.FriendlyName, 
302                                 SignatureAlgorithm.Value);
303
304                         AsymmetricAlgorithm key = PublicKey.Key;
305                         sb.AppendFormat ("[Public Key]{0}  Algorithm: ", nl);
306                         if (key is RSA)
307                                 sb.Append ("RSA");
308                         else if (key is DSA)
309                                 sb.Append ("DSA");
310                         else
311                                 sb.Append (key.ToString ());
312                         sb.AppendFormat ("{0}  Length: {1}{0}  Key Blob: ", nl, key.KeySize);
313                         AppendBuffer (sb, PublicKey.EncodedKeyValue.RawData);
314                         sb.AppendFormat ("{0}  Parameters: ", nl);
315                         AppendBuffer (sb, PublicKey.EncodedParameters.RawData);
316                         sb.Append (nl);
317
318                         return sb.ToString ();
319                 }
320
321                 private static void AppendBuffer (StringBuilder sb, byte[] buffer)
322                 {
323                         if (buffer == null)
324                                 return;
325                         for (int i=0; i < buffer.Length; i++) {
326                                 sb.Append (buffer [i].ToString ("x2"));
327                                 if (i < buffer.Length - 1)
328                                         sb.Append (" ");
329                         }
330                 }
331
332                 [MonoTODO ("by default this depends on the incomplete X509Chain")]
333                 public bool Verify ()
334                 {
335                         return Impl.Verify (this);
336                 }
337
338                 // static methods
339
340                 private static byte[] signedData = new byte[] { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02 };
341
342                 [MonoTODO ("Detection limited to Cert, Pfx, Pkcs12, Pkcs7 and Unknown")]
343                 public static X509ContentType GetCertContentType (byte[] rawData)
344                 {
345                         if ((rawData == null) || (rawData.Length == 0))
346                                 throw new ArgumentException ("rawData");
347
348                         X509ContentType type = X509ContentType.Unknown;
349                         try {
350                                 ASN1 data = new ASN1 (rawData);
351                                 if (data.Tag != 0x30) {
352                                         string msg = Locale.GetText ("Unable to decode certificate.");
353                                         throw new CryptographicException (msg);
354                                 }
355
356                                 if (data.Count == 0)
357                                         return type;
358
359                                 if (data.Count == 3) {
360                                         switch (data [0].Tag) {
361                                         case 0x30:
362                                                 // SEQUENCE / SEQUENCE / BITSTRING
363                                                 if ((data [1].Tag == 0x30) && (data [2].Tag == 0x03))
364                                                         type = X509ContentType.Cert;
365                                                 break;
366                                         case 0x02:
367                                                 // INTEGER / SEQUENCE / SEQUENCE
368                                                 if ((data [1].Tag == 0x30) && (data [2].Tag == 0x30))
369                                                         type = X509ContentType.Pkcs12;
370                                                 // note: Pfx == Pkcs12
371                                                 break;
372                                         }
373                                 }
374                                 // check for PKCS#7 (count unknown but greater than 0)
375                                 // SEQUENCE / OID (signedData)
376                                 if ((data [0].Tag == 0x06) && data [0].CompareValue (signedData))
377                                         type = X509ContentType.Pkcs7;
378                         }
379                         catch (Exception e) {
380                                 string msg = Locale.GetText ("Unable to decode certificate.");
381                                 throw new CryptographicException (msg, e);
382                         }
383
384                         return type;
385                 }
386
387                 [MonoTODO ("Detection limited to Cert, Pfx, Pkcs12 and Unknown")]
388                 public static X509ContentType GetCertContentType (string fileName)
389                 {
390                         if (fileName == null)
391                                 throw new ArgumentNullException ("fileName");
392                         if (fileName.Length == 0)
393                                 throw new ArgumentException ("fileName");
394
395                         byte[] data = File.ReadAllBytes (fileName);
396                         return GetCertContentType (data);
397                 }
398
399                 // internal stuff because X509Certificate2 isn't complete enough
400                 // (maybe X509Certificate3 will be better?)
401
402                 [Obsolete ("KILL")]
403                 internal MX.X509Certificate MonoCertificate {
404                         get {
405                                 var monoImpl = Impl as X509Certificate2ImplMono;
406                                 if (monoImpl == null)
407                                         throw new NotSupportedException ();
408                                 return monoImpl.MonoCertificate;
409                         }
410                 }
411
412 #else
413                 // HACK - this ensure the type X509Certificate2 and PrivateKey property exists in the build before
414                 // Mono.Security.dll is built. This is required to get working client certificate in SSL/TLS
415                 public AsymmetricAlgorithm PrivateKey {
416                         get { return null; }
417                 }
418 #endif
419         }
420 }