EldoS | Feel safer!

Software components for data protection, secure storage and transfer

invalid xmldsig signature

Also by EldoS: CallbackDisk
Create virtual disks backed by memory or custom location, expose disk images as disks and more.
#17393
Posted: 08/31/2011 01:59:31
by Vladimir Kukov (Basic support level)
Joined: 08/31/2011
Posts: 6

Currenty I am evaluating your product to use in our integration solution.

For test purposes I am signing one element in simple XML document (#ClientSection).

Code
<?xml version="1.0" encoding="utf-8"?>
<Container>
  <SenderDocument Id="ClientSection">
    <Number>013A</Number>
  </SenderDocument>
  <ServerDocument Id="ServerSection">
    <AcceptedTime>0015</AcceptedTime>
  </ServerDocument>
</Container>


Signing code using SecureBlackbox components

Code
public XmlElement Sign(XmlDocument xmlDocument, SignatureOptions options, params string[] sectionsToSign)
        {
            TElXMLDOMDocument sXmlDocument = new TElXMLDOMDocument();
            
            //load xml document
            MemoryStream memoryBuffer = new MemoryStream();
            xmlDocument.Save(memoryBuffer);
            memoryBuffer.Position = 0;
            sXmlDocument.LoadFromStream(memoryBuffer, "utf-8", true);
            memoryBuffer.Dispose();

            //prepare references
            TElXMLReferenceList refList = new TElXMLReferenceList();

            foreach (string section in sectionsToSign)
            {
                TElXMLReference reference = new TElXMLReference();
                
                reference.DigestMethod = SBXMLSec.Unit.xdmSHA1;
                reference.URINode = sXmlDocument.GetElementByID(section);
                reference.URI = "#" + section;
                reference.TransformChain.Add(new TElXMLC14NTransform());

                refList.Add(reference);
            }

            //prepare signer
            TElXMLSigner xmlSigner = new TElXMLSigner();
            xmlSigner.SignatureType = SBXMLSec.Unit.xstEnveloped;
            xmlSigner.CanonicalizationMethod = SBXMLDefs.Unit.xcmCanon;
            xmlSigner.SignatureMethodType = SBXMLSec.Unit.xmtSig;
            xmlSigner.References = refList;
            xmlSigner.IncludeKey = true;
            xmlSigner.SignatureCompliance = SBXMLSec.Unit.xscDSIG;
            xmlSigner.SignatureMethod = SBXMLSec.Unit.xsmRSA_SHA1;

            //prepare certificate
            TElWinCertStorage certStorage = new TElWinCertStorage();
            certStorage.SystemStores.Text = options.IsSystemStore ?
                "SYSTEM" : "MY";

            TElX509Certificate x509cert = null;

            for (int i = 0; i < certStorage.Count; i++)
         {
                var currCert = certStorage.get_Certificates(i);

                string serial = SBUtils.Unit.BeautifyBinaryString(SBUtils.Unit.BinaryToString(currCert.SerialNumber), ' ');
                serial = serial.Remove(0, 3);
                if (serial.Equals(options.SerialNumber, StringComparison.InvariantCultureIgnoreCase))
                {
                    x509cert = currCert;
                    break;
                }
         }
            
            
            TElXMLKeyInfoX509Data x509Data = new TElXMLKeyInfoX509Data(true);
            x509Data.Certificate = x509cert;
            xmlSigner.KeyData = x509Data;

            //sign
            xmlSigner.Signature.ID = options.SignatureId;

            xmlSigner.UpdateReferencesDigest();
            xmlSigner.GenerateSignature();
            
            //get signature
            var node = sXmlDocument.FirstChild;
            xmlSigner.Signature.ID = options.SignatureId;
            xmlSigner.Save(ref node);
            string signature = node.FindNode("ds:Signature").OuterXML;
            XmlDocument signatureDoc = new XmlDocument();
            signatureDoc.LoadXml(signature);

            //cleanup
            certStorage.Dispose();
            xmlSigner.Dispose();

            return signatureDoc.DocumentElement;
        }


gives signature
Code
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="DivSignature">
      <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="#ClientSection">
          <ds:Transforms>
            <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
          </ds:Transforms>
          <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
          <ds:DigestValue>GE2keiXMHMsgkD8UFZwcaJPsSNo=</ds:DigestValue>
        </ds:Reference>
      </ds:SignedInfo>
      <ds:SignatureValue>LZ2c906H+cGfBO1k27rKexR9ynXmfwe+aNEE...0fQK/3B4bed7BAZrV2cDajFM34YN51dxuKg=</ds:SignatureValue>
      <ds:KeyInfo>
        <ds:KeyValue>
          <ds:RSAKeyValue>
            <ds:Modulus>rjXlH1ff2/mT+Am96...feR9+OWaWymXseMjEK0Li2RyGNshIeGOoKdEHaZcqJx78yfFN8hK+uRqHq7xrHL4iYb1s=</ds:Modulus>
            <ds:Exponent>AQAB</ds:Exponent>
          </ds:RSAKeyValue>
        </ds:KeyValue>
        <ds:X509Data>
          <ds:X509IssuerSerial>
            <ds:X509IssuerName>CN=ViktorRoot</ds:X509IssuerName>
            <ds:X509SerialNumber>277636348702376296889648764475334507354</ds:X509SerialNumber>
          </ds:X509IssuerSerial>
          <ds:X509SubjectName>CN=Fred</ds:X509SubjectName>
          <ds:X509Certificate>MIIC...UfV9/b+ZP4C...MkdW/ovPz...gBCI+cFFo...BAJT+6x1q...oTzQ/mMD3EvCmJf+wu04FHN7JX93bMdC8YrxiXeEq4lPa+ZgeQz3KYj49Q9RMC2Oe304rqlUSpICgODKtyRDjRqMZ6bmcC/BvQJ</ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </ds:Signature>


This signature could not be validated using .net's SignedXml class.

Same signing functionality using .net SignedXml

Code
       public XmlElement Sign(XmlDocument xmlDocument, SignatureOptions options, params string[] sectionIdsToSign)
        {
            var cert = GetCertificate(options);
            SignedXml signedXml = new SignedXml(xmlDocument);
            signedXml.SigningKey = cert.PrivateKey;

            foreach (string sectionId in sectionIdsToSign)
            {
                Reference reference = new Reference("#" + sectionId);
                
                reference.AddTransform(new XmlDsigC14NTransform());
                signedXml.AddReference(reference);
            }

            KeyInfo keyInfo = new KeyInfo();
            keyInfo.AddClause(new KeyInfoX509Data(cert, X509IncludeOption.EndCertOnly));
            keyInfo.AddClause(new RSAKeyValue((RSA)cert.PublicKey.Key));

            signedXml.KeyInfo = keyInfo;
            
            signedXml.Signature.Id = options.SignatureId;

            signedXml.ComputeSignature();
            return signedXml.GetXml();
        }


gives different signature

Code
    <Signature Id="DivSignature" xmlns="http://www.w3.org/2000/09/xmldsig#">
      <SignedInfo>
        <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
        <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
        <Reference URI="#ClientSection">
          <Transforms>
            <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
          </Transforms>
          <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
          <DigestValue>Eg2OfKKbZVw8l2E/aUxUy70LvoU=</DigestValue>
        </Reference>
      </SignedInfo>
      <SignatureValue>Fc/uiP6yHsK2z+rVV1IC7I6V0x6nSSZ7l7uo4NN4TE2RaX9ym2CH1oW2KxHKGBRnWl+5oE/yDcPuoJw67veJQlBBODV1meE1Mc2WcSIzv/61FKoTjc8e9+24A3s+l1+RlWl7j7EVOn3/hRi6q4if8NqOfhM1ze4o0cSl5ZCS+8k=</SignatureValue>
      <KeyInfo>
        <X509Data>
          <X509Certificate>MIIC...UfV9/b+ZP4C...MkdW/ovPz...gBCI+cFFo...BAJT+6x1q...oTzQ/mMD3EvCmJf+wu04FHN7JX93bMdC8YrxiXeEq4lPa+ZgeQz3KYj49Q9RMC2Oe304rqlUSpICgODKtyRDjRqMZ6bmcC/BvQJ</X509Certificate>
        </X509Data>
        <KeyValue>
          <RSAKeyValue>
            <Modulus>rjXlH1ff2/mT+Am96...feR9+OWaWymXseMjEK0Li2RyGNshIeGOoKdEHaZcqJx78yfFN8hK+uRqHq7xrHL4iYb1s=</Modulus>
            <Exponent>AQAB</Exponent>
          </RSAKeyValue>
        </KeyValue>
      </KeyInfo>
    </Signature>


Please notice that with the same c14n transform and same sha1 method DigestValue in Reference element differs. How this can be possible?
#17394
Posted: 08/31/2011 02:41:00
by Eugene Mayevski (EldoS Corp.)

"Can not be validated" gives not much information about the problem. The obvious difference is absence of "ds:" prefix in .NET output. You can achieve the same by reading this FAQ article. The prefix affects the value of the signature.


Sincerely yours
Eugene Mayevski
#17397
Posted: 08/31/2011 03:59:19
by Vladimir Kukov (Basic support level)
Joined: 08/31/2011
Posts: 6

I have tried removing "ds" prefix, the signature is still invalid.

I have stepped into .net code, the problem is with Reference hash which is invalid.

TElXMLSigner produces hash "GE2keiXMHMsgkD8UFZwcaJPsSNo=" for "#ClientSection" reference. (see my output sample) .NET implementation produces hash "Eg2OfKKbZVw8l2E/aUxUy70LvoU=" for same reference (transform and hash method are identical).
#17398
Posted: 08/31/2011 04:05:39
by Dmytro Bogatskyy (EldoS Corp.)

Quote
I have tried removing "ds" prefix, the signature is still invalid.

Using SignaturePrefix property?

Did your .Net validation code set XmlDocument.PreserveWhitespace property to true before loading a document?

Could you please attach xml documents? Posting them in a message breaks xml signature, as formatting of elements changed.
#17399
Posted: 08/31/2011 04:28:40
by Vladimir Kukov (Basic support level)
Joined: 08/31/2011
Posts: 6

Yes, using SignaturePrefix.
PreserveWhitespace has no effect too.

I am attaching entire Visual Studio 2010 solution.

Steps to reproduce problem:
1. Browse for simplefile.xml file (can be found in solution root).
2. Set your certificate serial (currently looks only in windows user store)
3. Press Sign

simplefile_signed.xml result will be signature created by .net (<DigestValue>Eg2OfKKbZVw8l2E/aUxUy70LvoU=</DigestValue>) ]

4. Change output file name to i.e. simplefile_signed_sb.xml
5. Change Xml manager radio button to "secure box".
6. Press Sign

simplefile_signed_sb.xml result will be signature created by SecureBlackbox
(<DigestValue>GE2keiXMHMsgkD8UFZwcaJPsSNo=</DigestValue>)

7. Browse for simplefile_signed_sb.xml, select "xmldsig" XML manager
8. Press Verify

.net validation obviously fails because of different reference hash.


[ Download ]
#17406
Posted: 08/31/2011 07:05:43
by Dmytro Bogatskyy (EldoS Corp.)

The problem is in PreserveWhitespace property and in this code:
Code
            //load xml document
            MemoryStream memoryBuffer = new MemoryStream();
            xmlDocument.Save(memoryBuffer);
            memoryBuffer.Position = 0;
            sXmlDocument.LoadFromStream(memoryBuffer, "utf-8", true);
            memoryBuffer.Dispose();

If you check sXmlDocument.OuterXML property you will see that XmlDocument .Net class automatically format document on saving (because PreserveWhitespace property is off).
The simple solution will be to enable PreserveWhitespace property before Save or Load method.
#17415
Posted: 09/01/2011 01:47:28
by Vladimir Kukov (Basic support level)
Joined: 08/31/2011
Posts: 6

PreserveWhitespace = true does not help.
But thank you for pointing at whitespace handling. In fact .net implementation ignores whitespaces and newlines when calculating hash. So to provide interopability with .net removing whitespaces and newlines does resolve my issue
#17416
Posted: 09/01/2011 02:42:43
by Eugene Mayevski (EldoS Corp.)

Thank you for reporting your findings. Can you please describe in more details (for other readers and for our future reference) what in particular you did to gain compatibility?


Sincerely yours
Eugene Mayevski
#17417
Posted: 09/01/2011 03:55:06
by Dmytro Bogatskyy (EldoS Corp.)

Quote
PreserveWhitespace = true does not help.

Possible, you enabled this property in a wrong place, for your sample, I meant, to enable it before a code posted in the previous message. It will do the trick.
However, you have many XmlDocument instances, and if you copy XmlElement from one document to another, you may get a problem similar to that you experience (.net implementation ignores whitespace characters and newlines when calculating hash).
The simplest solution is to enable PreserveWhitespace property for each instance of XmlDocument after you create it or to remove whitespace characters in the original xml document like you did.

From MSDN:
Quote

If PreserveWhitespace is true before Load or LoadXml is called, white space nodes are preserved; otherwise, if this property is false, significant white space is preserved, white space is not.
If PreserveWhitespace is true before Save is called, white space in the document is preserved in the output; otherwise, if this property is false, XmlDocument auto-indents the output.
...
The default is false.
#17418
Posted: 09/01/2011 06:34:49
by Vladimir Kukov (Basic support level)
Joined: 08/31/2011
Posts: 6

Just to clarify. In order to provide compatability with .NET SignedXml functionality all whitespaces and newlines between elements should be removed.
SignedXml gives same reference hash result for documents with or without whitespaces.

In my case XmlDocument.InnerXml was helpfull as it returns markup without formatting:

Code
            string xml = xmlDocument.InnerXml;
            MemoryStream str = new MemoryStream(Encoding.UTF8.GetBytes(xml));
            sXmlDocument.LoadFromStream(str, "utf-8", true);
            str.Close();
Also by EldoS: CallbackRegistry
A component to monitor and control Windows registry access and create virtual registry keys.

Reply

Statistics

Topic viewed 3522 times

Number of guests: 1, registered members: 0, in total hidden: 0




|

Back to top

As of July 15, 2016 EldoS Corporation will operate as a division of /n software inc. For more information, please read the announcement.

Got it!