2 // EncryptedData.cs: Handles WS-Security EncryptedData
5 // Sebastien Pouliot (spouliot@motus.com)
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
11 using System.Security.Cryptography;
12 using System.Security.Cryptography.Xml;
15 using Microsoft.Web.Services.Timestamp;
17 using Microsoft.Web.Services.Xml;
20 namespace Microsoft.Web.Services.Security {
22 public sealed class EncryptedData : ISecurityElement, IXmlElement {
24 private SecurityToken token;
25 private EncryptionKey encryptionKey;
26 private EncryptedKey encryptedKey;
27 private string reference;
30 private XmlElement target;
32 public EncryptedData (EncryptionKey key) : this (key, null) {}
34 public EncryptedData (SecurityToken token) : this (token, null) {}
36 public EncryptedData (XmlElement element)
41 public EncryptedData (EncryptionKey key, string reference)
43 this.encryptionKey = key;
44 this.reference = reference;
48 public EncryptedData (SecurityToken token, string reference)
50 // to be compatible with WSE1
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 #");
58 this.reference = reference;
60 encryptionKey = token.EncryptionKey;
62 AsymmetricEncryptionKey aek = (token.EncryptionKey as AsymmetricEncryptionKey);
64 encryptedKey = new EncryptedKey (aek);
67 SymmetricEncryptionKey sek = (token.EncryptionKey as SymmetricEncryptionKey);
69 encryptedKey = new EncryptedKey (sek);
72 type = XmlEncryption.TypeURI.Content;
75 public EncryptedData (XmlElement element, EncryptedKey encryptedKey)
77 this.encryptedKey = encryptedKey;
81 public EncryptedKey EncryptedKey {
82 get { return encryptedKey; }
85 public string EncryptionMethod {
86 get { return encryptedKey.SessionAlgorithmURI; }
89 public KeyInfo KeyInfo {
93 public string Reference {
94 get { return reference; }
97 public XmlElement TargetElement {
98 get { return target; }
106 throw new ArgumentNullException ("value");
108 case XmlEncryption.TypeURI.Content:
111 case XmlEncryption.TypeURI.Element:
115 throw new ArgumentException ("bad Type");
120 [MonoTODO("incomplete - only works for soap:Body")]
121 public XmlElement Decrypt ()
124 throw new InvalidOperationException ("no document to decrypt");
125 if ((encryptedKey == null) || (encryptedKey.Key == null))
126 throw new InvalidOperationException ("no key to decrypt with");
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];
133 algo = ema.InnerText;
135 if (algo != encryptedKey.SessionAlgorithmURI)
136 throw new Exception ("TODO ???");
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);
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;
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;
163 // copied from SoapEnvelope.cs to avoid creating the object
164 private XmlElement GetBody (XmlDocument message)
166 XmlNodeList xnl = message.GetElementsByTagName (Soap.ElementNames.Body, Soap.NamespaceURI);
167 return (XmlElement) xnl [0];
170 [MonoTODO("incomplete - only works for soap:Body")]
171 public void Encrypt (XmlDocument message)
174 throw new ArgumentNullException ("message");
176 if (reference != null) {
180 target = GetBody (message);
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 !
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);
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);
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);
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;
212 XmlElement ed = message.CreateElement (XmlEncryption.Prefix, XmlEncryption.ElementNames.EncryptedData, XmlEncryption.NamespaceURI);
213 ed.Attributes.Append (edid);
214 ed.Attributes.Append (edt);
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);
224 encryptedKey.ReferenceList.Add (edid.InnerText);
228 [MonoTODO("incomplete - minimal implementation")]
229 public XmlElement GetXml (XmlDocument document)
231 if (document == null)
232 throw new ArgumentNullException ("document");
234 XmlAttribute ema = document.CreateAttribute (XmlEncryption.AttributeNames.Algorithm);
235 ema.InnerText = encryptedKey.SessionAlgorithmURI;
237 XmlElement em = document.CreateElement (XmlEncryption.Prefix, XmlEncryption.ElementNames.EncryptionMethod, XmlEncryption.NamespaceURI);
238 em.Attributes.Append (ema);
240 XmlAttribute edi = document.CreateAttribute (XmlEncryption.AttributeNames.Id);
241 XmlAttribute edt = document.CreateAttribute (XmlEncryption.AttributeNames.Type);
242 edt.InnerText = XmlEncryption.TypeURI.Content;
244 XmlElement ed = document.CreateElement (XmlEncryption.Prefix, XmlEncryption.ElementNames.EncryptedData, XmlEncryption.NamespaceURI);
245 ed.Attributes.Append (edi);
246 ed.Attributes.Append (edt);
252 public void LoadXml (XmlElement element)
255 throw new ArgumentNullException ("element");
256 if ((element.LocalName != XmlEncryption.ElementNames.EncryptedData) || (element.NamespaceURI != XmlEncryption.NamespaceURI))
257 throw new ArgumentException ("invalid LocalName or NamespaceURI");
259 XmlAttribute xa = element.Attributes [XmlEncryption.AttributeNames.Type];