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)
10 using System.Collections;
12 using System.Runtime.InteropServices;
13 using System.Security.Cryptography;
17 using System.Security.Cryptography.Xml;
19 namespace Microsoft.Web.Services.Security {
21 namespace System.Security.Cryptography.Xml {
23 public class SignedXml {
25 private Signature signature;
26 private AsymmetricAlgorithm key;
27 private string keyName;
28 private XmlDocument envdoc;
32 signature = new Signature ();
33 signature.SignedInfo = new SignedInfo ();
36 public SignedXml (XmlDocument document)
38 signature = new Signature ();
39 signature.SignedInfo = new SignedInfo ();
43 public SignedXml (XmlElement elem) : this ()
46 throw new ArgumentNullException ("elem");
47 signature = new Signature ();
48 signature.SignedInfo = new SignedInfo ();
51 public const string XmlDsigCanonicalizationUrl = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
52 public const string XmlDsigCanonicalizationWithCommentsUrl = XmlDsigCanonicalizationUrl + "#WithComments";
53 public const string XmlDsigNamespaceUrl = "http://www.w3.org/2000/09/xmldsig#";
54 public const string XmlDsigDSAUrl = XmlDsigNamespaceUrl + "dsa-sha1";
55 public const string XmlDsigHMACSHA1Url = XmlDsigNamespaceUrl + "hmac-sha1";
56 public const string XmlDsigMinimalCanonicalizationUrl = XmlDsigNamespaceUrl + "minimal";
57 public const string XmlDsigRSASHA1Url = XmlDsigNamespaceUrl + "rsa-sha1";
58 public const string XmlDsigSHA1Url = XmlDsigNamespaceUrl + "sha1";
60 public KeyInfo KeyInfo {
61 get { return signature.KeyInfo; }
62 set { signature.KeyInfo = value; }
65 public Signature Signature {
66 get { return signature; }
69 public string SignatureLength {
70 get { return signature.SignedInfo.SignatureLength; }
73 public string SignatureMethod {
74 get { return signature.SignedInfo.SignatureMethod; }
77 public byte[] SignatureValue {
78 get { return signature.SignatureValue; }
81 public SignedInfo SignedInfo {
82 get { return signature.SignedInfo; }
85 public AsymmetricAlgorithm SigningKey {
90 public string SigningKeyName {
91 get { return keyName; }
92 set { keyName = value; }
95 public void AddObject (DataObject dataObject)
97 signature.AddObject (dataObject);
100 public void AddReference (Reference reference)
102 signature.SignedInfo.AddReference (reference);
105 private Stream ApplyTransform (Transform t, XmlDocument doc)
108 if (t is XmlDsigEnvelopedSignatureTransform) {
109 XmlDocument d = (XmlDocument) t.GetOutput ();
110 MemoryStream ms = new MemoryStream ();
115 return (Stream) t.GetOutput ();
118 private Stream ApplyTransform (Transform t, Stream s)
122 s = (Stream) t.GetOutput ();
124 catch (Exception e) {
125 string temp = e.ToString (); // stop debugger
130 [MonoTODO("incomplete")]
131 private byte[] GetReferenceHash (Reference r)
133 XmlDocument doc = new XmlDocument ();
134 doc.PreserveWhitespace = true;
138 foreach (DataObject obj in signature.ObjectList) {
139 if ("#" + obj.Id == r.Uri) {
140 doc.LoadXml (obj.GetXml ().OuterXml);
147 if (r.TransformChain.Count > 0) {
148 foreach (Transform t in r.TransformChain) {
150 s = ApplyTransform (t, doc);
152 s = ApplyTransform (t, s);
156 s = ApplyTransform (new XmlDsigC14NTransform (), doc);
158 // TODO: We should reuse the same hash object (when possible)
159 HashAlgorithm hash = (HashAlgorithm) CryptoConfig.CreateFromName (r.DigestMethod);
160 return hash.ComputeHash (s);
163 private void DigestReferences ()
165 // we must tell each reference which hash algorithm to use
166 // before asking for the SignedInfo XML !
167 foreach (Reference r in signature.SignedInfo.References) {
168 // assume SHA-1 if nothing is specified
169 if (r.DigestMethod == null)
170 r.DigestMethod = XmlDsigSHA1Url;
171 r.DigestValue = GetReferenceHash (r);
175 private Stream SignedInfoTransformed ()
177 Transform t = (Transform) CryptoConfig.CreateFromName (signature.SignedInfo.CanonicalizationMethod);
181 XmlDocument doc = new XmlDocument ();
182 doc.LoadXml (signature.SignedInfo.GetXml ().OuterXml);
183 return ApplyTransform (t, doc);
186 private byte[] Hash (string hashAlgorithm)
188 HashAlgorithm hash = HashAlgorithm.Create (hashAlgorithm);
189 // get the hash of the C14N SignedInfo element
190 return hash.ComputeHash (SignedInfoTransformed ());
193 public bool CheckSignature ()
195 // CryptographicException
197 key = GetPublicKey ();
198 return CheckSignature (key);
201 private bool CheckReferenceIntegrity ()
203 // check digest (hash) for every reference
204 foreach (Reference r in signature.SignedInfo.References) {
205 // stop at first broken reference
206 if (! Compare (r.DigestValue, GetReferenceHash (r)))
212 public bool CheckSignature (AsymmetricAlgorithm key)
215 throw new ArgumentNullException ("key");
217 // Part 1: Are all references digest valid ?
218 bool result = CheckReferenceIntegrity ();
220 // Part 2: Is the signature (over SignedInfo) valid ?
221 SignatureDescription sd = (SignatureDescription) CryptoConfig.CreateFromName (signature.SignedInfo.SignatureMethod);
223 byte[] hash = Hash (sd.DigestAlgorithm);
224 AsymmetricSignatureDeformatter verifier = (AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName (sd.DeformatterAlgorithm);
226 if (verifier != null) {
227 verifier.SetHashAlgorithm (sd.DigestAlgorithm);
228 result = verifier.VerifySignature (hash, signature.SignatureValue);
237 private bool Compare (byte[] expected, byte[] actual)
239 bool result = ((expected != null) && (actual != null));
241 int l = expected.Length;
242 result = (l == actual.Length);
244 for (int i=0; i < l; i++) {
245 if (expected[i] != actual[i])
253 public bool CheckSignature (KeyedHashAlgorithm macAlg)
256 throw new ArgumentNullException ("macAlg");
258 // Part 1: Are all references digest valid ?
259 bool result = CheckReferenceIntegrity ();
261 // Part 2: Is the signature (over SignedInfo) valid ?
262 byte[] actual = macAlg.ComputeHash (SignedInfoTransformed ());
263 result = Compare (signature.SignatureValue, actual);
268 public bool CheckSignatureReturningKey (out AsymmetricAlgorithm signingKey)
270 // here's the key used for verifying the signature
272 key = GetPublicKey ();
274 // we'll find the key if we haven't already
275 return CheckSignature (key);
278 public void ComputeSignature ()
281 // required before hashing
282 signature.SignedInfo.SignatureMethod = key.SignatureAlgorithm;
285 SignatureDescription sd = (SignatureDescription) CryptoConfig.CreateFromName (signature.SignedInfo.SignatureMethod);
287 // the hard part - C14Ning the KeyInfo
288 byte[] hash = Hash (sd.DigestAlgorithm);
289 AsymmetricSignatureFormatter signer = null;
291 // in need for a CryptoConfig factory
293 signer = new DSASignatureFormatter (key);
295 signer = new RSAPKCS1SignatureFormatter (key);
297 if (signer != null) {
298 signer.SetHashAlgorithm ("SHA1");
299 signature.SignatureValue = signer.CreateSignature (hash);
304 public void ComputeSignature (KeyedHashAlgorithm macAlg)
307 throw new ArgumentNullException ("macAlg");
309 if (macAlg is HMACSHA1) {
312 signature.SignedInfo.SignatureMethod = XmlDsigHMACSHA1Url;
313 signature.SignatureValue = macAlg.ComputeHash (SignedInfoTransformed ());
316 throw new CryptographicException ("unsupported algorithm");
320 public virtual XmlElement GetIdElement (XmlDocument document, string idValue)
322 return document.GetElementById (idValue);
325 protected virtual AsymmetricAlgorithm GetPublicKey ()
327 AsymmetricAlgorithm key = null;
328 if (signature.KeyInfo != null) {
329 foreach (KeyInfoClause kic in signature.KeyInfo) {
330 if (kic is DSAKeyValue)
332 else if (kic is RSAKeyValue)
336 key.FromXmlString (kic.GetXml ().InnerXml);
344 public XmlElement GetXml ()
346 return signature.GetXml ();
349 public void LoadXml (XmlElement value)
351 signature.LoadXml (value);
355 private XmlResolver xmlResolver;
357 [MonoTODO("property not (yet) used in class")]
359 XmlResolver Resolver {
360 set { xmlResolver = value; }