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