[System.Data.Common] Add IDbColumnSchemaGenerator
[mono.git] / mcs / class / System.Security / System.Security.Cryptography.Xml / EncryptedXml.cs
1 //
2 // EncryptedXml.cs - EncryptedXml implementation for XML Encryption
3 //
4 // Author:
5 //      Tim Coleman (tim@timcoleman.com)
6 //
7 // Copyright (C) Tim Coleman, 2004
8
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
31 using System.Collections;
32 using System.IO;
33 using System.Security.Cryptography;
34 using System.Security.Cryptography.X509Certificates;
35 using System.Security.Policy;
36 using System.Text;
37 using System.Xml;
38
39 namespace System.Security.Cryptography.Xml {
40         public class EncryptedXml {
41
42                 #region Fields
43
44                 public const string XmlEncAES128KeyWrapUrl      = XmlEncNamespaceUrl + "kw-aes128";
45                 public const string XmlEncAES128Url             = XmlEncNamespaceUrl + "aes128-cbc";
46                 public const string XmlEncAES192KeyWrapUrl      = XmlEncNamespaceUrl + "kw-aes192";
47                 public const string XmlEncAES192Url             = XmlEncNamespaceUrl + "aes192-cbc";
48                 public const string XmlEncAES256KeyWrapUrl      = XmlEncNamespaceUrl + "kw-aes256";
49                 public const string XmlEncAES256Url             = XmlEncNamespaceUrl + "aes256-cbc";
50                 public const string XmlEncDESUrl                = XmlEncNamespaceUrl + "des-cbc";
51                 public const string XmlEncElementContentUrl     = XmlEncNamespaceUrl + "Content";
52                 public const string XmlEncElementUrl            = XmlEncNamespaceUrl + "Element";
53                 public const string XmlEncEncryptedKeyUrl       = XmlEncNamespaceUrl + "EncryptedKey";
54                 public const string XmlEncNamespaceUrl          = "http://www.w3.org/2001/04/xmlenc#";
55                 public const string XmlEncRSA15Url              = XmlEncNamespaceUrl + "rsa-1_5";
56                 public const string XmlEncRSAOAEPUrl            = XmlEncNamespaceUrl + "rsa-oaep-mgf1p";
57                 public const string XmlEncSHA256Url             = XmlEncNamespaceUrl + "sha256";
58                 public const string XmlEncSHA512Url             = XmlEncNamespaceUrl + "sha512";
59                 public const string XmlEncTripleDESKeyWrapUrl   = XmlEncNamespaceUrl + "kw-tripledes";
60                 public const string XmlEncTripleDESUrl          = XmlEncNamespaceUrl + "tripledes-cbc";
61
62                 Evidence documentEvidence;
63                 Encoding encoding = Encoding.UTF8;
64                 internal Hashtable keyNameMapping = new Hashtable ();
65                 CipherMode mode = CipherMode.CBC;
66                 PaddingMode padding = PaddingMode.ISO10126;
67                 string recipient;
68                 XmlResolver resolver;
69                 XmlDocument document;
70
71                 #endregion // Fields
72         
73                 #region Constructors
74
75                 [MonoTODO]
76                 public EncryptedXml ()
77                 {
78                 }
79
80                 [MonoTODO]
81                 public EncryptedXml (XmlDocument document)
82                 {
83                         this.document = document;
84                 }
85
86                 [MonoTODO]
87                 public EncryptedXml (XmlDocument document, Evidence evidence)
88                 {
89                         this.document = document;
90                         DocumentEvidence = evidence;
91                 }
92         
93                 #endregion // Constructors
94         
95                 #region Properties
96
97                 public Evidence DocumentEvidence {
98                         get { return documentEvidence; }
99                         set { documentEvidence = value; }
100                 }
101
102                 public Encoding Encoding {
103                         get { return encoding; }
104                         set { encoding = value; }
105                 }
106
107                 public CipherMode Mode {
108                         get { return mode; }
109                         set { mode = value; }
110                 }
111
112                 public PaddingMode Padding {
113                         get { return padding; }
114                         set { padding = value; }
115                 }
116
117                 public string Recipient {
118                         get { return recipient; }
119                         set { recipient = value; }
120                 }
121                 
122                 public XmlResolver Resolver {
123                         get { return resolver; }
124                         set { resolver = value; }
125                 }
126
127                 #endregion // Properties
128
129                 #region Methods
130
131                 public void AddKeyNameMapping (string keyName, object keyObject)
132                 {
133                         keyNameMapping [keyName] = keyObject;
134                 }
135
136                 public void ClearKeyNameMappings ()
137                 {
138                         keyNameMapping.Clear ();
139                 }
140
141                 public byte[] DecryptData (EncryptedData encryptedData, SymmetricAlgorithm symmetricAlgorithm)
142                 {
143                         if (encryptedData == null)
144                                 throw new ArgumentNullException ("encryptedData");
145                         if (symmetricAlgorithm == null)
146                                 throw new ArgumentNullException ("symmetricAlgorithm");
147
148                         PaddingMode bak = symmetricAlgorithm.Padding;
149                         try {
150                                 symmetricAlgorithm.Padding = Padding;
151                                 return Transform (encryptedData.CipherData.CipherValue, symmetricAlgorithm.CreateDecryptor (), symmetricAlgorithm.BlockSize / 8, true);
152                         } finally {
153                                 symmetricAlgorithm.Padding = bak;
154                         }
155                 }
156
157                 public void DecryptDocument ()
158                 {
159                         XmlNodeList nodes = document.GetElementsByTagName ("EncryptedData", XmlEncNamespaceUrl);
160                         foreach (XmlNode node in nodes) {
161                                 EncryptedData encryptedData = new EncryptedData ();
162                                 encryptedData.LoadXml ((XmlElement) node);
163                                 SymmetricAlgorithm symAlg = GetDecryptionKey (encryptedData, encryptedData.EncryptionMethod.KeyAlgorithm);
164                                 ReplaceData ((XmlElement) node, DecryptData (encryptedData, symAlg));
165                         }
166                 }
167
168                 public virtual byte[] DecryptEncryptedKey (EncryptedKey encryptedKey)
169                 {
170                         if (encryptedKey == null)
171                                 throw new ArgumentNullException ("encryptedKey");
172
173                         object keyAlg = null;
174                         foreach (KeyInfoClause innerClause in encryptedKey.KeyInfo) {
175                                 if (innerClause is KeyInfoName) {
176                                         keyAlg = keyNameMapping [((KeyInfoName) innerClause).Value];
177                                         break;
178                                 }
179                         }
180                         switch (encryptedKey.EncryptionMethod.KeyAlgorithm) {
181                         case XmlEncRSA15Url:
182                                 return DecryptKey (encryptedKey.CipherData.CipherValue, (RSA) keyAlg, false);
183                         case XmlEncRSAOAEPUrl:
184                                 return DecryptKey (encryptedKey.CipherData.CipherValue, (RSA) keyAlg, true);
185                         }
186                         return DecryptKey (encryptedKey.CipherData.CipherValue, (SymmetricAlgorithm) keyAlg);
187                 }
188
189                 public static byte[] DecryptKey (byte[] keyData, SymmetricAlgorithm symmetricAlgorithm)
190                 {
191                         if (keyData == null)
192                                 throw new ArgumentNullException ("keyData");
193                         if (symmetricAlgorithm == null)
194                                 throw new ArgumentNullException ("symmetricAlgorithm");
195
196                         if (symmetricAlgorithm is TripleDES)
197                                 return SymmetricKeyWrap.TripleDESKeyWrapDecrypt (symmetricAlgorithm.Key, keyData);
198                         if (symmetricAlgorithm is Rijndael)
199                                 return SymmetricKeyWrap.AESKeyWrapDecrypt (symmetricAlgorithm.Key, keyData);
200                         throw new CryptographicException ("The specified cryptographic transform is not supported.");
201                 }
202
203                 [MonoTODO ("Test this.")]
204                 public static byte[] DecryptKey (byte[] keyData, RSA rsa, bool useOAEP)
205                 {
206                         AsymmetricKeyExchangeDeformatter deformatter = null;
207                         if (useOAEP) 
208                                 deformatter = new RSAOAEPKeyExchangeDeformatter (rsa);
209                         else
210                                 deformatter = new RSAPKCS1KeyExchangeDeformatter (rsa);
211                         return deformatter.DecryptKeyExchange (keyData);
212                 }
213
214                 public EncryptedData Encrypt (XmlElement inputElement, string keyName)
215                 {
216                         // There are two keys of note here.
217                         // 1) KeyAlg: the key-encryption-key is used to wrap a key.  The keyName
218                         //    parameter will give us the KEK.
219                         // 2) SymAlg: A 256-bit AES key will be generated to encrypt the contents.
220                         //    This key will be wrapped using the KEK.
221
222                         SymmetricAlgorithm symAlg = SymmetricAlgorithm.Create ("Rijndael");
223                         symAlg.KeySize = 256;
224                         symAlg.GenerateKey ();
225                         symAlg.GenerateIV ();
226
227                         EncryptedData encryptedData = new EncryptedData ();
228                         EncryptedKey encryptedKey = new EncryptedKey();
229
230                         object keyAlg = keyNameMapping [keyName];
231
232                         encryptedKey.EncryptionMethod = new EncryptionMethod (GetKeyWrapAlgorithmUri (keyAlg));
233
234                         if (keyAlg is RSA)
235                                 encryptedKey.CipherData = new CipherData (EncryptKey (symAlg.Key, (RSA) keyAlg, false));
236                         else
237                                 encryptedKey.CipherData = new CipherData (EncryptKey (symAlg.Key, (SymmetricAlgorithm) keyAlg));
238
239                         encryptedKey.KeyInfo = new KeyInfo();
240                         encryptedKey.KeyInfo.AddClause (new KeyInfoName (keyName));
241                         
242                         encryptedData.Type = XmlEncElementUrl;
243                         encryptedData.EncryptionMethod = new EncryptionMethod (GetAlgorithmUri (symAlg));
244                         encryptedData.KeyInfo = new KeyInfo ();
245                         encryptedData.KeyInfo.AddClause (new KeyInfoEncryptedKey (encryptedKey));
246                         encryptedData.CipherData = new CipherData (EncryptData (inputElement, symAlg, false));
247
248                         return encryptedData;
249                 }
250                 
251                 [MonoTODO]
252                 public EncryptedData Encrypt (XmlElement inputElement, X509Certificate2 certificate)
253                 {
254                         throw new NotImplementedException ();
255                 }
256
257                 public byte[] EncryptData (byte[] plaintext, SymmetricAlgorithm symmetricAlgorithm)
258                 {
259                         if (plaintext == null)
260                                 throw new ArgumentNullException ("plaintext");
261                         if (symmetricAlgorithm == null)
262                                 throw new ArgumentNullException ("symmetricAlgorithm");
263
264                         PaddingMode bak = symmetricAlgorithm.Padding;
265                         try {
266                                 symmetricAlgorithm.Padding = Padding;
267                                 return EncryptDataCore (plaintext, symmetricAlgorithm);
268                         } finally {
269                                 symmetricAlgorithm.Padding = bak;
270                         }
271                 }
272
273                 byte[] EncryptDataCore (byte[] plainText, SymmetricAlgorithm symAlg)
274                 {
275                         // Write the symmetric algorithm IV and ciphertext together.
276                         // We use a memory stream to accomplish this.
277                         MemoryStream stream = new MemoryStream ();
278                         BinaryWriter writer = new BinaryWriter (stream);
279
280                         writer.Write (symAlg.IV);
281                         writer.Write (Transform (plainText, symAlg.CreateEncryptor ()));
282                         writer.Flush ();
283
284                         byte [] output = stream.ToArray ();
285
286                         writer.Close ();
287                         stream.Close ();
288
289                         return output;
290                 }
291
292                 public byte[] EncryptData (XmlElement inputElement, SymmetricAlgorithm symmetricAlgorithm, bool content)
293                 {
294                         if (inputElement == null)
295                                 throw new ArgumentNullException ("inputElement");
296
297                         if (content)
298                                 return EncryptData (Encoding.GetBytes (inputElement.InnerXml), symmetricAlgorithm);
299                         else
300                                 return EncryptData (Encoding.GetBytes (inputElement.OuterXml), symmetricAlgorithm);
301                 }
302
303                 public static byte[] EncryptKey (byte[] keyData, SymmetricAlgorithm symmetricAlgorithm)
304                 {
305                         if (keyData == null)
306                                 throw new ArgumentNullException ("keyData");
307                         if (symmetricAlgorithm == null)
308                                 throw new ArgumentNullException ("symmetricAlgorithm");
309
310                         if (symmetricAlgorithm is TripleDES)
311                                 return SymmetricKeyWrap.TripleDESKeyWrapEncrypt (symmetricAlgorithm.Key, keyData);
312                         if (symmetricAlgorithm is Rijndael)
313                                 return SymmetricKeyWrap.AESKeyWrapEncrypt (symmetricAlgorithm.Key, keyData);
314
315                         throw new CryptographicException ("The specified cryptographic transform is not supported.");
316                 }
317
318                 [MonoTODO ("Test this.")]
319                 public static byte[] EncryptKey (byte[] keyData, RSA rsa, bool useOAEP)
320                 {
321                         AsymmetricKeyExchangeFormatter formatter = null;
322                         if (useOAEP) 
323                                 formatter = new RSAOAEPKeyExchangeFormatter (rsa);
324                         else
325                                 formatter = new RSAPKCS1KeyExchangeFormatter (rsa);
326                         return formatter.CreateKeyExchange (keyData);
327                 }
328
329                 private static SymmetricAlgorithm GetAlgorithm (string symAlgUri)
330                 {
331                         SymmetricAlgorithm symAlg = null;
332
333                         switch (symAlgUri) {
334                         case XmlEncAES128Url:
335                         case XmlEncAES128KeyWrapUrl:
336                                 symAlg = SymmetricAlgorithm.Create ("Rijndael");
337                                 symAlg.KeySize = 128;
338                                 break;
339                         case XmlEncAES192Url:
340                         case XmlEncAES192KeyWrapUrl:
341                                 symAlg = SymmetricAlgorithm.Create ("Rijndael");
342                                 symAlg.KeySize = 192;
343                                 break;
344                         case XmlEncAES256Url:
345                         case XmlEncAES256KeyWrapUrl:
346                                 symAlg = SymmetricAlgorithm.Create ("Rijndael");
347                                 symAlg.KeySize = 256;
348                                 break;
349                         case XmlEncDESUrl:
350                                 symAlg = SymmetricAlgorithm.Create ("DES");
351                                 break;
352                         case XmlEncTripleDESUrl:
353                         case XmlEncTripleDESKeyWrapUrl:
354                                 symAlg = SymmetricAlgorithm.Create ("TripleDES");
355                                 break;
356                         default:
357                                 throw new CryptographicException ("symAlgUri");
358                         }
359
360                         return symAlg;
361                 }
362
363                 private static string GetAlgorithmUri (SymmetricAlgorithm symAlg)
364                 {
365                         if (symAlg is Rijndael)
366                         {
367                                 switch (symAlg.KeySize) {
368                                 case 128:
369                                         return XmlEncAES128Url;
370                                 case 192:
371                                         return XmlEncAES192Url;
372                                 case 256:
373                                         return XmlEncAES256Url;
374                                 }
375                         }
376                         else if (symAlg is DES)
377                                 return XmlEncDESUrl;
378                         else if (symAlg is TripleDES)
379                                 return XmlEncTripleDESUrl;
380
381                         throw new ArgumentException ("symAlg");
382                 }
383
384                 private static string GetKeyWrapAlgorithmUri (object keyAlg)
385                 {
386                         if (keyAlg is Rijndael)
387                         {
388                                 switch (((Rijndael) keyAlg).KeySize) {
389                                 case 128:
390                                         return XmlEncAES128KeyWrapUrl;
391                                 case 192:
392                                         return XmlEncAES192KeyWrapUrl;
393                                 case 256:
394                                         return XmlEncAES256KeyWrapUrl;
395                                 }
396                         }
397                         else if (keyAlg is RSA) 
398                                 return XmlEncRSA15Url;
399                         else if (keyAlg is TripleDES)
400                                 return XmlEncTripleDESKeyWrapUrl;
401
402                         throw new ArgumentException ("keyAlg");
403                 }
404
405                 public virtual byte[] GetDecryptionIV (EncryptedData encryptedData, string symmetricAlgorithmUri)
406                 {
407                         if (encryptedData == null)
408                                 throw new ArgumentNullException ("encryptedData");
409
410                         SymmetricAlgorithm symAlg = GetAlgorithm (symmetricAlgorithmUri);
411                         byte[] iv = new Byte [symAlg.BlockSize / 8];
412                         Buffer.BlockCopy (encryptedData.CipherData.CipherValue, 0, iv, 0, iv.Length);
413                         return iv;
414                 }
415
416                 public virtual SymmetricAlgorithm GetDecryptionKey (EncryptedData encryptedData, string symmetricAlgorithmUri)
417                 {
418                         if (encryptedData == null)
419                                 throw new ArgumentNullException ("encryptedData");
420                         if (symmetricAlgorithmUri == null)
421                                 return null;
422
423                         SymmetricAlgorithm symAlg = GetAlgorithm (symmetricAlgorithmUri);
424                         symAlg.IV = GetDecryptionIV (encryptedData, encryptedData.EncryptionMethod.KeyAlgorithm);
425                         KeyInfo keyInfo = encryptedData.KeyInfo;
426                         foreach (KeyInfoClause clause in keyInfo) {
427                                 if (clause is KeyInfoEncryptedKey) {
428                                         symAlg.Key = DecryptEncryptedKey (((KeyInfoEncryptedKey) clause).EncryptedKey);
429                                         break;
430                                 }
431                         }
432                         return symAlg;
433                 }
434
435                 public virtual XmlElement GetIdElement (XmlDocument document, string idValue)
436                 {
437                         if ((document == null) || (idValue == null))
438                                 return null;
439
440                         // this works only if there's a DTD or XSD available to define the ID
441                         XmlElement xel = document.GetElementById (idValue);
442                         if (xel == null) {
443                                 // search an "undefined" ID
444                                 xel = (XmlElement) document.SelectSingleNode ("//*[@Id='" + idValue + "']");
445                         }
446                         return xel;
447                 }
448
449                 public void ReplaceData (XmlElement inputElement, byte[] decryptedData)
450                 {
451                         if (inputElement == null)
452                                 throw new ArgumentNullException ("inputElement");
453                         if (decryptedData == null)
454                                 throw new ArgumentNullException ("decryptedData");
455
456                         XmlDocument ownerDocument = inputElement.OwnerDocument;
457                         XmlTextReader reader = new XmlTextReader (new StringReader (Encoding.GetString (decryptedData, 0, decryptedData.Length)));
458                         reader.MoveToContent ();
459                         XmlNode node = ownerDocument.ReadNode (reader);
460                         inputElement.ParentNode.ReplaceChild (node, inputElement);
461                 }
462
463                 public static void ReplaceElement (XmlElement inputElement, EncryptedData encryptedData, bool content)
464                 {
465                         if (inputElement == null)
466                                 throw new ArgumentNullException ("inputElement");
467                         if (encryptedData == null)
468                                 throw new ArgumentNullException ("encryptedData");
469
470                         XmlDocument ownerDocument = inputElement.OwnerDocument;
471                         inputElement.ParentNode.ReplaceChild (encryptedData.GetXml (ownerDocument), inputElement);
472                 }
473
474                 private byte[] Transform (byte[] data, ICryptoTransform transform)
475                 {
476                         return Transform (data, transform, 0, false);
477                 }
478
479                 private byte[] Transform (byte[] data, ICryptoTransform transform, int blockOctetCount, bool trimPadding)
480                 {
481                         MemoryStream output = new MemoryStream ();
482                         CryptoStream crypto = new CryptoStream (output, transform, CryptoStreamMode.Write);
483                         crypto.Write (data, 0, data.Length);
484
485                         crypto.FlushFinalBlock ();
486
487                         // strip padding (see xmlenc spec 5.2)
488                         int trimSize = 0;
489                         if (trimPadding)
490                                 trimSize = output.GetBuffer () [output.Length - 1];
491                         // It should not happen, but somehow .NET allows such cipher 
492                         // data as if there were no padding.
493                         if (trimSize > blockOctetCount)
494                                 trimSize = 0;
495                         byte[] result = new byte [output.Length - blockOctetCount - trimSize];
496                         Array.Copy (output.GetBuffer (), blockOctetCount, result, 0, result.Length);
497
498                         crypto.Close ();
499                         output.Close ();
500
501                         return result;
502                 }
503
504                 #endregion // Methods
505         }
506 }
507