Start Chat
Search
Ithy Logo

XML Digital Signature Process

Detailed Steps for Creating and Signing an XML Document

scenic rock formation during sunset

Highlights

  • DOM Creation: Use APIs such as DOMParser (JavaScript) or minidom (Python) to parse XML strings into a document structure.
  • Canonicalization: Normalize the XML according to the XML C14N specification (with comments) to ensure consistency for signing.
  • RSA-SHA256 Signature: Digest the canonicalized XML with SHA-256 and encrypt it using the participant’s private key, then base64 encode the signature for inclusion in a SOAP message.

Step-by-Step Process

1. Creating a DOM Representation of the XML Data

Overview

The very first step involves parsing the provided XML string to generate a DOM (Document Object Model) representation. This structure enables programmatic manipulation of the XML data. Depending on your development environment, a variety of approaches are available. In JavaScript, the DOMParser API is ideal, while in Python, modules such as xml.dom.minidom allow similar functionality.

JavaScript Implementation

Below is an example in JavaScript which demonstrates how to create a DOM representation of our XML data.


// Define the XML data as a string
const xmlString = `<CsbReport ApplicationType="CSB_REPORT" ParticipantName="PT_400198">
    <ReportListRequest ReportType="STTL_STATEMENT" MarketName="BALIMB" RunType="INDICATIVE" 
       StartDate="2024-06-09" EndDate="2024-06-09"/>
</CsbReport>`;

// Create a DOMParser instance
const parser = new DOMParser();

// Parse XML string into a DOM Document
const xmlDoc = parser.parseFromString(xmlString, "text/xml");

// The 'xmlDoc' object is now a DOM representation of the XML data
console.log(xmlDoc);
  

Note that using DOMParser, the XML is parsed to create a tree structure that can be navigated and manipulated.

Python Implementation

For those using Python, the minidom module can be used similarly:


# Import the required module
from xml.dom import minidom

# Define the XML data
xml_data = """<CsbReport ApplicationType="CSB_REPORT" ParticipantName="PT_400198">
    <ReportListRequest ReportType="STTL_STATEMENT" MarketName="BALIMB" RunType="INDICATIVE" 
       StartDate="2024-06-09" EndDate="2024-06-09"/>
</CsbReport>"""

# Parse the XML string to get a DOM representation
doc = minidom.parseString(xml_data)

# Output the DOM as an XML string for verification
print(doc.toxml())
  

2. Creating a Canonicalised Representation of the DOM Data

What is Canonicalization?

Canonicalization (C14N) is the process of converting XML content into a standard, normalized format. This is crucial prior to performing any digital signature operations, as it ensures the quality and consistency of the XML structure regardless of minor formatting differences, such as whitespace or attribute ordering. In our requirement, we are following the method described by the XML C14N standard with comments (http://www.w3.org/TR/2001/REC-xmlc14n-20010315#WithComments).

Canonicalization Example in JavaScript

While some environments require specialized libraries for canonicalization (e.g., xmldom combined with a canonicalizer), a simplified approach using the XMLSerializer (in JavaScript) can be applied for basic canonicalization. Note that this simplified method might not handle every nuance of the official standard.


// Serialize the DOM representation back to string
const serializer = new XMLSerializer();
let canonicalXml = serializer.serializeToString(xmlDoc);

// Simplified approach: In practice, you might need a dedicated library to ensure that the XML 
// adheres strictly to the canonicalization guidelines with comments.
console.log("Canonical XML:", canonicalXml);
  

Canonicalization in Python

Python developers can use the lxml library to perform canonicalization. This library provides support for strictly following the C14N method.


from lxml import etree

# Parse the original XML using lxml
root = etree.fromstring(xml_data)

# Canonicalize the XML with comments included (note: ensure the proper method or library is used)
canonicalized_xml = etree.tostring(root, encoding="unicode", method="xml")

# Normalize spacing by removing unnecessary newline or tab characters
canonicalized_xml = canonicalized_xml.replace('\n', '').replace('\t', '')
print("Canonicalized XML:", canonicalized_xml)
  

3. Creating the RSA-SHA256 Signature

Digesting the Canonical XML

After canonicalization, the next step is to compute the SHA-256 digest of the canonicalized XML string. This digest serves as the input for the RSA signature process. Using SHA-256 ensures that the hash is robust and secure.

In modern applications, the Web Crypto API (for browsers) or Python’s Cryptography library can be employed to perform the digest and sign operations.

JavaScript Example with Web Crypto API

Although a fully functional implementation requires possessing a private key, below is a conceptual illustration:


async function generateRSASignature(canonicalXml, privateKey) {
  // Convert the canonical XML into a Uint8Array for processing
  const encoder = new TextEncoder();
  const data = encoder.encode(canonicalXml);

  // Compute the SHA-256 digest
  const hashBuffer = await crypto.subtle.digest("SHA-256", data);

  // Create the signature by signing the hash with the RSA private key using RSASSA-PKCS1-v1_5
  const signatureBuffer = await crypto.subtle.sign(
    {
      name: "RSASSA-PKCS1-v1_5",
      hash: { name: "SHA-256" }
    },
    privateKey, // Your RSA private key must be imported as CryptoKey
    hashBuffer
  );
  
  // Convert the signature from an ArrayBuffer into a base64-encoded string
  const signatureArray = new Uint8Array(signatureBuffer);
  let binaryString = "";
  signatureArray.forEach(byte => binaryString += String.fromCharCode(byte));
  const base64Signature = btoa(binaryString);
  
  return base64Signature;
}

// Example usage: generateRSASignature(canonicalXml, myPrivateKey).then(console.log);
  

Python Example with Cryptography Library

In Python, you can utilize the Cryptography library to generate the SHA-256 digest and sign it with an RSA key.


from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend

# Load the private key from a PEM file
with open("private_key.pem", "rb") as key_file:
    private_key = serialization.load_pem_private_key(
        key_file.read(),
        password=None,
        backend=default_backend()
    )

# Compute the SHA-256 digest of the canonicalized XML
digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(canonicalized_xml.encode('utf-8'))
hashed_data = digest.finalize()

# Create the RSA signature using PSS padding
signature = private_key.sign(
    hashed_data,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA256()
)
  

4. Encoding the Binary Signature with Base64

Why Base64?

The binary signature produced by the RSA encryption is not directly suitable for inclusion in XML documents. Encoding it to Base64 converts these bytes into a text string, which is safe for XML and other textual data formats, including SOAP messages.

Encoding in JavaScript

The example code given in the signing step using the Web Crypto API already demonstrates converting the signature to Base64. In JavaScript, the use of btoa achieves this conversion.

Encoding in Python

Python’s base64 module can perform this encoding:


import base64

# Convert the binary signature to a Base64-encoded string
base64_signature = base64.b64encode(signature).decode('utf-8')
print("Base64 Signature:", base64_signature)
  

5. Embedding the Signature into the SOAP Message

SOAP Message Structure

In SOAP-based web services, the digital signature is typically embedded within the SOAP header. The Signature element, which encapsulates the Base64-encoded signature, becomes part of the security header.

A sample SOAP envelope including a Signature element is illustrated below.

Example SOAP Message with Signature


<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
    <soap:Header>
        <wsse:Security soap:mustUnderstand="true" 
            xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <ds:Signature Id="Signature-001" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <ds:SignedInfo>
                    <ds:CanonicalizationMethod 
                        Algorithm="http://www.w3.org/TR/2001/REC-xmlc14n-20010315#WithComments"/>
                    <ds:SignatureMethod 
                        Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
                    <ds:Reference URI="#CsbReport">
                        <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>[CalculatedDigestValue]</ds:DigestValue>
                    </ds:Reference>
                </ds:SignedInfo>
                <ds:SignatureValue>[Base64Signature]</ds:SignatureValue>
                <ds:KeyInfo>
                    <ds:KeyValue>
                        <ds:RSAKeyValue>
                            <ds:Modulus>[ModulusValue]</ds:Modulus>
                            <ds:Exponent>[ExponentValue]</ds:Exponent>
                        </ds:RSAKeyValue>
                    </ds:KeyValue>
                </ds:KeyInfo>
            </ds:Signature>
        </wsse:Security>
    </soap:Header>
    <soap:Body>
        <CsbReport ApplicationType="CSB_REPORT" ParticipantName="PT_400198" id="CsbReport">
            <ReportListRequest ReportType="STTL_STATEMENT" MarketName="BALIMB" 
                RunType="INDICATIVE" StartDate="2024-06-09" EndDate="2024-06-09"/>
        </CsbReport>
    </soap:Body>
</soap:Envelope>
  

Replace [Base64Signature] with the generated Base64 signature string and fill in the digest and key details as required by your implementation and environment.


6. Storing the XML Data

Storage Options

Once the XML has been generated and signed, it needs to be stored securely for further processing or audit. There are several methods for storing XML data:

  • File System: Save the XML as a file on disk. Suitable for logging purposes or systems where XML files are processed by batch jobs.
  • Database Storage: Store the XML string in a database column for retrieval and processing by another service or application.
  • In-Memory Storage: For scenarios requiring temporary persistence, XML data may be stored in memory or in a session store.

Example: Storing XML Data in JavaScript (Browser Storage)

If you are dealing with a browser-based application, you might store the XML in the local storage:


// Convert the DOM back to an XML string
const xmlDataToStore = serializer.serializeToString(xmlDoc);

// Store the XML data in the browser's localStorage
localStorage.setItem('csbReportXML', xmlDataToStore);
  

Example: Storing XML Data Using Node.js

In a server-side environment using Node.js, you can write the XML data to a file:


const fs = require('fs');

// Define the SOAP message which includes the signature
const soapMessage = `<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Header>...</soap:Header>
  <soap:Body>
    ${xmlDataToStore}
  </soap:Body>
</soap:Envelope>`;

// Write the SOAP message to an XML file
fs.writeFileSync("output.xml", soapMessage, "utf8");
  

Example: Storing XML Data in Python

Python also provides straightforward file handling capabilities:


# Define the SOAP message with the XML data
soap_message = f"""<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
    <soap:Header>...</soap:Header>
    <soap:Body>
        {canonicalized_xml}
    </soap:Body>
</soap:Envelope>"""

# Write the SOAP message to a file
with open("output.xml", "w", encoding="utf-8") as file:
    file.write(soap_message)
  

Detailed Process Table

Step Description Key Functions/Methods
DOM Creation Generate a DOM from the XML string using parsing libraries. DOMParser (JavaScript), minidom (Python)
Canonicalization Normalize the XML to a consistent format as per the XML C14N standard with comments. XMLSerializer, lxml.etree.tostring()
Digital Signature Compute SHA-256 digest and sign it with the RSA private key. crypto.subtle.digest/sign (JavaScript), Cryptography library (Python)
Base64 Encoding Convert the binary signature to a text string suitable for XML. btoa() (JavaScript), base64.b64encode() (Python)
SOAP Message Embed the signature into a SOAP envelope for secure data interchange. String interpolation, XML templating
Data Storage Store the XML data for future processing using file or database systems. LocalStorage, fs.writeFileSync (Node.js), file write in Python

Conclusion

In summary, the process of creating and signing an XML document involves several crucial stages. You begin by parsing the XML data to obtain a DOM representation, which allows for structured manipulation. This representation is then canonicalized to ensure the XML is in a normalized state, following the XML C14N with comments standard. Once canonicalization is complete, you digest the normalized XML using SHA-256 and then sign the digest using RSA encryption with the participant’s private key. The resulting binary signature must be encoded into Base64 to be safely embedded within a SOAP message. Finally, the signed SOAP message can be stored using various methods, depending on your system’s architecture. Each of these steps is foundational to achieving a secure, standard-compliant XML digital signature, ensuring both data integrity and authenticity in web services.

References

Recommended


Last updated February 24, 2025
Ask Ithy AI
Download Article
Delete Article