When dealing with secure web services, it is often necessary to digitally sign SOAP messages to guarantee the authenticity and integrity of the transmitted data. This article describes a comprehensive MATLAB solution to perform the following:
Our solution leverages MATLAB's built-in capabilities as well as Java interoperability to perform operations that are not natively supported by MATLAB. An entire code snippet is presented that integrates these operations in a coherent workflow.
The following sections describe each step in detail.
The first step involves converting XML data into a Document Object Model (DOM). MATLAB’s matlab.io.xml.dom
package or Java-based XML parsing can be used to accomplish this. The code snippet below demonstrates how to use MATLAB's Java integration capabilities to parse an XML string into a DOM Document object.
% Import necessary packages for XML parsing and DOM manipulation
import matlab.io.xml.dom.*;
import javax.xml.parsers.DocumentBuilderFactory;
% Define your XML string – it can be any valid XML.
xmlString = ['<!-- Sample XML with comments -->' newline, ...
'<root>', newline, ...
' <!-- This is a comment -->', newline, ...
' <data>Example content</data>', newline, ...
'</root>'];
% Use Java's DocumentBuilder to parse the XML string.
factory = DocumentBuilderFactory.newInstance();
% Enable the processing of comments
factory.setIgnoringComments(false);
builder = factory.newDocumentBuilder();
isr = java.io.StringReader(xmlString);
inputSource = org.xml.sax.InputSource(isr);
doc = builder.parse(inputSource);
% The variable "doc" now holds the DOM representation of the XML document.
In the code above, comments in the XML are preserved by ensuring the document builder does not ignore them. This is important for canonicalization if comments are required to be included.
Canonicalization is the process of converting XML content into a standardized format. This is crucial for digital signatures, ensuring that semantically equivalent XML documents have an identical physical representation. The W3C canonicalization standard (inclusive of comments) specifies rules for whitespace, attribute order, and more. MATLAB does not provide a built-in function to achieve full canonicalization; thus, a simplified version using Java's transformation libraries can be used as a placeholder.
% Import Java classes for transformation
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
% Create a Transformer instance with indentation properties.
transformerFactory = TransformerFactory.newInstance();
transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, 'no');
transformer.setOutputProperty(OutputKeys.INDENT, 'yes');
% Note: For true canonicalization, further customization or third-party libraries
% must be used to strictly follow the W3C canonicalization standard with comments.
% Use a StringWriter to capture the output of the transformer.
sw = java.io.StringWriter();
source = DOMSource(doc);
result = StreamResult(sw);
% Transform the DOM document to a canonicalized XML string.
transformer.transform(source, result);
canonicalizedXML = char(sw.toString());
The output of this transformation is a standardized XML string. Although this example uses basic Java transformations, for complete canonicalization according to the W3C spec (including proper treatment of namespaces and comments), consider using a dedicated library such as org.apache.xml.security.c14n.Canonicalizer
if available.
At this stage, the canonicalized XML representation must be hashed using the SHA-256 algorithm. The resulting hash is then encrypted using the RSA algorithm. Java’s java.security
package is employed via MATLAB to sign the digest with a private key.
% Import Java security classes for digest and signature generation.
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.nio.file.Files;
import java.nio.file.Paths;
% Create a MessageDigest instance for SHA-256.
messageDigest = MessageDigest.getInstance('SHA-256');
% Calculate the digest of the canonicalized XML string.
% Ensure the string is converted to bytes.
digest = messageDigest.digest(uint8(canonicalizedXML));
% Load the private key from a file (assumes PEM format with no header/footer conversion).
% For a production environment, ensure that the key format is parsed correctly.
keyPath = 'privateKey.der'; % The key should be in DER format for this example.
privateKeyBytes = Files.readAllBytes(Paths.get(keyPath));
keySpec = PKCS8EncodedKeySpec(privateKeyBytes);
% Generate the private key using the RSA algorithm.
privateKey = KeyFactory.getInstance('RSA').generatePrivate(keySpec);
% Create a Signature object using SHA256withRSA.
signatureInstance = Signature.getInstance('SHA256withRSA');
% Initialize signature object with the private key.
signatureInstance.initSign(privateKey);
% Update the signature object with the digest bytes.
signatureInstance.update(digest);
% Generate the RSA signature.
signedBytes = signatureInstance.sign();
In this code, the SHA-256 digest of the XML is computed first. The digest is then signed using RSA with a private key which must be provided in an appropriate format. Adjust the file path and key format as necessary for your application.
To ensure safe transmission of binary data in text-based formats such as XML, the signature is base64 encoded. MATLAB's Java interoperation allows us to use Java’s encoder.
% Using Java's Base64 encoder to convert the byte array into a base64 encoded string.
encoder = Base64.getEncoder();
base64Signature = char(encoder.encodeToString(signedBytes));
The variable base64Signature
now contains the signature as a base64 string, suitable for embedding within XML content.
With the base64-encoded signature available, the final step is to embed it into a properly formatted SOAP message. The SOAP envelope includes a header for the security (Signature) information and a body where the primary XML content may reside.
% Construct a SOAP message as a string with placeholders for the signature and the XML payload.
% The XML payload can be the original XML or another message as per your application requirements.
xmlPayload = canonicalizedXML; % This example reuses the canonicalized XML as the payload.
soapMessage = sprintf(['<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">' newline, ...
' <soap:Header>' newline, ...
' <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">' newline, ...
' <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">' newline, ...
' <ds:SignatureValue>%s</ds:SignatureValue>' newline, ...
' </ds:Signature>' newline, ...
' </wsse:Security>' newline, ...
' </soap:Header>' newline, ...
' <soap:Body>' newline, ...
' %s' newline, ...
' </soap:Body>' newline, ...
'</soap:Envelope>'], base64Signature, xmlPayload);
% Display the SOAP message to the MATLAB Command Window.
disp(soapMessage);
This SOAP message includes a security header that houses the signature (according to XML digital signature standards) and a body that contains the XML payload.
The following table summarizes each step in the process, describing the purpose and key operations involved:
Step | Operation | Key MATLAB/Java Functions |
---|---|---|
1. DOM Representation | Parse XML string into a DOM using Java DocumentBuilder | DocumentBuilderFactory, parse, StringReader |
2. Canonicalization | Transform DOM into standardized XML; preserve comments | Transformer, StreamResult, DOMSource |
3. SHA-256 Digest & RSA Signature | Generate hash digest and sign it using RSA with a private key | MessageDigest, Signature, PKCS8EncodedKeySpec |
4. Base64 Encoding | Encode binary signature into a string for XML embedding | java.util.Base64 |
5. SOAP Message Construction | Embed the base64 signature and payload into a SOAP envelope | sprintf, disp |
Combining all the steps discussed above, the following complete MATLAB code example demonstrates the entire process end-to-end:
%==========================================================================
% MATLAB Code: XML Digital Signature and SOAP Message Creation
%==========================================================================
try
%% Step 1: Create a DOM Representation of the XML Data
import matlab.io.xml.dom.*;
import javax.xml.parsers.DocumentBuilderFactory;
% Define XML string with comments
xmlString = ['<!-- Sample XML with Comments -->' newline, ...
'<root>' newline, ...
' <!-- Comment inside XML -->' newline, ...
' <data>Example Content</data>' newline, ...
'</root>'];
% Initialize the DocumentBuilderFactory
factory = DocumentBuilderFactory.newInstance();
% Ensure comments are preserved during parsing
factory.setIgnoringComments(false);
builder = factory.newDocumentBuilder();
isr = java.io.StringReader(xmlString);
inputSource = org.xml.sax.InputSource(isr);
doc = builder.parse(inputSource);
%% Step 2: Create a Canonicalized Representation of the DOM Data
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
% Create a transformer instance with formatting properties
transformerFactory = TransformerFactory.newInstance();
transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, 'no');
transformer.setOutputProperty(OutputKeys.INDENT, 'yes');
% Transform the DOM into a string using a StringWriter
sw = java.io.StringWriter();
source = DOMSource(doc);
result = StreamResult(sw);
transformer.transform(source, result);
canonicalizedXML = char(sw.toString());
%% Step 3: Compute SHA-256 Digest and Create RSA Signature
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.nio.file.Files;
import java.nio.file.Paths;
% Compute the SHA-256 digest of the canonicalized XML content
messageDigest = MessageDigest.getInstance('SHA-256');
digest = messageDigest.digest(uint8(canonicalizedXML));
% Load RSA private key from a file (DER format)
keyPath = 'privateKey.der'; % Make sure this file exists and is appropriately formatted.
privateKeyBytes = Files.readAllBytes(Paths.get(keyPath));
keySpec = PKCS8EncodedKeySpec(privateKeyBytes);
privateKey = KeyFactory.getInstance('RSA').generatePrivate(keySpec);
% Create RSA signature for the SHA-256 digest using SHA256withRSA algorithm
signatureInstance = Signature.getInstance('SHA256withRSA');
signatureInstance.initSign(privateKey);
signatureInstance.update(digest);
signedBytes = signatureInstance.sign();
%% Step 4: Base64 Encode the Signature
encoder = Base64.getEncoder();
base64Signature = char(encoder.encodeToString(signedBytes));
%% Step 5: Place the Signature in the SOAP Message
% Prepare your SOAP message payload (here, we are using the canonicalized XML as an example)
xmlPayload = canonicalizedXML;
soapMessage = sprintf(['<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">' newline, ...
' <soap:Header>' newline, ...
' <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">' newline, ...
' <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">' newline, ...
' <ds:SignatureValue>%s</ds:SignatureValue>' newline, ...
' </ds:Signature>' newline, ...
' </wsse:Security>' newline, ...
' </soap:Header>' newline, ...
' <soap:Body>' newline, ...
' %s' newline, ...
' </soap:Body>' newline, ...
'</soap:Envelope>'], base64Signature, xmlPayload);
% Display the produced SOAP message
disp(soapMessage);
fprintf('SOAP message creation successful.\n');
catch ME
fprintf('An error occurred: %s\n', ME.message);
end
%==========================================================================
This complete code handles XML parsing, canonicalization (simplified), digest computation, RSA signing, base64 encoding, and SOAP message construction. Adjust file paths, key formats, and parameters as necessary to fit your specific application environment.
This detailed MATLAB implementation demonstrates how to create a SOAP message with a digitally signed XML payload. It covers the entire workflow from transforming raw XML into a DOM representation and canonicalizing it (while preserving comments per W3C recommendations) to signing the resulting SHA-256 digest with RSA using a private key. The binary signature is encoded into a base64 string and then embedded in a SOAP envelope.
The provided code uses MATLAB’s integration with Java to accomplish tasks that are not directly supported by MATLAB, such as XML canonicalization and RSA signing. While the canonicalization implemented here is a simplified version, it serves as a solid foundation. For production-level applications, further enhancements may be necessary, including the use of dedicated libraries to fully adhere to the W3C canonicalization standard and robust error handling. This comprehensive solution thus provides a powerful template for ensuring secure communications through SOAP in MATLAB.