Sunday, January 30, 2011

Xml Digital Signature: Signing the KeyInfo

@YaronNaveh

Frederic Vidal had shown me recently a nice trick with Xml digital signatures. Suppose you want to add a signature which looks like this:

<ds:Signature xmlns:ds='http://www.w3.org/2000/09/xmldsig#' Id='Signature001'>
   <ds:SignedInfo>
     <ds:CanonicalizationMethod Algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315' />
     <ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1' />
     <ds:Reference URI=''>
       <ds:Transforms>
         <ds:Transform Algorithm='http://www.w3.org/2000/09/xmldsig#enveloped-signature' />
       </ds:Transforms>
       <ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1' />
       <ds:DigestValue>sXe2PnaG...</ds:DigestValue>
     </ds:Reference>
     <ds:Reference URI='#KeyInfo001'>
       <ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1' />
       <ds:DigestValue>ZOS23PQ9TcDu+G...</ds:DigestValue>
     </ds:Reference>
   </ds:SignedInfo>
   <ds:SignatureValue>jTLX0/8XkY2aCte7...</ds:SignatureValue>
     <ds:KeyInfo Id='KeyInfo001'>
       <ds:X509Data>
         <ds:X509Certificate>E3wdSY4n7MgUmJzMIGfMA0...</ds:X509Certificate>
     </ds:X509Data>
   </ds:KeyInfo>
</ds:Signature>

The interesting part is the second reference (in bold) – the signature signs the KeyInfo (#KeyInfo001), which is part of the signature element itself. The regular api to add reference to a signature is this:

var reference = new Reference();
reference.Uri = "#KeyInfo001";

However it will not work here since it will look for an element with this ID in the signed document (e.g. soap envelope). However the ID is inside the signature element itself, which is still not a part of the document because it was not create yet.
Frederic had found a nice trick: Inherit from SignedXml and override the default logic to find the references such that it will search in the signature itself (which is the base class).

public class CustomIdSignedXml : SignedXml
{
   public CustomIdSignedXml(XmlDocument doc) : base(doc)
   {
     return;
   }

   public override XmlElement GetIdElement(XmlDocument doc, string id)
   {
     if (String.Compare(id, this.KeyInfo.Id, StringComparison.OrdinalIgnoreCase) == 0)
       return this.KeyInfo.GetXml();
     else
       return base.GetIdElement(doc, id);
   }
}

This is applicable to web services, since many soap stacks will not allow to customize them to sign the KeyInfo, and if this is required you would need to sign the message like this yourself.

@YaronNaveh

What's next? get this blog rss updates or register for mail updates!

8 comments:

Unknown said...

Hi Yaron.
I also working with xml signing. I've one example xml and I sign other xml, and generated xml same as example xml. During signing no problems. But cannot verify my xml. I've found app which verifying xml. It told that signing is wrong. Could u help me, please? My contact mirzaevaziz (at) gmail (dot) com
Thank you

Yaron Naveh (MVP) said...

TrachinuS

see here how to verify digital signature:

http://msdn.microsoft.com/en-us/library/ms229950.aspx

DkAngelito said...

Hi Yaron,
I want to know if this could help to use user name authentication and body signing using certificates like described here

Yaron Naveh (MVP) said...

Hi DkAngelito

I answered you in SO.

DkAngelito said...

Hi Yaron,
Your answer helps me a lot and excuse me for bothering you again but I still have some troubles.

I tried your solution and the soap message looks very similar, but the main difference is that the new message includes a second Reference element inside SignedInfo (with all element within it) and that it doesn't include any of the wsa elements (Action, MessageID, To) but I'm not sure if these are really needed.

Do you know why is this happening and how can I solve it.

Thanks again

Yaron Naveh (MVP) said...

instead of MessageVersion.Soap11 use other enum value with the addressing version you need.

which other element is signed in addition to the original one? myabe a timestamp? I don't think this should matter.

Did wcf sent a nonce in the username token?

DkAngelito said...

Hi,
I'm not including a timestamp, so this cant be.

I also think that there is an element on the header that is being signed too. How can I know which elements are being signed and How can I control this or make that only the body gets signed?

Yaron Naveh (MVP) said...

send me the message in email