Signing XML files using YubiHSM 2

Many applications make use of XML to structure data stored in files, databases, or elsewhere. To establish trust in such data, these documents can be signed using XML Signatures.

In order to sign XML documents you can use a tool called xmlsectool. As xmlsectool is implemented as a Java application using the JCA en JCE standards, we can use a YubiHSM 2 to store the signing keys we use for generating XML signatures.

A simple example

As an example, generate an RSA key pair and a self-signed certificate stored on the YubiHSM 2:

keytool -keystore NONE -storetype PKCS11 -storepass 0001password -addProvider SunPKCS11 -providerArg ./sunpkcs11.conf -genkey -alias rsaSign -keyalg RSA -dname CN=rsaSign
Generating 2,048 bit RSA key pair and self-signed certificate (SHA256withRSA) with a validity of 90 days
        for: CN=rsaSign

As before, we are using the SunPKCS11 provider to interface with the YubiHSM2, similar to other examples. See Usage_Guides/Using_YubiHSM2_with_Java/example_signing_with_YubiHSM2.adoc for more details.

Signing XML files

Let’s generate a very simple XML file:

echo '<x></x>' > unsigned.xml

Sign the XML file using xmlsectool:

xmlsectool --sign --pkcs11Config ./sunpkcs11.conf --inFile unsigned.xml --keyAlias rsaSign --keyPassword 0001password --outFile signed.xml

INFO  XMLSecTool - Reading XML document from file 'unsigned.xml'
INFO  XMLSecTool - XML document parsed and is well-formed.
INFO  XMLSecTool - XML document successfully signed
INFO  XMLSecTool - XML document written to file /home/user/signed.xml

The signed XML document nog contains a Signature element containing the a SignatureValue and a KeyInfo element containing a copy of the X.509 certificate on the YubiHSM 2:

<x>
  <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <ds:SignedInfo>
      <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
      <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
      <ds:Reference URI="">
        <ds:Transforms>
          <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
          <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
        </ds:Transforms>
        <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
        <ds:DigestValue>9hy1oK7rXCJu4rTqLZ7cGUH3rPyGm4QllC8VRv6mX60=</ds:DigestValue>
      </ds:Reference>
    </ds:SignedInfo>
    <ds:SignatureValue>ce5SooQsD...aiUDiOkaBiWI8A4olAuRcIgme0PqeLg==</ds:SignatureValue>
    <ds:KeyInfo>
      <ds:KeyValue>
        <ds:RSAKeyValue>
          <ds:Modulus>uSsZh/aAk...MK4yY1LTUqF2HzSO9d4vGdWzwm4Z63ot6w==</ds:Modulus>
          <ds:Exponent>AQAB</ds:Exponent>
        </ds:RSAKeyValue>
      </ds:KeyValue>
      <ds:X509Data>
        <ds:X509Certificate>MIICxzCCAa+g.../BUkO7i8reQw+6qA==</ds:X509Certificate>
      </ds:X509Data>
    </ds:KeyInfo>
  </ds:Signature>
</x>

In the above document, we have shortened the Base64 encoded text elements for brevity.

Verifying XML digital signatures

To verify the signed XML file, we can also use xmlsectool:

xmlsectool --verifySignature --inFile signed.xml --pkcs11Config ./sunpkcs11.conf --keyAlias rsaSign --keyPassword 0001password
INFO  XMLSecTool - Reading XML document from file 'signed.xml'
INFO  XMLSecTool - XML document parsed and is well-formed.
INFO  XMLSecTool - XML document signature verified.

Here, we are referring to the signing certificate stored on the YubiHSM 2. to be able to verify signatures when direct access to the YubiHSM 2 is not available, we need to export the signing certificate and distribute it to whoever needs to be able to verify such signatures.

To export the signing certificate stored on a YubiHSM 2 using keytool:

keytool -keystore NONE -storetype PKCS11 -storepass 0001password -addProvider SunPKCS11 -providerArg ./sunpkcs11.conf -exportcert -alias rsaSign -rfc > signing-crt.pem

We can now use xmlsectool to verify an XML digital signature using the public key in the signing certificate:

xmlsectool  --verifySignature --inFile signed.xml --certificate signing-crt.pem
INFO  XMLSecTool - Reading XML document from file 'signed.xml'
INFO  XMLSecTool - XML document parsed and is well-formed.
INFO  XMLSecTool - XML document signature verified.

In case the signature does not verify, xmlsectool will complain:

xmlsectool  --verifySignature --inFile signed.xml --certificate signing-crt.pem
INFO  XMLSecTool - Reading XML document from file 'signed.xml'
INFO  XMLSecTool - XML document parsed and is well-formed.
WARN  XMLSignature - Signature verification failed.
ERROR XMLSecTool - XML document signature verification failed
make: *** [verify] Error 7

In this case, either the XML document was changed after its signature was generated, or the public key in the certificate does not match the private key used for signing. Either way, the XML signature cannot be used to establish trust in the XML document’s authenticity.

For more informatin, see Using PKCS11 Credentials from the xmlsectool documentation.

A real-world example: SAML metadata signing

One example application of using XML signatures is in identity federation, where users can logon to a web application after authenticating somewhere else. A well-known protocol used for identity federation is SAML 2.0, and this protocol is based on XML.

The parties where users may want to logon (called Service Provicers) need to exchange information with the parties where users authenticate (called Identity Providers), and this SAML 2.0 Metadata is typically signed using XML Signatures so it can be automatically verified by SAML peers.

Consider the following SAML 2.0 metadata document for a ficticious Service Provider which specifies its identifier (entity ID), its SAML signing certificate and the URL endpoint an Identity Provider can direct users to in order to process a SAML authentication response:

<md:EntityDescriptor  ID="XYZ123456"
    xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://example.com/saml/sp.xml">
  <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" >
    <md:KeyDescriptor>
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:X509Data>
          <ds:X509Certificate>
MIHnMIGiAgEBMA0GCSqGSIb3DQEBBAUAMA8xDTALBgNVBAMMBHNpZ24wHhcNMjMw
MTA1MTI0ODExWhcNMjgwNjI3MTI0ODExWjAPMQ0wCwYDVQQDDARzaWduMEwwDQYJ
KoZIhvcNAQEBBQADOwAwOAIxAKrBRhYU03MSaU8jBPNUx9wcc6bWhMpinZmINR0J
Ndh3Sk/Pddh7zskcLGonFsmasQIDAQABMA0GCSqGSIb3DQEBBAUAAzEADng7opb7
8PNoLZH1QzYqmxV0ZSc3rE0OlTW00W/Xq7+77OhU5vVAVYnXpQLlv6sB
</ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </md:KeyDescriptor>
    <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" index="0" Location="https://example.com/saml/acs"/>
  </md:SPSSODescriptor>
</md:EntityDescriptor>

Note that the certificate in the Metadata is intended for validating SAML protocol messages and is typically different from the certificate used for validating SAML 2.0 metadata. Either or both certificates can have their private keys stored on the YubiHSM 2, but be aware that SAML protocol messages are signed much more frequently than SAML metadata documents, so the former may require multiple YubiHSM 2 deployments in order to scale with the load on your SAML IdP or SP.

To sign this SAML metadata document, we again use xmlsectool with the signing key stored in a YubiHSM 2. We also specify ID as the name of the XML attribute to use in the XML signature.

xmlsectool --sign --pkcs11Config ./sunpkcs11.conf  --inFile unsigned.xml --keyAlias rsaSign --keyPassword 0001password --outFile signed.xml --referenceIdAttributeName ID

As before, we will need to export the SAML signing certificate to distribute among our SAML peers so they can validate our signed metadata.