rename net_2_1 to moonlight
[mcs.git] / class / System.Security / System.Security.Cryptography.Xml / EncryptedXml.cs
blob0cdeb1302bd6a11ed8db1da6dbed2edc5240bc3c
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
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.
30 #if NET_2_0
32 using System.Collections;
33 using System.IO;
34 using System.Security.Cryptography;
35 using System.Security.Cryptography.X509Certificates;
36 using System.Security.Policy;
37 using System.Text;
38 using System.Xml;
40 namespace System.Security.Cryptography.Xml {
41 public class EncryptedXml {
43 #region Fields
45 public const string XmlEncAES128KeyWrapUrl = XmlEncNamespaceUrl + "kw-aes128";
46 public const string XmlEncAES128Url = XmlEncNamespaceUrl + "aes128-cbc";
47 public const string XmlEncAES192KeyWrapUrl = XmlEncNamespaceUrl + "kw-aes192";
48 public const string XmlEncAES192Url = XmlEncNamespaceUrl + "aes192-cbc";
49 public const string XmlEncAES256KeyWrapUrl = XmlEncNamespaceUrl + "kw-aes256";
50 public const string XmlEncAES256Url = XmlEncNamespaceUrl + "aes256-cbc";
51 public const string XmlEncDESUrl = XmlEncNamespaceUrl + "des-cbc";
52 public const string XmlEncElementContentUrl = XmlEncNamespaceUrl + "Content";
53 public const string XmlEncElementUrl = XmlEncNamespaceUrl + "Element";
54 public const string XmlEncEncryptedKeyUrl = XmlEncNamespaceUrl + "EncryptedKey";
55 public const string XmlEncNamespaceUrl = "http://www.w3.org/2001/04/xmlenc#";
56 public const string XmlEncRSA15Url = XmlEncNamespaceUrl + "rsa-1_5";
57 public const string XmlEncRSAOAEPUrl = XmlEncNamespaceUrl + "rsa-oaep-mgf1p";
58 public const string XmlEncSHA256Url = XmlEncNamespaceUrl + "sha256";
59 public const string XmlEncSHA512Url = XmlEncNamespaceUrl + "sha512";
60 public const string XmlEncTripleDESKeyWrapUrl = XmlEncNamespaceUrl + "kw-tripledes";
61 public const string XmlEncTripleDESUrl = XmlEncNamespaceUrl + "tripledes-cbc";
63 Evidence documentEvidence;
64 Encoding encoding = Encoding.UTF8;
65 internal Hashtable keyNameMapping = new Hashtable ();
66 CipherMode mode = CipherMode.CBC;
67 PaddingMode padding = PaddingMode.ISO10126;
68 string recipient;
69 XmlResolver resolver;
70 XmlDocument document;
72 #endregion // Fields
74 #region Constructors
76 [MonoTODO]
77 public EncryptedXml ()
81 [MonoTODO]
82 public EncryptedXml (XmlDocument document)
84 this.document = document;
87 [MonoTODO]
88 public EncryptedXml (XmlDocument document, Evidence evidence)
90 this.document = document;
91 DocumentEvidence = evidence;
94 #endregion // Constructors
96 #region Properties
98 public Evidence DocumentEvidence {
99 get { return documentEvidence; }
100 set { documentEvidence = value; }
103 public Encoding Encoding {
104 get { return encoding; }
105 set { encoding = value; }
108 public CipherMode Mode {
109 get { return mode; }
110 set { mode = value; }
113 public PaddingMode Padding {
114 get { return padding; }
115 set { padding = value; }
118 public string Recipient {
119 get { return recipient; }
120 set { recipient = value; }
123 public XmlResolver Resolver {
124 get { return resolver; }
125 set { resolver = value; }
128 #endregion // Properties
130 #region Methods
132 public void AddKeyNameMapping (string keyName, object keyObject)
134 keyNameMapping [keyName] = keyObject;
137 public void ClearKeyNameMappings ()
139 keyNameMapping.Clear ();
142 public byte[] DecryptData (EncryptedData encryptedData, SymmetricAlgorithm symAlg)
144 if (encryptedData == null)
145 throw new ArgumentNullException ("encryptedData");
146 if (symAlg == null)
147 throw new ArgumentNullException ("symAlg");
149 PaddingMode bak = symAlg.Padding;
150 try {
151 symAlg.Padding = Padding;
152 return Transform (encryptedData.CipherData.CipherValue, symAlg.CreateDecryptor (), symAlg.BlockSize / 8, true);
153 } finally {
154 symAlg.Padding = bak;
158 public void DecryptDocument ()
160 XmlNodeList nodes = document.GetElementsByTagName ("EncryptedData", XmlEncNamespaceUrl);
161 foreach (XmlNode node in nodes) {
162 EncryptedData encryptedData = new EncryptedData ();
163 encryptedData.LoadXml ((XmlElement) node);
164 SymmetricAlgorithm symAlg = GetDecryptionKey (encryptedData, encryptedData.EncryptionMethod.KeyAlgorithm);
165 ReplaceData ((XmlElement) node, DecryptData (encryptedData, symAlg));
169 public virtual byte[] DecryptEncryptedKey (EncryptedKey encryptedKey)
171 if (encryptedKey == null)
172 throw new ArgumentNullException ("encryptedKey");
174 object keyAlg = null;
175 foreach (KeyInfoClause innerClause in encryptedKey.KeyInfo) {
176 if (innerClause is KeyInfoName) {
177 keyAlg = keyNameMapping [((KeyInfoName) innerClause).Value];
178 break;
181 switch (encryptedKey.EncryptionMethod.KeyAlgorithm) {
182 case XmlEncRSA15Url:
183 return DecryptKey (encryptedKey.CipherData.CipherValue, (RSA) keyAlg, false);
184 case XmlEncRSAOAEPUrl:
185 return DecryptKey (encryptedKey.CipherData.CipherValue, (RSA) keyAlg, true);
187 return DecryptKey (encryptedKey.CipherData.CipherValue, (SymmetricAlgorithm) keyAlg);
190 public static byte[] DecryptKey (byte[] keyData, SymmetricAlgorithm symAlg)
192 if (keyData == null)
193 throw new ArgumentNullException ("keyData");
194 if (symAlg == null)
195 throw new ArgumentNullException ("symAlg");
197 if (symAlg is TripleDES)
198 return SymmetricKeyWrap.TripleDESKeyWrapDecrypt (symAlg.Key, keyData);
199 if (symAlg is Rijndael)
200 return SymmetricKeyWrap.AESKeyWrapDecrypt (symAlg.Key, keyData);
201 throw new CryptographicException ("The specified cryptographic transform is not supported.");
204 [MonoTODO ("Test this.")]
205 public static byte[] DecryptKey (byte[] keyData, RSA rsa, bool fOAEP)
207 AsymmetricKeyExchangeDeformatter deformatter = null;
208 if (fOAEP)
209 deformatter = new RSAOAEPKeyExchangeDeformatter (rsa);
210 else
211 deformatter = new RSAPKCS1KeyExchangeDeformatter (rsa);
212 return deformatter.DecryptKeyExchange (keyData);
215 public EncryptedData Encrypt (XmlElement inputElement, string keyName)
217 // There are two keys of note here.
218 // 1) KeyAlg: the key-encryption-key is used to wrap a key. The keyName
219 // parameter will give us the KEK.
220 // 2) SymAlg: A 256-bit AES key will be generated to encrypt the contents.
221 // This key will be wrapped using the KEK.
223 SymmetricAlgorithm symAlg = SymmetricAlgorithm.Create ("Rijndael");
224 symAlg.KeySize = 256;
225 symAlg.GenerateKey ();
226 symAlg.GenerateIV ();
228 EncryptedData encryptedData = new EncryptedData ();
229 EncryptedKey encryptedKey = new EncryptedKey();
231 object keyAlg = keyNameMapping [keyName];
233 encryptedKey.EncryptionMethod = new EncryptionMethod (GetKeyWrapAlgorithmUri (keyAlg));
235 if (keyAlg is RSA)
236 encryptedKey.CipherData = new CipherData (EncryptKey (symAlg.Key, (RSA) keyAlg, false));
237 else
238 encryptedKey.CipherData = new CipherData (EncryptKey (symAlg.Key, (SymmetricAlgorithm) keyAlg));
240 encryptedKey.KeyInfo = new KeyInfo();
241 encryptedKey.KeyInfo.AddClause (new KeyInfoName (keyName));
243 encryptedData.Type = XmlEncElementUrl;
244 encryptedData.EncryptionMethod = new EncryptionMethod (GetAlgorithmUri (symAlg));
245 encryptedData.KeyInfo = new KeyInfo ();
246 encryptedData.KeyInfo.AddClause (new KeyInfoEncryptedKey (encryptedKey));
247 encryptedData.CipherData = new CipherData (EncryptData (inputElement, symAlg, false));
249 return encryptedData;
252 [MonoTODO]
253 public EncryptedData Encrypt (XmlElement inputElement, X509Certificate2 certificate)
255 throw new NotImplementedException ();
258 public byte[] EncryptData (byte[] plainText, SymmetricAlgorithm symAlg)
260 if (plainText == null)
261 throw new ArgumentNullException ("plainText");
262 if (symAlg == null)
263 throw new ArgumentNullException ("symAlg");
265 PaddingMode bak = symAlg.Padding;
266 try {
267 symAlg.Padding = Padding;
268 return EncryptDataCore (plainText, symAlg);
269 } finally {
270 symAlg.Padding = bak;
274 byte[] EncryptDataCore (byte[] plainText, SymmetricAlgorithm symAlg)
276 // Write the symmetric algorithm IV and ciphertext together.
277 // We use a memory stream to accomplish this.
278 MemoryStream stream = new MemoryStream ();
279 BinaryWriter writer = new BinaryWriter (stream);
281 writer.Write (symAlg.IV);
282 writer.Write (Transform (plainText, symAlg.CreateEncryptor ()));
283 writer.Flush ();
285 byte [] output = stream.ToArray ();
287 writer.Close ();
288 stream.Close ();
290 return output;
293 public byte[] EncryptData (XmlElement inputElement, SymmetricAlgorithm symAlg, bool content)
295 if (inputElement == null)
296 throw new ArgumentNullException ("inputElement");
298 if (content)
299 return EncryptData (Encoding.GetBytes (inputElement.InnerXml), symAlg);
300 else
301 return EncryptData (Encoding.GetBytes (inputElement.OuterXml), symAlg);
304 public static byte[] EncryptKey (byte[] keyData, SymmetricAlgorithm symAlg)
306 if (keyData == null)
307 throw new ArgumentNullException ("keyData");
308 if (symAlg == null)
309 throw new ArgumentNullException ("symAlg");
311 if (symAlg is TripleDES)
312 return SymmetricKeyWrap.TripleDESKeyWrapEncrypt (symAlg.Key, keyData);
313 if (symAlg is Rijndael)
314 return SymmetricKeyWrap.AESKeyWrapEncrypt (symAlg.Key, keyData);
316 throw new CryptographicException ("The specified cryptographic transform is not supported.");
319 [MonoTODO ("Test this.")]
320 public static byte[] EncryptKey (byte[] keyData, RSA rsa, bool fOAEP)
322 AsymmetricKeyExchangeFormatter formatter = null;
323 if (fOAEP)
324 formatter = new RSAOAEPKeyExchangeFormatter (rsa);
325 else
326 formatter = new RSAPKCS1KeyExchangeFormatter (rsa);
327 return formatter.CreateKeyExchange (keyData);
330 private static SymmetricAlgorithm GetAlgorithm (string symAlgUri)
332 SymmetricAlgorithm symAlg = null;
334 switch (symAlgUri) {
335 case XmlEncAES128Url:
336 case XmlEncAES128KeyWrapUrl:
337 symAlg = SymmetricAlgorithm.Create ("Rijndael");
338 symAlg.KeySize = 128;
339 break;
340 case XmlEncAES192Url:
341 case XmlEncAES192KeyWrapUrl:
342 symAlg = SymmetricAlgorithm.Create ("Rijndael");
343 symAlg.KeySize = 192;
344 break;
345 case XmlEncAES256Url:
346 case XmlEncAES256KeyWrapUrl:
347 symAlg = SymmetricAlgorithm.Create ("Rijndael");
348 symAlg.KeySize = 256;
349 break;
350 case XmlEncDESUrl:
351 symAlg = SymmetricAlgorithm.Create ("DES");
352 break;
353 case XmlEncTripleDESUrl:
354 case XmlEncTripleDESKeyWrapUrl:
355 symAlg = SymmetricAlgorithm.Create ("TripleDES");
356 break;
357 default:
358 throw new CryptographicException ("symAlgUri");
361 return symAlg;
364 private static string GetAlgorithmUri (SymmetricAlgorithm symAlg)
366 if (symAlg is Rijndael)
368 switch (symAlg.KeySize) {
369 case 128:
370 return XmlEncAES128Url;
371 case 192:
372 return XmlEncAES192Url;
373 case 256:
374 return XmlEncAES256Url;
377 else if (symAlg is DES)
378 return XmlEncDESUrl;
379 else if (symAlg is TripleDES)
380 return XmlEncTripleDESUrl;
382 throw new ArgumentException ("symAlg");
385 private static string GetKeyWrapAlgorithmUri (object keyAlg)
387 if (keyAlg is Rijndael)
389 switch (((Rijndael) keyAlg).KeySize) {
390 case 128:
391 return XmlEncAES128KeyWrapUrl;
392 case 192:
393 return XmlEncAES192KeyWrapUrl;
394 case 256:
395 return XmlEncAES256KeyWrapUrl;
398 else if (keyAlg is RSA)
399 return XmlEncRSA15Url;
400 else if (keyAlg is TripleDES)
401 return XmlEncTripleDESKeyWrapUrl;
403 throw new ArgumentException ("keyAlg");
406 public virtual byte[] GetDecryptionIV (EncryptedData encryptedData, string symAlgUri)
408 if (encryptedData == null)
409 throw new ArgumentNullException ("encryptedData");
411 SymmetricAlgorithm symAlg = GetAlgorithm (symAlgUri);
412 byte[] iv = new Byte [symAlg.BlockSize / 8];
413 Buffer.BlockCopy (encryptedData.CipherData.CipherValue, 0, iv, 0, iv.Length);
414 return iv;
417 public virtual SymmetricAlgorithm GetDecryptionKey (EncryptedData encryptedData, string symAlgUri)
419 if (encryptedData == null)
420 throw new ArgumentNullException ("encryptedData");
421 if (symAlgUri == null)
422 return null;
424 SymmetricAlgorithm symAlg = GetAlgorithm (symAlgUri);
425 symAlg.IV = GetDecryptionIV (encryptedData, encryptedData.EncryptionMethod.KeyAlgorithm);
426 KeyInfo keyInfo = encryptedData.KeyInfo;
427 foreach (KeyInfoClause clause in keyInfo) {
428 if (clause is KeyInfoEncryptedKey) {
429 symAlg.Key = DecryptEncryptedKey (((KeyInfoEncryptedKey) clause).EncryptedKey);
430 break;
433 return symAlg;
436 public virtual XmlElement GetIdElement (XmlDocument document, string idValue)
438 if ((document == null) || (idValue == null))
439 return null;
441 // this works only if there's a DTD or XSD available to define the ID
442 XmlElement xel = document.GetElementById (idValue);
443 if (xel == null) {
444 // search an "undefined" ID
445 xel = (XmlElement) document.SelectSingleNode ("//*[@Id='" + idValue + "']");
447 return xel;
450 public void ReplaceData (XmlElement inputElement, byte[] decryptedData)
452 if (inputElement == null)
453 throw new ArgumentNullException ("inputElement");
454 if (decryptedData == null)
455 throw new ArgumentNullException ("decryptedData");
457 XmlDocument ownerDocument = inputElement.OwnerDocument;
458 XmlTextReader reader = new XmlTextReader (new StringReader (Encoding.GetString (decryptedData, 0, decryptedData.Length)));
459 reader.MoveToContent ();
460 XmlNode node = ownerDocument.ReadNode (reader);
461 inputElement.ParentNode.ReplaceChild (node, inputElement);
464 public static void ReplaceElement (XmlElement inputElement, EncryptedData encryptedData, bool content)
466 if (inputElement == null)
467 throw new ArgumentNullException ("inputElement");
468 if (encryptedData == null)
469 throw new ArgumentNullException ("encryptedData");
471 XmlDocument ownerDocument = inputElement.OwnerDocument;
472 inputElement.ParentNode.ReplaceChild (encryptedData.GetXml (ownerDocument), inputElement);
475 private byte[] Transform (byte[] data, ICryptoTransform transform)
477 return Transform (data, transform, 0, false);
480 private byte[] Transform (byte[] data, ICryptoTransform transform, int blockOctetCount, bool trimPadding)
482 MemoryStream output = new MemoryStream ();
483 CryptoStream crypto = new CryptoStream (output, transform, CryptoStreamMode.Write);
484 crypto.Write (data, 0, data.Length);
486 crypto.FlushFinalBlock ();
488 // strip padding (see xmlenc spec 5.2)
489 int trimSize = 0;
490 if (trimPadding)
491 trimSize = output.GetBuffer () [output.Length - 1];
492 // It should not happen, but somehow .NET allows such cipher
493 // data as if there were no padding.
494 if (trimSize > blockOctetCount)
495 trimSize = 0;
496 byte[] result = new byte [output.Length - blockOctetCount - trimSize];
497 Array.Copy (output.GetBuffer (), blockOctetCount, result, 0, result.Length);
499 crypto.Close ();
500 output.Close ();
502 return result;
505 #endregion // Methods
509 #endif