**** Merged from MCS ****
[mono-project.git] / mcs / class / Microsoft.Web.Services / Microsoft.Web.Services.Security / SignedXml.cs
blob8f42768fc12c9105675c008cf236bbd77fcf1849
1 //
2 // SignedXml.cs - SignedXml implementation for XML Signature
3 //
4 // Author:
5 // Sebastien Pouliot (spouliot@motus.com)
6 //
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 //
10 using System;
11 using System.Collections;
12 using System.IO;
13 using System.Runtime.InteropServices;
14 using System.Security.Cryptography;
15 using System.Xml;
17 using SSCX = System.Security.Cryptography.Xml;
19 #if WSE2
20 using Microsoft.Web.Services.Xml;
21 #endif
22 #if (WSE1 || WSE2)
23 using Microsoft.Web.Services.Security;
25 namespace Microsoft.Web.Services.Security {
27 public class SignedXml : IXmlElement {
28 #else
29 using System.Security.Cryptography.Xml;
31 namespace System.Security.Cryptography.Xml {
33 public class SignedXml {
34 #endif
36 #if (WSE1 || WSE2)
37 private SignedXmlSignature signature;
39 public SignedXml ()
41 signature = new SignedXmlSignature ();
42 signature.SignedInfo = new SignedInfo ();
44 #else
45 private Signature signature;
47 public SignedXml ()
49 signature = new Signature ();
50 signature.SignedInfo = new SignedInfo ();
52 #endif
53 private AsymmetricAlgorithm key;
54 private string keyName;
55 private XmlDocument envdoc;
57 public SignedXml (XmlDocument document) : this ()
59 envdoc = document;
62 public SignedXml (XmlElement elem) : this ()
64 if (elem == null)
65 throw new ArgumentNullException ("elem");
68 public const string XmlDsigCanonicalizationUrl = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
69 public const string XmlDsigCanonicalizationWithCommentsUrl = XmlDsigCanonicalizationUrl + "#WithComments";
70 public const string XmlDsigNamespaceUrl = "http://www.w3.org/2000/09/xmldsig#";
71 public const string XmlDsigDSAUrl = XmlDsigNamespaceUrl + "dsa-sha1";
72 public const string XmlDsigHMACSHA1Url = XmlDsigNamespaceUrl + "hmac-sha1";
73 public const string XmlDsigMinimalCanonicalizationUrl = XmlDsigNamespaceUrl + "minimal";
74 public const string XmlDsigRSASHA1Url = XmlDsigNamespaceUrl + "rsa-sha1";
75 public const string XmlDsigSHA1Url = XmlDsigNamespaceUrl + "sha1";
77 public SSCX.KeyInfo KeyInfo {
78 get { return signature.KeyInfo; }
79 set { signature.KeyInfo = value; }
82 #if (WSE1 || WSE2)
83 public SignedXmlSignature Signature {
84 get { return signature; }
86 #else
87 public Signature Signature {
88 get { return signature; }
90 #endif
91 public string SignatureLength {
92 get { return signature.SignedInfo.SignatureLength; }
95 public string SignatureMethod {
96 get { return signature.SignedInfo.SignatureMethod; }
99 public byte[] SignatureValue {
100 get { return signature.SignatureValue; }
103 public SignedInfo SignedInfo {
104 get { return signature.SignedInfo; }
107 public AsymmetricAlgorithm SigningKey {
108 get { return key; }
109 set { key = value; }
112 public string SigningKeyName {
113 get { return keyName; }
114 set { keyName = value; }
117 public void AddObject (SSCX.DataObject dataObject)
119 signature.AddObject (dataObject);
122 public void AddReference (Reference reference)
124 signature.SignedInfo.AddReference (reference);
127 private Stream ApplyTransform (SSCX.Transform t, XmlDocument doc)
129 t.LoadInput (doc);
130 if (t is SSCX.XmlDsigEnvelopedSignatureTransform) {
131 XmlDocument d = (XmlDocument) t.GetOutput ();
132 MemoryStream ms = new MemoryStream ();
133 d.Save (ms);
134 return ms;
136 else
137 return (Stream) t.GetOutput ();
140 private Stream ApplyTransform (SSCX.Transform t, Stream s)
142 try {
143 t.LoadInput (s);
144 s = (Stream) t.GetOutput ();
146 catch (Exception e) {
147 string temp = e.ToString (); // stop debugger
149 return s;
152 [MonoTODO("incomplete")]
153 private byte[] GetReferenceHash (Reference r)
155 XmlDocument doc = new XmlDocument ();
156 doc.PreserveWhitespace = true;
157 if (r.Uri == "")
158 doc = envdoc;
159 else {
160 foreach (SSCX.DataObject obj in signature.ObjectList) {
161 if ("#" + obj.Id == r.Uri) {
162 doc.LoadXml (obj.GetXml ().OuterXml);
163 break;
168 Stream s = null;
169 if (r.TransformChain.Count > 0) {
170 foreach (SSCX.Transform t in r.TransformChain) {
171 if (s == null)
172 s = ApplyTransform (t, doc);
173 else
174 s = ApplyTransform (t, s);
177 else
178 s = ApplyTransform (new SSCX.XmlDsigC14NTransform (), doc);
180 // TODO: We should reuse the same hash object (when possible)
181 HashAlgorithm hash = (HashAlgorithm) CryptoConfig.CreateFromName (r.DigestMethod);
182 return hash.ComputeHash (s);
185 private void DigestReferences ()
187 // we must tell each reference which hash algorithm to use
188 // before asking for the SignedInfo XML !
189 foreach (Reference r in signature.SignedInfo.References) {
190 // assume SHA-1 if nothing is specified
191 if (r.DigestMethod == null)
192 r.DigestMethod = XmlDsigSHA1Url;
193 r.DigestValue = GetReferenceHash (r);
197 private Stream SignedInfoTransformed ()
199 SSCX.Transform t = (SSCX.Transform) CryptoConfig.CreateFromName (signature.SignedInfo.CanonicalizationMethod);
200 if (t == null)
201 return null;
203 XmlDocument doc = new XmlDocument ();
204 doc.LoadXml (signature.SignedInfo.GetXml ().OuterXml);
205 return ApplyTransform (t, doc);
208 private byte[] Hash (string hashAlgorithm)
210 HashAlgorithm hash = HashAlgorithm.Create (hashAlgorithm);
211 // get the hash of the C14N SignedInfo element
212 return hash.ComputeHash (SignedInfoTransformed ());
215 public virtual bool CheckSignature ()
217 // CryptographicException
218 if (key == null)
219 key = GetPublicKey ();
220 return CheckSignature (key);
223 private bool CheckReferenceIntegrity ()
225 // check digest (hash) for every reference
226 foreach (Reference r in signature.SignedInfo.References) {
227 // stop at first broken reference
228 if (! Compare (r.DigestValue, GetReferenceHash (r)))
229 return false;
231 return true;
234 public bool CheckSignature (AsymmetricAlgorithm key)
236 if (key == null)
237 throw new ArgumentNullException ("key");
239 // Part 1: Are all references digest valid ?
240 bool result = CheckReferenceIntegrity ();
241 if (result) {
242 // Part 2: Is the signature (over SignedInfo) valid ?
243 SignatureDescription sd = (SignatureDescription) CryptoConfig.CreateFromName (signature.SignedInfo.SignatureMethod);
245 byte[] hash = Hash (sd.DigestAlgorithm);
246 AsymmetricSignatureDeformatter verifier = (AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName (sd.DeformatterAlgorithm);
248 if (verifier != null) {
249 verifier.SetHashAlgorithm (sd.DigestAlgorithm);
250 result = verifier.VerifySignature (hash, signature.SignatureValue);
252 else
253 result = false;
256 return result;
259 private bool Compare (byte[] expected, byte[] actual)
261 bool result = ((expected != null) && (actual != null));
262 if (result) {
263 int l = expected.Length;
264 result = (l == actual.Length);
265 if (result) {
266 for (int i=0; i < l; i++) {
267 if (expected[i] != actual[i])
268 return false;
272 return result;
275 public bool CheckSignature (KeyedHashAlgorithm macAlg)
277 if (macAlg == null)
278 throw new ArgumentNullException ("macAlg");
280 // Part 1: Are all references digest valid ?
281 bool result = CheckReferenceIntegrity ();
282 if (result) {
283 // Part 2: Is the signature (over SignedInfo) valid ?
284 byte[] actual = macAlg.ComputeHash (SignedInfoTransformed ());
285 result = Compare (signature.SignatureValue, actual);
287 return result;
290 public bool CheckSignatureReturningKey (out AsymmetricAlgorithm signingKey)
292 // here's the key used for verifying the signature
293 if (key == null)
294 key = GetPublicKey ();
295 signingKey = key;
296 // we'll find the key if we haven't already
297 return CheckSignature (key);
300 public void ComputeSignature ()
302 if (key != null) {
303 // required before hashing
304 signature.SignedInfo.SignatureMethod = key.SignatureAlgorithm;
305 DigestReferences ();
307 SignatureDescription sd = (SignatureDescription) CryptoConfig.CreateFromName (signature.SignedInfo.SignatureMethod);
309 // the hard part - C14Ning the KeyInfo
310 byte[] hash = Hash (sd.DigestAlgorithm);
311 AsymmetricSignatureFormatter signer = null;
313 // in need for a CryptoConfig factory
314 if (key is DSA)
315 signer = new DSASignatureFormatter (key);
316 else if (key is RSA)
317 signer = new RSAPKCS1SignatureFormatter (key);
319 if (signer != null) {
320 signer.SetHashAlgorithm ("SHA1");
321 signature.SignatureValue = signer.CreateSignature (hash);
326 public void ComputeSignature (KeyedHashAlgorithm macAlg)
328 if (macAlg == null)
329 throw new ArgumentNullException ("macAlg");
331 if (macAlg is HMACSHA1) {
332 DigestReferences ();
334 signature.SignedInfo.SignatureMethod = XmlDsigHMACSHA1Url;
335 signature.SignatureValue = macAlg.ComputeHash (SignedInfoTransformed ());
337 else
338 throw new CryptographicException ("unsupported algorithm");
341 // is that all ?
342 public virtual XmlElement GetIdElement (XmlDocument document, string idValue)
344 return document.GetElementById (idValue);
347 protected virtual AsymmetricAlgorithm GetPublicKey ()
349 AsymmetricAlgorithm key = null;
350 if (signature.KeyInfo != null) {
351 foreach (SSCX.KeyInfoClause kic in signature.KeyInfo) {
352 if (kic is SSCX.DSAKeyValue)
353 key = DSA.Create ();
354 else if (kic is SSCX.RSAKeyValue)
355 key = RSA.Create ();
357 if (key != null) {
358 key.FromXmlString (kic.GetXml ().InnerXml);
359 break;
363 return key;
366 public XmlElement GetXml ()
368 return signature.GetXml ();
371 public void LoadXml (XmlElement value)
373 signature.LoadXml (value);
376 #if (WSE1 || WSE2)
377 [MonoTODO]
378 public virtual XmlElement GetXml (XmlDocument document)
380 return null;
382 #endif
384 #if ! NET_1_0
385 private XmlResolver xmlResolver;
387 [MonoTODO("property not (yet) used in class")]
388 [ComVisible(false)]
389 XmlResolver Resolver {
390 set { xmlResolver = value; }
392 #endif