What a way to start off a new category on my blog called "Interoperability".
I recently came across an interesting post in the forums that goes something like
this:
[QUOTE]
Currently I'm working with Visual Studio 2003 in
order to generate xmldsig signature. I'm using the class signedxml to create
the xmldsig signature and I get somthing like this:
[Signature xmlns="http://www.w3.org/2000/09/xmldsig#"]
[SignedInfo]
[CanonicalizationMethod
Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" /]
............
[/Signature]
But I need the signature to be in a namespace that should
be identified by
the dsig prefix:
[dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"]
[dsig:SignedInfo]
.....
[/dsig:Signature]
I really didnt think anything of this. At first glance, I thought the problem lies
not in the code BUT the processor / validator that was used to read this.
The dsig or any prefix, for that matter, doesn't indicate whether they 2 use
different namespaces. Check
the [default] namespaces and compare.
Strictly speaking -
-
[ds:Signature xmlns:ds="urn:softwaremaker.net-foo.bar" /]
-
[dsig:Signature xmlns:dsig="urn:softwaremaker.net-foo.bar" /]
-
[Signature xmlns="urn:softwaremaker.net-foo/bar /]
are isomorphically the
same. If the end processor / validator reads it and treats differently, I believe
that it should be a design flaw at the other end as it is really poor design
to rely on namespace prefix.
If you look at the _XML-DSIG_ Specifications, Section 1.3 states that:
This namespace is also used as the prefix for algorithm identifiers
used by this specification. While applications MUST support XML and XML namespaces,
the use of internal entities [XML] or our "dsig" XML namespace prefix and defaulting/scoping
conventions are OPTIONAL; we use these facilities to provide compact and readable
examples.
Therefore, it is NOT necessarily to have a prefix to it as long as it points to the
same namespace.
However, I spoke too fast. Further explanations by the other party has made me put
my thinking cap on. He provided 2 reasons being:
-
Compatibility with our existing signer.
-
We are planning to extend the signature to _XAdES_ format. In that case the prefix
is mandatory.
I am surprised [which kinda shows how much I know, or dont know ???]. I spent some
minutes digging into the _XAdES_ specifications and true enough, it declares:
The XML schema definition in clause 5 Qualifying properties
syntax defines the prefix "ds" for all the XML elements already defined in [XMLDSIG],
and states that the default namespace is the one defined for the present document.
In consequence, in the examples of this clause, the elements already defined in [XMLDSIG]
appear with the prefix "ds", whereas the new XML elements defined in the present document
appear without prefix.
XMLDSIG
|
<ds:Signature ID?>- - - - - - - - -+- - - - -+
<ds:SignedInfo> | |
<ds:CanonicalizationMethod/> | |
<ds:SignatureMethod/> | |
(<ds:Reference URI? > | |
(<ds:Transforms>)? | |
<ds:DigestMethod> | |
<ds:DigestValue> | |
</ds:Reference>)+ | |
</ds:SignedInfo> | |
<ds:SignatureValue> | |
(<ds:KeyInfo>)?- - - - - - - - - + |
|
<ds:Object> |
|
<QualifyingProperties> |
|
<SignedProperties> |
|
<SignedSignatureProperties> |
(SigningTime) |
(SigningCertificate) |
(SignaturePolicyIdentifier) |
(SignatureProductionPlace)? |
(SignerRole)? |
</SignedSignatureProperties> |
|
<SignedDataObjectProperties> |
(DataObjectFormat)* |
(CommitmentTypeIndication)* |
(AllDataObjectsTimeStamp)* |
(IndividualDataObjectsTimeStamp)* |
</SignedDataObjectProperties> |
|
</SignedProperties> |
|
<UnsignedProperties> |
|
<UnsignedSignatureProperties> |
(CounterSignature)* |
</UnsignedSignatureProperties> |
|
</UnsignedProperties> |
|
</QualifyingProperties> |
|
</ds:Object> |
|
</ds:Signature>- - - - - - - - - - - - - - - +
|
XAdES
|
Readers must take into account that the XAdES forms build
up on the[XMLDSIG] by adding new XML elements containing qualifying information within
the shown [XMLDSIG]ds:Object element, according to the rules defined in the present
document. This ds:Object element will act as a bag for the whole set of qualifying
properties defined in the present document, conveniently grouped.
So, there are 2 questions to answer here:
-
Is there a way to handle the Digital Signature prefix in the SignedXML
Class in .NET Framework 1.1
-
If so - How ? If not - How ?
I decided to spend some time on this and after much disassembling some of the System.Security.Crytography.XML binaries,
I found out to my dismay that the answer to Question [1] is NO. This is because the
constants and the URIs of the XML Digital Signature functions in the System.Security.Crytography.XML space
are found in the XMLSignature class and that class is declared
as an internal class
i-frown.
Therefore, the answer to Question [2] would be to build our own customized Digital
Signature stack. This may actually sound harder than it is. Truth is:- With Reflector and
work done behind the MONO-Project and
published on Koders.com, I hacked
a workaround in a few hours time. That actually means that I didnt really do much
testing on it and so I disclaim myself
from any liabilities, including, but not limited to, mistakes, injuries, deaths,
etc caused if you choose to use it.
You would use this assembly just like you would with System.Security.Cryptography.Xml.
The namespace would be Softwaremaker.NET.Security.Cryptography.Xml.PfDsigInterop.
Do take note that I ONLY implemented the XML Digital Signature here.
Imports System
Imports System.IO
Imports System.Security.Cryptography
Imports System.Xml
Imports System.text
Imports Mono.Xml
Imports System.Text.UTF8Encoding
Imports Softwaremaker.NET.Security.Cryptography.Xml.PfDsigInterop
myRSA = New RSACryptoServiceProvider
myRSA.FromXmlString("...")
Dim doc As XmlDocument = New XmlDocument
doc.PreserveWhitespace = False
doc.Load(New XmlTextReader("..."))
Dim mySignedXML As SignedXml = New SignedXml(doc)
mySignedXML.SigningKey = myRSA
'
Create a data object to hold the data to sign.
Dim dataObject As New DataObject
dataObject.Data = doc.ChildNodes
dataObject.Id = "someSWMId"
'
Add the data object to the signature.
mySignedXML.AddObject(dataObject)
Dim ref As New Reference
ref.Uri = "#someSWMId"
mySignedXML.AddReference(ref)
mySignedXML.ComputeSignature()
Dim xmldg As XmlElement = mySignedXML.GetXml
'
Append the element to the XML document.
doc.DocumentElement.AppendChild(doc.ImportNode(xmldg, True))
'
Save the signed XML document to a file
Dim xmltw As New XmlTextWriter("...", New UTF8Encoding(False))
doc.WriteTo(xmltw)
xmltw.Close()
To verify the signed XML, we would just have to use back the System.Security.Cryptography.Xml found
in the .NET Framework. At least, the .NET stack got the design of the namespaces and
the prefixes right.
'
Create a new XML document.
Dim xmlDocument As New XmlDocument
'
Load the passedXML file into the document.
xmlDocument.Load("...")
'
Create a new original SignedXml object and pass it the XML document class.
Dim signedXml As New System.Security.Cryptography.Xml.SignedXml
'
Find the "Signature" node and create a new XmlNodeList object.
Dim xmlnsmgr As New XmlNamespaceManager(xmlDocument.NameTable)
xmlnsmgr.AddNamespace("SWM", "http://www.w3.org/2000/09/xmldsig#")
Dim nodeList As XmlNodeList = xmlDocument.SelectNodes("//SWM:Signature",
xmlnsmgr)
signedXml.LoadXml(CType(nodeList(0),
XmlElement))
myRSA = New RSACryptoServiceProvider
myRSA.FromXmlString("...")
Return signedXml.CheckSignature(a)
You can download my [prefixed-XMLDSIG] custom assembly here.
Do let me know if you have any comments or feedback. Enjoy !!!