This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mcs / class / Microsoft.Web.Services / Microsoft.Web.Services.Security / EncryptedData.cs
1 //
2 // EncryptedData.cs: Handles WS-Security EncryptedData
3 //
4 // Author:
5 //      Sebastien Pouliot (spouliot@motus.com)
6 //
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 //
9
10 using System;
11 using System.Security.Cryptography;
12 using System.Security.Cryptography.Xml;
13 using System.Text;
14 using System.Xml;
15 using Microsoft.Web.Services.Timestamp;
16 #if !WSE1
17 using Microsoft.Web.Services.Xml;
18 #endif
19
20 namespace Microsoft.Web.Services.Security {
21
22         public sealed class EncryptedData : ISecurityElement, IXmlElement {
23
24                 private SecurityToken token;
25                 private EncryptionKey encryptionKey;
26                 private EncryptedKey encryptedKey;
27                 private string reference;
28                 private string type;
29                 private KeyInfo ki;
30                 private XmlElement target;
31
32                 public EncryptedData (EncryptionKey key) : this (key, null) {}
33
34                 public EncryptedData (SecurityToken token) : this (token, null) {}
35
36                 public EncryptedData (XmlElement element) 
37                 {
38                         LoadXml (element);
39                 }
40
41                 public EncryptedData (EncryptionKey key, string reference) 
42                 {
43                         this.encryptionKey = key;
44                         this.reference = reference;
45                 }
46
47                 [MonoTODO]
48                 public EncryptedData (SecurityToken token, string reference)
49                 {
50 // to be compatible with WSE1
51 //                      if (token == null)
52 //                              throw new ArgumentNullException ("token");
53                         if (!token.SupportsDataEncryption)
54                                 throw new NotSupportedException ("!SupportsDataEncryption");
55                         if ((reference != null) && (reference [0] != '#'))
56                                 throw new ArgumentException ("reference must start with a #");
57
58                         this.reference = reference;
59                         this.token = token;
60                         encryptionKey = token.EncryptionKey;
61                         
62                         AsymmetricEncryptionKey aek = (token.EncryptionKey as AsymmetricEncryptionKey);
63                         if (aek != null) {
64                                 encryptedKey = new EncryptedKey (aek);
65                         }
66 #if !WSE1
67                         SymmetricEncryptionKey sek = (token.EncryptionKey as SymmetricEncryptionKey);
68                         if (sek != null) {
69                                 encryptedKey = new EncryptedKey (sek);
70                         }
71 #endif
72                         type = XmlEncryption.TypeURI.Content;
73                 }
74
75                 public EncryptedData (XmlElement element, EncryptedKey encryptedKey) 
76                 {
77                         this.encryptedKey = encryptedKey;
78                         LoadXml (element);
79                 }
80
81                 public EncryptedKey EncryptedKey {
82                         get { return encryptedKey; }
83                 }
84
85                 public string EncryptionMethod {
86                         get { return encryptedKey.SessionAlgorithmURI; }
87                 }
88
89                 public KeyInfo KeyInfo {
90                         get { return ki; }
91                 }
92
93                 public string Reference {
94                         get { return reference; }
95                 }
96
97                 public XmlElement TargetElement {
98                         get { return target; }
99                 }
100
101                 [MonoTODO]
102                 public string Type {
103                         get { return type; }
104                         set {
105                                 if (value == null)
106                                         throw new ArgumentNullException ("value");
107                                 switch (value) {
108                                         case XmlEncryption.TypeURI.Content:
109                                                 type = value;
110                                                 break;
111                                         case XmlEncryption.TypeURI.Element:
112                                                 type = value;
113                                                 break;
114                                         default:
115                                                 throw new ArgumentException ("bad Type");
116                                 }
117                         }
118                 }
119
120                 [MonoTODO("incomplete - only works for soap:Body")]
121                 public XmlElement Decrypt ()
122                 {
123                         if (target == null)
124                                 throw new InvalidOperationException ("no document to decrypt");
125                         if ((encryptedKey == null) || (encryptedKey.Key == null))
126                                 throw new InvalidOperationException ("no key to decrypt with");
127                         
128                         string algo = null;
129                         XmlNodeList xnl = target.GetElementsByTagName (XmlEncryption.ElementNames.EncryptionMethod, XmlEncryption.NamespaceURI);
130                         if ((xnl != null) && (xnl.Count > 0)) {
131                                 XmlAttribute ema = xnl [0].Attributes [XmlEncryption.AttributeNames.Algorithm];
132                                 if (ema != null)
133                                         algo = ema.InnerText;
134                         }
135                         if (algo != encryptedKey.SessionAlgorithmURI)
136                                 throw new Exception ("TODO ???");
137
138                         byte[] encdata = null;
139                         xnl = target.GetElementsByTagName (XmlEncryption.ElementNames.CipherData, XmlEncryption.NamespaceURI);
140                         if ((xnl != null) && (xnl.Count > 0)) {
141                                 XmlElement cd = (XmlElement) xnl [0];
142                                 foreach (XmlNode xn in cd.ChildNodes) {
143                                         if ((xn.LocalName == XmlEncryption.ElementNames.CipherValue) && (xn.NamespaceURI == XmlEncryption.NamespaceURI)) {
144                                                 encdata = Convert.FromBase64String (xn.InnerText);
145                                         }
146                                 }
147                         }
148
149                         // get the IV in front of the encrypted data
150                         int ivLength = encryptedKey.Key.Algorithm.IV.Length;
151                         byte[] iv = new byte [ivLength];
152                         Buffer.BlockCopy (encdata, 0, iv, 0, ivLength);
153                         encryptedKey.Key.Algorithm.IV = iv;
154
155                         ICryptoTransform ct = encryptedKey.Key.Algorithm.CreateDecryptor ();
156                         byte[] decdata = ct.TransformFinalBlock (encdata, ivLength, encdata.Length - ivLength);
157                         string xml = Encoding.UTF8.GetString (decdata);
158                         target.ParentNode.InnerXml = xml;
159
160                         return target;
161                 }
162
163                 // copied from SoapEnvelope.cs to avoid creating the object
164                 private XmlElement GetBody (XmlDocument message) 
165                 {
166                         XmlNodeList xnl = message.GetElementsByTagName (Soap.ElementNames.Body, Soap.NamespaceURI);
167                         return (XmlElement) xnl [0];
168                 }
169
170                 [MonoTODO("incomplete - only works for soap:Body")]
171                 public void Encrypt (XmlDocument message) 
172                 {
173                         if (message == null)
174                                 throw new ArgumentNullException ("message");
175                         
176                         if (reference != null) {
177                                 // TODO
178                         }
179                         else {
180                                 target = GetBody (message);
181                         }
182
183                         // now the XML encryption stuff
184                         if (target != null) {
185                                 SymmetricAlgorithm sa = encryptedKey.Key.Algorithm;
186                                 // IMPORTANT: each encryption MUST have it's own IV !
187                                 sa.GenerateIV ();
188                                 ICryptoTransform ct = sa.CreateEncryptor ();
189                                 byte[] toencrypt = Encoding.UTF8.GetBytes (target.InnerXml);
190                                 byte[] encrypted = ct.TransformFinalBlock (toencrypt, 0, toencrypt.Length);
191                                 // it's our responsability to zeroize our copy of the unencrypted data
192                                 Array.Clear (toencrypt, 0, toencrypt.Length);
193
194                                 XmlAttribute ema = message.CreateAttribute ("Algorithm");
195                                 ema.InnerText = encryptedKey.SessionAlgorithmURI; 
196                                 XmlElement em = message.CreateElement (XmlEncryption.Prefix, XmlEncryption.ElementNames.EncryptionMethod, XmlEncryption.NamespaceURI);
197                                 em.Attributes.Append (ema);
198
199                                 XmlElement cv = message.CreateElement (XmlEncryption.Prefix, XmlEncryption.ElementNames.CipherValue, XmlEncryption.NamespaceURI);
200                                 byte[] data = new byte [sa.IV.Length + encrypted.Length];
201                                 Buffer.BlockCopy (sa.IV, 0, data, 0, sa.IV.Length);
202                                 Buffer.BlockCopy (encrypted, 0, data, sa.IV.Length, encrypted.Length);
203                                 cv.InnerText = Convert.ToBase64String (data);
204                                 XmlElement cd = message.CreateElement (XmlEncryption.Prefix, XmlEncryption.ElementNames.CipherData, XmlEncryption.NamespaceURI);
205                                 cd.AppendChild (cv);
206
207                                 XmlAttribute edid = message.CreateAttribute ("Id");
208                                 edid.InnerText = "EncryptedContent-" + Guid.NewGuid ().ToString ();
209                                 XmlAttribute edt = message.CreateAttribute ("Type");
210                                 edt.InnerText = XmlEncryption.TypeURI.Content;
211
212                                 XmlElement ed = message.CreateElement (XmlEncryption.Prefix, XmlEncryption.ElementNames.EncryptedData, XmlEncryption.NamespaceURI);
213                                 ed.Attributes.Append (edid);
214                                 ed.Attributes.Append (edt);
215                                 ed.AppendChild (em);
216                                 ed.AppendChild (cd);
217                                 target.RemoveAll ();
218                                 target.AppendChild (ed);
219                                 // no sure why but encryption make this Id appear in WSE so...
220                                 XmlAttribute id = message.CreateAttribute (WSTimestamp.Prefix, WSTimestamp.AttributeNames.Id, WSTimestamp.NamespaceURI);
221                                 id.InnerText = "Id-" + Guid.NewGuid ().ToString ();
222                                 target.Attributes.Append (id);
223
224                                 encryptedKey.ReferenceList.Add (edid.InnerText);
225                         }
226                 }
227
228                 [MonoTODO("incomplete - minimal implementation")]
229                 public XmlElement GetXml (XmlDocument document) 
230                 {
231                         if (document == null)
232                                 throw new ArgumentNullException ("document");
233
234                         XmlAttribute ema = document.CreateAttribute (XmlEncryption.AttributeNames.Algorithm);
235                         ema.InnerText = encryptedKey.SessionAlgorithmURI;
236
237                         XmlElement em = document.CreateElement (XmlEncryption.Prefix, XmlEncryption.ElementNames.EncryptionMethod, XmlEncryption.NamespaceURI);
238                         em.Attributes.Append (ema);
239
240                         XmlAttribute edi = document.CreateAttribute (XmlEncryption.AttributeNames.Id);
241                         XmlAttribute edt = document.CreateAttribute (XmlEncryption.AttributeNames.Type);
242                         edt.InnerText = XmlEncryption.TypeURI.Content;
243
244                         XmlElement ed = document.CreateElement (XmlEncryption.Prefix, XmlEncryption.ElementNames.EncryptedData, XmlEncryption.NamespaceURI);
245                         ed.Attributes.Append (edi);
246                         ed.Attributes.Append (edt);
247                         ed.AppendChild (em);
248                         return ed;
249                 }
250
251                 [MonoTODO]
252                 public void LoadXml (XmlElement element)
253                 {
254                         if (element == null)
255                                 throw new ArgumentNullException ("element");
256                         if ((element.LocalName != XmlEncryption.ElementNames.EncryptedData) || (element.NamespaceURI != XmlEncryption.NamespaceURI))
257                                 throw new ArgumentException ("invalid LocalName or NamespaceURI");
258
259                         XmlAttribute xa = element.Attributes [XmlEncryption.AttributeNames.Type];
260                         if (xa != null) {
261                                 Type = xa.InnerText;
262                         }
263
264                         target = element;
265                 }
266         }
267 }