Merge pull request #901 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / corlib / System.Security.Cryptography.X509Certificates / X509Certificate20.cs
1 //
2 // X509Certificate20.cs: Partial class to handle new 2.0-only stuff
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,2008 Novell, Inc (http://www.novell.com)
9 // Copyright 2013 Xamarin Inc.
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System.IO;
32 using System.Runtime.InteropServices;
33 using System.Security.Permissions;
34 using System.Text;
35
36 using Mono.Security;
37 using Mono.Security.X509;
38
39 using System.Runtime.Serialization;
40
41 namespace System.Security.Cryptography.X509Certificates {
42
43         [ComVisible (true)]
44         [MonoTODO ("X509ContentType.SerializedCert isn't supported (anywhere in the class)")]
45         public partial class X509Certificate : IDeserializationCallback, ISerializable {
46                 private string issuer_name;
47                 private string subject_name;
48
49
50                 public X509Certificate ()
51                 {
52                         // this allows an empty certificate to exists
53                 }
54
55                 public X509Certificate (byte[] rawData, string password)
56                 {
57                         Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
58                 }
59
60                 [MonoTODO ("SecureString support is incomplete")]
61                 public X509Certificate (byte[] rawData, SecureString password)
62                 {
63                         Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
64                 }
65
66                 public X509Certificate (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
67                 {
68                         Import (rawData, password, keyStorageFlags);
69                 }
70
71                 [MonoTODO ("SecureString support is incomplete")]
72                 public X509Certificate (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
73                 {
74                         Import (rawData, password, keyStorageFlags);
75                 }
76
77                 public X509Certificate (string fileName)
78                 {
79                         Import (fileName, (string)null, X509KeyStorageFlags.DefaultKeySet);
80                 }
81
82                 public X509Certificate (string fileName, string password)
83                 {
84                         Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
85                 }
86
87                 [MonoTODO ("SecureString support is incomplete")]
88                 public X509Certificate (string fileName, SecureString password)
89                 {
90                         Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
91                 }
92
93                 public X509Certificate (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
94                 {
95                         Import (fileName, password, keyStorageFlags);
96                 }
97
98                 [MonoTODO ("SecureString support is incomplete")]
99                 public X509Certificate (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
100                 {
101                         Import (fileName, password, keyStorageFlags);
102                 }
103
104                 public X509Certificate (SerializationInfo info, StreamingContext context)
105                 {
106                         byte[] raw = (byte[]) info.GetValue ("RawData", typeof (byte[]));
107                         Import (raw, (string)null, X509KeyStorageFlags.DefaultKeySet);
108                 }
109
110
111                 public string Issuer {
112                         get {
113                                 if (x509 == null)
114                                         throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
115
116                                 if (issuer_name == null)
117                                         issuer_name = X501.ToString (x509.GetIssuerName (), true, ", ", true);
118                                 return issuer_name;
119                         }
120                 }
121
122                 public string Subject {
123                         get {
124                                 if (x509 == null)
125                                         throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
126
127                                 if (subject_name == null)
128                                         subject_name = X501.ToString (x509.GetSubjectName (), true, ", ", true);
129                                 return subject_name;
130                         }
131                 }
132
133                 [ComVisible (false)]
134                 public IntPtr Handle {
135                         get { return IntPtr.Zero; }
136                 }
137
138
139                 [ComVisible (false)]
140                 public override bool Equals (object obj) 
141                 {
142                         X509Certificate x = (obj as X509Certificate);
143                         if (x != null)
144                                 return this.Equals (x);
145                         return false;
146                 }
147
148                 [MonoTODO ("X509ContentType.Pfx/Pkcs12 and SerializedCert are not supported")]
149                 [ComVisible (false)]
150                 public virtual byte[] Export (X509ContentType contentType)
151                 {
152                         return Export (contentType, (byte[])null);
153                 }
154
155                 [MonoTODO ("X509ContentType.Pfx/Pkcs12 and SerializedCert are not supported")]
156                 [ComVisible (false)]
157                 public virtual byte[] Export (X509ContentType contentType, string password)
158                 {
159                         byte[] pwd = (password == null) ? null : Encoding.UTF8.GetBytes (password);
160                         return Export (contentType, pwd);
161                 }
162
163                 [MonoTODO ("X509ContentType.Pfx/Pkcs12 and SerializedCert are not supported. SecureString support is incomplete.")]
164                 public virtual byte[] Export (X509ContentType contentType, SecureString password)
165                 {
166                         byte[] pwd = (password == null) ? null : password.GetBuffer ();
167                         return Export (contentType, pwd);
168                 }
169
170                 internal byte[] Export (X509ContentType contentType, byte[] password)
171                 {
172                         if (x509 == null)
173                                 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
174
175                         try {
176                                 switch (contentType) {
177                                 case X509ContentType.Cert:
178                                         return x509.RawData;
179                                 case X509ContentType.Pfx: // this includes Pkcs12
180                                         // TODO
181                                         throw new NotSupportedException ();
182                                 case X509ContentType.SerializedCert:
183                                         // TODO
184                                         throw new NotSupportedException ();
185                                 default:
186                                         string msg = Locale.GetText ("This certificate format '{0}' cannot be exported.", contentType);
187                                         throw new CryptographicException (msg);
188                                 }
189                         }
190                         finally {
191                                 // protect password
192                                 if (password != null)
193                                         Array.Clear (password, 0, password.Length);
194                         }
195                 }
196
197                 [ComVisible (false)]
198                 public virtual void Import (byte[] rawData)
199                 {
200                         Import (rawData, (string)null, X509KeyStorageFlags.DefaultKeySet);
201                 }
202
203                 private Mono.Security.X509.X509Certificate ImportPkcs12 (byte[] rawData, string password)
204                 {
205                         var pfx = (password == null) ? new Mono.Security.X509.PKCS12 (rawData) : new Mono.Security.X509.PKCS12 (rawData, password);
206                         if (pfx.Certificates.Count == 0) {
207                                 // no certificate was found
208                                 return null;
209                         } else if (pfx.Keys.Count == 0) {
210                                 // no key were found - pick the first certificate
211                                 return pfx.Certificates [0];
212                         } else {
213                                 // find the certificate that match the first key
214                                 var keypair = (pfx.Keys [0] as AsymmetricAlgorithm);
215                                 string pubkey = keypair.ToXmlString (false);
216                                 foreach (var c in pfx.Certificates) {
217                                         if ((c.RSA != null) && (pubkey == c.RSA.ToXmlString (false)))
218                                                 return c;
219                                         if ((c.DSA != null) && (pubkey == c.DSA.ToXmlString (false)))
220                                                 return c;
221                                 }
222                                 return pfx.Certificates [0]; // no match, pick first certificate without keys
223                         }
224                 }
225
226                 [MonoTODO ("missing KeyStorageFlags support")]
227                 [ComVisible (false)]
228                 public virtual void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
229                 {
230                         Reset ();
231                         if (password == null) {
232                                 try {
233                                         x509 = new Mono.Security.X509.X509Certificate (rawData);
234                                 }
235                                 catch (Exception e) {
236                                         try {
237                                                 x509 = ImportPkcs12 (rawData, null);
238                                         }
239                                         catch {
240                                                 string msg = Locale.GetText ("Unable to decode certificate.");
241                                                 // inner exception is the original (not second) exception
242                                                 throw new CryptographicException (msg, e);
243                                         }
244                                 }
245                         } else {
246                                 // try PKCS#12
247                                 try {
248                                         x509 = ImportPkcs12 (rawData, password);
249                                 }
250                                 catch {
251                                         // it's possible to supply a (unrequired/unusued) password
252                                         // fix bug #79028
253                                         x509 = new Mono.Security.X509.X509Certificate (rawData);
254                                 }
255                         }
256                 }
257
258                 [MonoTODO ("SecureString support is incomplete")]
259                 public virtual void Import (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
260                 {
261                         Import (rawData, (string)null, keyStorageFlags);
262                 }
263
264                 [ComVisible (false)]
265                 public virtual void Import (string fileName)
266                 {
267                         byte[] rawData = File.ReadAllBytes (fileName);
268                         Import (rawData, (string)null, X509KeyStorageFlags.DefaultKeySet);
269                 }
270
271                 [MonoTODO ("missing KeyStorageFlags support")]
272                 [ComVisible (false)]
273                 public virtual void Import (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
274                 {
275                         byte[] rawData = File.ReadAllBytes (fileName);
276                         Import (rawData, password, keyStorageFlags);
277                 }
278
279                 [MonoTODO ("SecureString support is incomplete, missing KeyStorageFlags support")]
280                 public virtual void Import (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
281                 {
282                         byte[] rawData = File.ReadAllBytes (fileName);
283                         Import (rawData, (string)null, keyStorageFlags);
284                 }
285
286                 void IDeserializationCallback.OnDeserialization (object sender)
287                 {
288                 }
289
290                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
291                 {
292                         // will throw a NRE if info is null (just like MS implementation)
293                         info.AddValue ("RawData", x509.RawData);
294                 }
295
296                 [ComVisible (false)]
297                 public virtual void Reset ()
298                 {
299                         x509 = null;
300                         issuer_name = null;
301                         subject_name = null;
302                         hideDates = false;
303                         cachedCertificateHash = null;
304                 }
305         }
306 }