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
.Xml
;
23 using Microsoft
.Web
.Services
.Security
;
25 namespace Microsoft
.Web
.Services
.Security
{
27 public class SignedXml
: IXmlElement
{
29 using System
.Security
.Cryptography
.Xml
;
31 namespace System
.Security
.Cryptography
.Xml
{
33 public class SignedXml
{
37 private SignedXmlSignature signature
;
41 signature
= new SignedXmlSignature ();
42 signature
.SignedInfo
= new SignedInfo ();
45 private Signature signature
;
49 signature
= new Signature ();
50 signature
.SignedInfo
= new SignedInfo ();
53 private AsymmetricAlgorithm key
;
54 private string keyName
;
55 private XmlDocument envdoc
;
57 public SignedXml (XmlDocument document
) : this ()
62 public SignedXml (XmlElement elem
) : this ()
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; }
83 public SignedXmlSignature Signature
{
84 get { return signature; }
87 public Signature Signature
{
88 get { return signature; }
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
{
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
)
130 if (t
is SSCX
.XmlDsigEnvelopedSignatureTransform
) {
131 XmlDocument d
= (XmlDocument
) t
.GetOutput ();
132 MemoryStream ms
= new MemoryStream ();
137 return (Stream
) t
.GetOutput ();
140 private Stream
ApplyTransform (SSCX
.Transform t
, Stream s
)
144 s
= (Stream
) t
.GetOutput ();
146 catch (Exception e
) {
147 string temp
= e
.ToString (); // stop debugger
152 [MonoTODO("incomplete")]
153 private byte[] GetReferenceHash (Reference r
)
155 XmlDocument doc
= new XmlDocument ();
156 doc
.PreserveWhitespace
= true;
160 foreach (SSCX
.DataObject obj
in signature
.ObjectList
) {
161 if ("#" + obj
.Id
== r
.Uri
) {
162 doc
.LoadXml (obj
.GetXml ().OuterXml
);
169 if (r
.TransformChain
.Count
> 0) {
170 foreach (SSCX
.Transform t
in r
.TransformChain
) {
172 s
= ApplyTransform (t
, doc
);
174 s
= ApplyTransform (t
, s
);
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
);
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
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
)))
234 public bool CheckSignature (AsymmetricAlgorithm key
)
237 throw new ArgumentNullException ("key");
239 // Part 1: Are all references digest valid ?
240 bool result
= CheckReferenceIntegrity ();
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
);
259 private bool Compare (byte[] expected
, byte[] actual
)
261 bool result
= ((expected
!= null) && (actual
!= null));
263 int l
= expected
.Length
;
264 result
= (l
== actual
.Length
);
266 for (int i
=0; i
< l
; i
++) {
267 if (expected
[i
] != actual
[i
])
275 public bool CheckSignature (KeyedHashAlgorithm macAlg
)
278 throw new ArgumentNullException ("macAlg");
280 // Part 1: Are all references digest valid ?
281 bool result
= CheckReferenceIntegrity ();
283 // Part 2: Is the signature (over SignedInfo) valid ?
284 byte[] actual
= macAlg
.ComputeHash (SignedInfoTransformed ());
285 result
= Compare (signature
.SignatureValue
, actual
);
290 public bool CheckSignatureReturningKey (out AsymmetricAlgorithm signingKey
)
292 // here's the key used for verifying the signature
294 key
= GetPublicKey ();
296 // we'll find the key if we haven't already
297 return CheckSignature (key
);
300 public void ComputeSignature ()
303 // required before hashing
304 signature
.SignedInfo
.SignatureMethod
= key
.SignatureAlgorithm
;
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
315 signer
= new DSASignatureFormatter (key
);
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
)
329 throw new ArgumentNullException ("macAlg");
331 if (macAlg
is HMACSHA1
) {
334 signature
.SignedInfo
.SignatureMethod
= XmlDsigHMACSHA1Url
;
335 signature
.SignatureValue
= macAlg
.ComputeHash (SignedInfoTransformed ());
338 throw new CryptographicException ("unsupported algorithm");
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
)
354 else if (kic
is SSCX
.RSAKeyValue
)
358 key
.FromXmlString (kic
.GetXml ().InnerXml
);
366 public XmlElement
GetXml ()
368 return signature
.GetXml ();
371 public void LoadXml (XmlElement
value)
373 signature
.LoadXml (value);
378 public virtual XmlElement
GetXml (XmlDocument document
)
385 private XmlResolver xmlResolver
;
387 [MonoTODO("property not (yet) used in class")]
389 XmlResolver Resolver
{
390 set { xmlResolver = value; }