Click here to Skip to main content
13,800,806 members
Click here to Skip to main content
Add your own
alternative version

Stats

10.2K views
2 bookmarked
Posted 9 Sep 2018
Licenced CPOL

The *AdES Collection, Part 2: XAdES signing for Windows in C++

, 11 Oct 2018
Rate this:
Please Sign up or sign in to vote.
XAdES-B and XAdES-T signing with my AdES and XML libraries

Introduction

This is a four part article about Advanced Electronic Signatures. The first part talks about CAdES, this part talks about XAdES, the third part is about PAdES and the final part considers ASiC.

My previous article about CAdES used the AdES library to create CMS Advanced Electronic Signatures, a set of extensions to Cryptographic Message Syntax (CMS) signed data making it suitable for advanced electronic signatures.

Using the same library, now you can sign XML data using XAdES, extension to XMLDSIG for signing XML documents.

While XAdES is way more complex than CAdES, it has one benefit: signed XML documents can still be read by encryption-unaware applications.

Background

CMS can sign any sort of binary data, so why a XML specific method? Simply because an application that is not aware of cryptography cannot read its data if they are enclosed in a CMS format. XML signing allows cryptography elements to be present in a XML document while an application can still read its data.

XMLDSIG is a protocol describing how an XML document may be signed. It defines three methods of signing:

  • Detached: the signature resides in another location
  • Enveloping: the signature contains the element to be signed
  • Enveloped: the element to be signed contains the signature as a child node.

A detached XML signature can refer to any sort of data, not just XML.

My library supports all the above signature modes. It uses my XML library, updated to support canonicalization.

Current limitations:

  • No verification
  • No CDatas, namespaces or comments in XML files
  • Supports up to XAdES-XL
  • Hash support SHA-1 and SHA-256

XML Canonicalization

There are unlimited valid representations for the same XML data, for example 

<foo /> equals to <foo></foo>
<foo val="yo" a=   "b" /> equals to <foo  a="b" val="yo" />

Therefore, in order to make sure that hashing does not vary, we have to make XML in canonical form, i.e. a standard 1-1 mapping of the same data to the same XML. This process is really weird, with some of the rules (but not all) below:

  • DocType headers are removed.
  • Elements must not be closed with />.
  • Atrributes are sorted alphabetically, but xmlns: namespaced attributes go first.
  • Specific whitespace is trimmed.
  • Namespace declarations propagade to children (this was a real pain for me in trying to find out why hashing did not match).

A nice practical guide is here. For sake of simplicity, our library does not support CDatas, comments, or namespaces. The official document is here

If you use detached XML signatures to sign a XML file, then this file needs not to be canonicalized, because detached XML signatures can work on plain binary data. However, if you consider detached signatures, why the need of XML signature anyway?

XMLDSIG

The signing process is as follows:

  • Canonicalization of the element to be signed, if not using detached signatures.
  • Hashing of the element.
  • Creating a SignedInfo element which contains everything to be signed (transforms, message hash, algorithm)
  • Signing of the above element.
  • Creating an element which contains all the information.
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
        <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
        <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
        <Reference URI="">
            <Transforms>
                <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
            </Transforms>
            <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
            <DigestValue>...</DigestValue>
        </Reference>
    </SignedInfo>
    <SignatureValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#">...</SignatureValue>
    <KeyInfo>
        <X509Data>
            <X509Certificate>...</X509Certificate>
        </X509Data>
    </KeyInfo>
</Signature>

The reference URI is empty when it is an enveloped signature. For detached signatures, it contains an URI to the data. Note that the signature value is not a complete PKCS#7 message, but only the encrypted hash. Therefore, we cannot use CryptSignMessage to build it, we will use the low level message functions instead (CryptMsgOpenToEncode,CryptMsgUpdate, etc.).

The SignedInfo element can contain as many references as we want, allowing us to sign many portions of data in one operation.

Windows has a CryptXML api to create a XMLDSIG but, since it is useless for XAdES, we will not use it here.

XAdES

XAdES builds on XMlDSIG with the following rules:

  • All elements are namespaced with ds:
  • SignedInfo element contains references to a set of SignedProperties
  • SignedInfo contains also a reference to the certificate. This allows us to sign in one operation the message, the signed properties and the certificate.
  • Unsigned properties are also added, similar to CAdES. When using XAdES-T, the entire ds:SignatureValue element is timestamped, not just the signature.
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<doc>
    <e2>hohoho</e2>
    <e3 id="elem3"/>
    <e6 a="http://www.w3.org">
        <e7 b="http://www.ietf.org">
            <e8 c="">
                <e9 d="http://www.ietf.org"/>
            </e8>
        </e7>
    </e6>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 

     Id="xmldsig-345B805C-ED11-469F-920A-AA82A6E02876">
        <ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
            <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:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
                <ds:DigestValue>TOSPv1v7yuYBhD56IgG5Wp8+3pkWmJEUO+QecU5/A3g=</ds:DigestValue>
            </ds:Reference>
            <ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" 

             URI="#xmldsig-345B805C-ED11-469F-920A-AA82A6E02876-sigprops">
                <ds:Transforms>
                    <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
                </ds:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
                <ds:DigestValue>0o9tulf/mGgQCINlIJ/fcZHc6DziU6XH9x8iXiqMat8=</ds:DigestValue>
            </ds:Reference>
            <ds:Reference URI="#xmldsig-345B805C-ED11-469F-920A-AA82A6E02876-keyinfo">
                <ds:Transforms>
                    <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
                </ds:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
                <ds:DigestValue>hflupFvhNJWQir2grNd7QK8RWtm0m2pAE8QNdRd8jIQ=</ds:DigestValue>
            </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 

         Id="xmldsig-345B805C-ED11-469F-920A-AA82A6E02876-sigvalue">
         lknnInZl2Xxp1ZeMLM+qUj/vyoyMvkxFoOB0EcqE0z14eEW1xmpLqWT/GcJRTqceOrFLZ98C6JXtIh1
         mdhF45Avo3ZC98I3ZU/jdwZ3nOlKRa0NB8+sSQADPD3CKwLIgJh07Nr3xlHenc/yqn1whLTVU7aC1tc
         MYXYQhyeux2DJ7+qyDTKgqKIMoH4NMc+JPMp3qwu0dxqBlgZz0g43kEpsgjrakwtqRp4VqFnmHQOsIr
         6XEnBNPXk8tTV+5yshHkSF1ELRHV2feSr7RvNHA5ZtRFSs4jCd24gyVT/P5YR8MIaN3Ir4ictp9SnCX
         a+/0+g6BfsKP1ykOIk5dQzOy5w==</ds:SignatureValue>
        <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 

         Id="xmldsig-345B805C-ED11-469F-920A-AA82A6E02876-keyinfo">
            <ds:X509Data>
                <ds:X509Certificate>...</ds:X509Certificate>
            </ds:X509Data>
        </ds:KeyInfo>
        <ds:Object>
            <xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" 

             xmlns:xades141="http://uri.etsi.org/01903/v1.4.1#" 

             Target="#xmldsig-345B805C-ED11-469F-920A-AA82A6E02876">
                <xades:SignedProperties xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 

                 xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" 

                 xmlns:xades141="http://uri.etsi.org/01903/v1.4.1#" 

                 Id="xmldsig-345B805C-ED11-469F-920A-AA82A6E02876-sigprops">
                    <xades:SignedSignatureProperties>
                        <xades:SigningTime>2018-09-09T10:12:24Z</xades:SigningTime>
                        <xades:SigningCertificateV2>
                            <xades:Cert>
                                <xades:CertDigest>
                                    <ds:DigestMethod Algorithm=
                                        "http://www.w3.org/2001/04/xmlenc#sha256"/>
                                    <ds:DigestValue>//0HypHbOffTJiry5S2iLFrxs6D1iPRmKZ4ShysSwxE=
                                    </ds:DigestValue>
                                </xades:CertDigest>
                                <xades:IssuerSerialV2>
                                    <ds:X509SerialNumber>18446744073709551615</ds:X509SerialNumber>
                                </xades:IssuerSerialV2>
                            </xades:Cert>
                        </xades:SigningCertificateV2>
                        <xades:SignaturePolicyIdentifier>
                            <xades:SignaturePolicyId>
                                <xades:SigPolicyId>
                                    <xades:Identifier>1.3.6.1.5.5.7.48.1</xades:Identifier>
                                </xades:SigPolicyId>
                                <xades:SigPolicyHash>
                                    <ds:DigestMethod Algorithm=
                                           "http://www.w3.org/2001/04/xmlenc#sha256"/>
                                    <ds:DigestValue>i8brzJOzs5A+2MFR/jxNzm+LaGGBQ7pNHV2uImgbY68=
                                           </ds:DigestValue>
                                </xades:SigPolicyHash>
                            </xades:SignaturePolicyId>
                        </xades:SignaturePolicyIdentifier>
                    </xades:SignedSignatureProperties>
                    <xades:SignedDataObjectProperties>
                        <xades:CommitmentTypeIndication>
                            <xades:CommitmentTypeId>
                                <xades:Identifier>http://uri.etsi.org/01903/v1.2.2#ProofOfOrigin
                                </xades:Identifier>
                                <xades:Description>Indicates that the signer recognizes 
                                 to have created, approved and sent the signed data object
                                </xades:Description>
                            </xades:CommitmentTypeId>
                            <xades:AllSignedDataObjects/>
                        </xades:CommitmentTypeIndication>
                    </xades:SignedDataObjectProperties>
                </xades:SignedProperties>
                <xades:UnsignedProperties>
                    <xades:UnsignedSignatureProperties>
                        <xades:SignatureTimeStamp>
                            <ds:CanonicalizationMethod Algorithm=
                             "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
                            <xades:EncapsulatedTimeStamp>...</xades:EncapsulatedTimeStamp>
                        </xades:SignatureTimeStamp>
                    </xades:UnsignedSignatureProperties>
                </xades:UnsignedProperties>
            </xades:QualifyingProperties>
        </ds:Object>
    </ds:Signature>
</doc>

This is a valid XAdES-T message containing signed properties and a timestamp.

In the UnsignedAttributes element, more levels can be added (for example, the C level).

Using the code

struct FILEREF
{
	const char* data = 0; // pointer to data
	DWORD sz = 0; // size, or 0 if null terminated XML
	const char* ref = 0;
	std::string mime = "application/octet-stream";
};

HRESULT XMLSign(LEVEL lev, std::vector<FILEREF>& data,const std::vector<CERT>& Certificates,SIGNPARAMETERS& Params, std::vector<char>& Signature);

Where:

  • lev is a value from the LEVEL enum (XMLDSIG, B,T). If you use XMLDSIG then the XML file is verifiable also with the Windows CryptXML API.
  • data contains the data to be signed. Each structure in the vector has:
    • A pointer to the bytes. If this is XML data and the signing mode is ENVELOPED, then this is a null terminated string and the second tuple parameter (DWORD) is zero. In this case, the data is signed as canonicalized XML and returned in enveloped mode.
    • If the mode is detached, then the two tuple parameters contain the pointer and size to the data, which is signed as raw.
    • The third parameter is the URI reference put in the signature. If this is an enveloped signature and the data is XML, this can be zero.
    • The fourth parameter is the MIME type of the content, by default, application/octet-stream.
  • Certificates contain the certificates to use for signing. If the mode is ENVELOPED, only one certificate is allowed.
  • Params is a structure that defines:
    • Attached from the ATTACHTYPE enum (DETACHED, ENVELOPING or ENVELOPED)
    • Hashing algorithm (default SHA-256, SHA-1 can also be specified)
    • Signing Policy
    • Timestamp parameters (URL, Policy, Nonce, Extensions)
    • The OID of a commitment type (1.2.840.113549.1.9.16.6.1 to 6)
  • Signature receives the signature.

If the mode is ENVELOPED, then the returned Signature is a single XMLElement which contains the original data and the enveloped signature.

If the mode is ENVELOPING with 1 certificate, then a single ds:Signature element is returned which contains the signature and a ds:Object element which contains the orignal data. If there are multiple certificates, multiple ds:Signature elements are returned in a <root> root element.

If the mode is DETACHED with 1 certificate, then a single ds:Signature element is returned. If there are multiple certificates, multiple ds:Signature elements are returned in a <root> root element.

For enveloped signatures, multiple signing is not possible for, when you add a signature to a XML element that already contains a ds:Signature element, the first signature will be rendered invalid (the hash of the content would change).

The XAdES file produced by my library validates as 100% correct at the ETSI conformance checker tools :)

Acknowledgements

History

  • 14th October, 2018: Changed parameters
  • 23th September, 2018: Added multiple certificate support
  • 22th September, 2018: Added enveloping mode
  • 14th September, 2018: Parameter updating
  • 12th September, 2018: Canonicalization info, ETSI tools
  • 9th September, 2018: First release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Michael Chourdakis
Engineer
Greece Greece
I'm working in C++, PHP , Java, Windows, iOS and Android.

I 've a PhD in Digital Signal Processing and Artificial Intelligence and I specialize in Pro Audio and AI applications.

My home page: http://www.michaelchourdakis.com

You may also be interested in...

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web03 | 2.8.181215.1 | Last Updated 11 Oct 2018
Article Copyright 2018 by Michael Chourdakis
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid