XMLDsig is a W3C Standard for digital signatures of XML documents. It is a reliable platform for building interoperable solutions where the data can be produced and/or consumed in different systems developed with different technologies.
I am going to show how to create and validate digital signatures in C# and Java so that both can sign and validate interoperably (sign in C# or Java, validate in C# or Java).
Let’s start with some theory. Assume we have an XML document we are going to sign.
There are three types of signatures:
- enveloped – the signature is embedded in the document as a descendant node of the root node of the document
- enveloping – the signature becomes the root of the document and the actual document becomes a direct child of the signature’s root element (possibly wrapped in an auxiliary tag, Object in the example below)
- detached – the signature and the document become siblings and are contained in a shared root element (Envelope in the example below) (for technical reasons we will discuss, the document is possibly wrapped in an auxiliary tag, Message in the example below)
The signing is performed in in three steps and two different crypto algorithms must be used:
- the signed document is canonicalized with the C14N algorithm to ensure that different formatting does not impact on the signature
- a hash of the signed document is computed using one of hashing algorithms (e.g. MD5/SHA1/SHA256)
- the signature is encrypted with the private key of an asymmetric algorithm (DSA/RSA)
The signature is then composed with all these elements so that it contains the information on the combination of algorithms used, the public key of the asymmetric algorithm and the actual value of the encrypted hash:
This example signature says that the combination of SHA1 (hash)/RSA(asymmetric) will be used together with the C14N canonicalizer.
Then, there is the DigestValue which is the Base64-ed value of the computed hash of the document. The hash is encrypted and included in the SignatureValue tag. At the end, there is the information on the certificate used to encrypt the signature – the public key is included in the signature. If an X509 certificate was used then the KeyInfo contains the Base64-ed value of the X509 certificate (the private key is not included!).
To validate the signature the client performs following steps:
- the document is canonicalized
- the hash of the signature is recomputed and compared to the declared DigestValue. If they do not match then the validation fails
- the public key from the KeyInfo section of the signature is used to decrypt the signature and compare the decrypted value to the DigestValue. If the comparison fails then the validation fails.
What we are going to do in subsequent entries is to be able to create any of the three types of signatures in C# and in Java and to be able to verify signatures in C# and in Java. The most interesting case is of course when one signs and the other one verifies – which will truly prove that XML signatures are interoperable.
This is not as obvious as it sounds just because both C# and Java have sligthly different XMLDSig API so that you could potentially have signatures created in C# that validate in C# but not in Java and vice versa.