2 // SignedXml.cs - SignedXml implementation for XML Signature
5 // Sebastien Pouliot (spouliot@motus.com)
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
11 using System.Collections;
13 using System.Runtime.InteropServices;
14 using System.Security.Cryptography;
17 using SSCX = System.Security.Cryptography.Xml;
20 using Microsoft.Web.Services.Security;
22 namespace Microsoft.Web.Services.Security {
24 using System.Security.Cryptography.Xml;
26 namespace System.Security.Cryptography.Xml {
28 public class SignedXml {
31 private SignedXmlSignature signature;
35 signature = new SignedXmlSignature ();
36 signature.SignedInfo = new SignedInfo ();
39 private Signature signature;
43 signature = new Signature ();
44 signature.SignedInfo = new SignedInfo ();
47 private AsymmetricAlgorithm key;
48 private string keyName;
49 private XmlDocument envdoc;
51 public SignedXml (XmlDocument document) : this ()
56 public SignedXml (XmlElement elem) : this ()
59 throw new ArgumentNullException ("elem");
62 public const string XmlDsigCanonicalizationUrl = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
63 public const string XmlDsigCanonicalizationWithCommentsUrl = XmlDsigCanonicalizationUrl + "#WithComments";
64 public const string XmlDsigNamespaceUrl = "http://www.w3.org/2000/09/xmldsig#";
65 public const string XmlDsigDSAUrl = XmlDsigNamespaceUrl + "dsa-sha1";
66 public const string XmlDsigHMACSHA1Url = XmlDsigNamespaceUrl + "hmac-sha1";
67 public const string XmlDsigMinimalCanonicalizationUrl = XmlDsigNamespaceUrl + "minimal";
68 public const string XmlDsigRSASHA1Url = XmlDsigNamespaceUrl + "rsa-sha1";
69 public const string XmlDsigSHA1Url = XmlDsigNamespaceUrl + "sha1";
71 public SSCX.KeyInfo KeyInfo {
72 get { return signature.KeyInfo; }
73 set { signature.KeyInfo = value; }
77 public SignedXmlSignature Signature {
78 get { return signature; }
81 public Signature Signature {
82 get { return signature; }
85 public string SignatureLength {
86 get { return signature.SignedInfo.SignatureLength; }
89 public string SignatureMethod {
90 get { return signature.SignedInfo.SignatureMethod; }
93 public byte[] SignatureValue {
94 get { return signature.SignatureValue; }
97 public SignedInfo SignedInfo {
98 get { return signature.SignedInfo; }
101 public AsymmetricAlgorithm SigningKey {
106 public string SigningKeyName {
107 get { return keyName; }
108 set { keyName = value; }
111 public void AddObject (SSCX.DataObject dataObject)
113 signature.AddObject (dataObject);
116 public void AddReference (Reference reference)
118 signature.SignedInfo.AddReference (reference);
121 private Stream ApplyTransform (SSCX.Transform t, XmlDocument doc)
124 if (t is SSCX.XmlDsigEnvelopedSignatureTransform) {
125 XmlDocument d = (XmlDocument) t.GetOutput ();
126 MemoryStream ms = new MemoryStream ();
131 return (Stream) t.GetOutput ();
134 private Stream ApplyTransform (SSCX.Transform t, Stream s)
138 s = (Stream) t.GetOutput ();
140 catch (Exception e) {
141 string temp = e.ToString (); // stop debugger
146 [MonoTODO("incomplete")]
147 private byte[] GetReferenceHash (Reference r)
149 XmlDocument doc = new XmlDocument ();
150 doc.PreserveWhitespace = true;
154 foreach (SSCX.DataObject obj in signature.ObjectList) {
155 if ("#" + obj.Id == r.Uri) {
156 doc.LoadXml (obj.GetXml ().OuterXml);
163 if (r.TransformChain.Count > 0) {
164 foreach (SSCX.Transform t in r.TransformChain) {
166 s = ApplyTransform (t, doc);
168 s = ApplyTransform (t, s);
172 s = ApplyTransform (new SSCX.XmlDsigC14NTransform (), doc);
174 // TODO: We should reuse the same hash object (when possible)
175 HashAlgorithm hash = (HashAlgorithm) CryptoConfig.CreateFromName (r.DigestMethod);
176 return hash.ComputeHash (s);
179 private void DigestReferences ()
181 // we must tell each reference which hash algorithm to use
182 // before asking for the SignedInfo XML !
183 foreach (Reference r in signature.SignedInfo.References) {
184 // assume SHA-1 if nothing is specified
185 if (r.DigestMethod == null)
186 r.DigestMethod = XmlDsigSHA1Url;
187 r.DigestValue = GetReferenceHash (r);
191 private Stream SignedInfoTransformed ()
193 SSCX.Transform t = (SSCX.Transform) CryptoConfig.CreateFromName (signature.SignedInfo.CanonicalizationMethod);
197 XmlDocument doc = new XmlDocument ();
198 doc.LoadXml (signature.SignedInfo.GetXml ().OuterXml);
199 return ApplyTransform (t, doc);
202 private byte[] Hash (string hashAlgorithm)
204 HashAlgorithm hash = HashAlgorithm.Create (hashAlgorithm);
205 // get the hash of the C14N SignedInfo element
206 return hash.ComputeHash (SignedInfoTransformed ());
209 public bool CheckSignature ()
211 // CryptographicException
213 key = GetPublicKey ();
214 return CheckSignature (key);
217 private bool CheckReferenceIntegrity ()
219 // check digest (hash) for every reference
220 foreach (Reference r in signature.SignedInfo.References) {
221 // stop at first broken reference
222 if (! Compare (r.DigestValue, GetReferenceHash (r)))
228 public bool CheckSignature (AsymmetricAlgorithm key)
231 throw new ArgumentNullException ("key");
233 // Part 1: Are all references digest valid ?
234 bool result = CheckReferenceIntegrity ();
236 // Part 2: Is the signature (over SignedInfo) valid ?
237 SignatureDescription sd = (SignatureDescription) CryptoConfig.CreateFromName (signature.SignedInfo.SignatureMethod);
239 byte[] hash = Hash (sd.DigestAlgorithm);
240 AsymmetricSignatureDeformatter verifier = (AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName (sd.DeformatterAlgorithm);
242 if (verifier != null) {
243 verifier.SetHashAlgorithm (sd.DigestAlgorithm);
244 result = verifier.VerifySignature (hash, signature.SignatureValue);
253 private bool Compare (byte[] expected, byte[] actual)
255 bool result = ((expected != null) && (actual != null));
257 int l = expected.Length;
258 result = (l == actual.Length);
260 for (int i=0; i < l; i++) {
261 if (expected[i] != actual[i])
269 public bool CheckSignature (KeyedHashAlgorithm macAlg)
272 throw new ArgumentNullException ("macAlg");
274 // Part 1: Are all references digest valid ?
275 bool result = CheckReferenceIntegrity ();
277 // Part 2: Is the signature (over SignedInfo) valid ?
278 byte[] actual = macAlg.ComputeHash (SignedInfoTransformed ());
279 result = Compare (signature.SignatureValue, actual);
284 public bool CheckSignatureReturningKey (out AsymmetricAlgorithm signingKey)
286 // here's the key used for verifying the signature
288 key = GetPublicKey ();
290 // we'll find the key if we haven't already
291 return CheckSignature (key);
294 public void ComputeSignature ()
297 // required before hashing
298 signature.SignedInfo.SignatureMethod = key.SignatureAlgorithm;
301 SignatureDescription sd = (SignatureDescription) CryptoConfig.CreateFromName (signature.SignedInfo.SignatureMethod);
303 // the hard part - C14Ning the KeyInfo
304 byte[] hash = Hash (sd.DigestAlgorithm);
305 AsymmetricSignatureFormatter signer = null;
307 // in need for a CryptoConfig factory
309 signer = new DSASignatureFormatter (key);
311 signer = new RSAPKCS1SignatureFormatter (key);
313 if (signer != null) {
314 signer.SetHashAlgorithm ("SHA1");
315 signature.SignatureValue = signer.CreateSignature (hash);
320 public void ComputeSignature (KeyedHashAlgorithm macAlg)
323 throw new ArgumentNullException ("macAlg");
325 if (macAlg is HMACSHA1) {
328 signature.SignedInfo.SignatureMethod = XmlDsigHMACSHA1Url;
329 signature.SignatureValue = macAlg.ComputeHash (SignedInfoTransformed ());
332 throw new CryptographicException ("unsupported algorithm");
336 public virtual XmlElement GetIdElement (XmlDocument document, string idValue)
338 return document.GetElementById (idValue);
341 protected virtual AsymmetricAlgorithm GetPublicKey ()
343 AsymmetricAlgorithm key = null;
344 if (signature.KeyInfo != null) {
345 foreach (SSCX.KeyInfoClause kic in signature.KeyInfo) {
346 if (kic is SSCX.DSAKeyValue)
348 else if (kic is SSCX.RSAKeyValue)
352 key.FromXmlString (kic.GetXml ().InnerXml);
360 public XmlElement GetXml ()
362 return signature.GetXml ();
365 public void LoadXml (XmlElement value)
367 signature.LoadXml (value);
371 private XmlResolver xmlResolver;
373 [MonoTODO("property not (yet) used in class")]
375 XmlResolver Resolver {
376 set { xmlResolver = value; }