IWConnect XML Digital Signature Snap Pack
Marjan Sterjev
IT Engineer | CISSP | CCSP | CEH (Master): research | learn | do | MENTOR
SnapLogic is an excellent Integration Platform as a Service (iPaaS). It is based on Java and provides tons of integration components (Snaps) out of the box. At the same time, it supports custom Snap components development. The development documentation is online and available for everyone. These couple of sentences I wrote at the beginning convey the main message I want to share here: there are no limits if you want to develop some custom functionality not provided directly by the vendor. ?The platform is based on Java, which is, under my humble opinion, the most mature enterprise grade language that has available libraries and platforms for everything.
One of the Snap functionalities not provided out of the box, but sometimes important for the enterprises, is XML Digital Signature. The SOA concept started with the XML Web Services and the SOAP protocol. There are many WS-* specifications related to SOAP digital signatures, reliability, notifications etc. Large enterprises still rely on these protocols and specifications for reliable message delivery with strict rules related to the message integrity and confidentiality. It is fair to say that remoting protocols have evolved in time and today we are dealing with REST endpoints, JSON, GraphQL. But, it is also fair to say that these protocols and/or specifications are under specified compared with the XML Web Services, sometimes missing important enterprise grade features.
XML Digital Signature is something that is added to the XML document before sending it to the recipient. It provides integrity (the signed parts were not modified) and non-repudiation (the sender owning the private key cannot claim that she did not send that message).
<Signature
<SignedInfo>
<CanonicalizationMethod />
<SignatureMethod />
<Reference>
<Transforms />
<DigestMethod />
<DigestValue />
</Reference>
<Reference /> etc.
</SignedInfo>
<SignatureValue />
<KeyInfo />
<Object />
</Signature>>
In essence, the simplified process is:
Java supports XML Digital Signatures and Oracle provides basic usage example. It seems that we have found an excellent opportunity to wrap enterprise grade functionality into custom SnapLogic Snap Pack.
IWConnect engineers recently created this custom Snap functionality. The package contains:
The Crypto Key Pair Account accepts private keys in PKCS8 format. For example, an Elliptic Curve private key could be:
-----BEGIN PRIVATE KEY----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgcbi/wfQ3XKnHOOa+
kY0b6slEsOqojH5tsm4hualSYxyhRANCAAQ/m16G2ochyPf9k/TCFvxRF5dUVnQh
YUQHdzheCF6rpU77/gfoRf7ja1VeglZTxRdwlXR4EZ+0KiEklB3G7NjU
-----END PRIVATE KEY------
The private key in the account is marked with high sensitivity. SnapLogic platform encrypts the key at rest and it is not visible in the UI once configured.
The Crypto Key Pair Account accepts public keys in X509 format. For example, an Elliptic Curve public key could be:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEP5tehtqHIcj3/ZP0whb8UReXVFZ0
IWFEB3c4Xgheq6VO+/4H6EX+42tVXoJWU8UXcJV0eBGftCohJJQdxuzY1A==
-----END PUBLIC KEY-----
Let us say that we are dealing with the following XML document:
<?xml version="1.0" encoding="UTF-8"?>
<iwc:Employee xmlns:iwc="https://iwconnect.com">
?? ?<iwc:PersonalInfo iwc:id="personal">
?? ??? ?<iwc:FirstName>Marjan</iwc:FirstName>
?? ??? ?<iwc:LastName>Sterjev</iwc:LastName>
?? ??? ?<iwc:Age>48</iwc:Age>
?? ?</iwc:PersonalInfo>
?? ?<iwc:ProfessionalInfo iwc:id="professional">
?? ??? ?<iwc:Title>CTO</iwc:Title>
?? ?</iwc:ProfessionalInfo>
</iwc:Employee>
The Signer Snap configuration for signing both references iwc:id="personal" and iwc:id="professional" by using SHA3_512 as digest method and ECDSA_SHA256 as signature method could be:
The Signer works against string specified in the input document as a field with name xml.
It is worth mentioning what XML ID attribute name and XML ID attribute namespace mean in the Signer’s configuration above. That is the tricky part in the implementation that is not explained that well in the official Java documentation. When we deal with HTML, most JavaScript developers are comfortable with calls like document.getElementById(id). In HTML, the attribute with name id is the one that distinguishes HTML elements. The id attribute has been defined in the HTML specification. However, in arbitrary XML documents, the literal id attribute has no meaning at all, unless you define that meaning by yourself. XML Digital Signature relies on References when deciding what shall be digested and signed.
For example:
领英推荐
<Reference URI=”#personal”>…</Reference>
<Reference URI=”#professional”>…</Reference>
So, how can we find the "#personal" and "#professional" references in the Employee document above? Note that the XML document has the attribute iwc:id for that purpose. The iwc prefix is bound to the https://iwconnect.com namespace. That's what has been configured in the Signer's screen above:
The Java code that deals with the referenced element search logic is XPath based and looks something like the following snippet:
String xPathExpression = null;
if (xmlIdAttributeNamespace == null)
?? ?xPathExpression = String.format("https://*[@%s=\"%s\"]", xmlIdAttributeName, reference);
else
?? ?xPathExpression = String.format("https://*[@%s:%s=\"%s\"]", DigSigNamespaceContext.IDNS,
?? ??? ??? ?xmlIdAttributeName, reference);
xPath.setNamespaceContext(namespaceContext);
Element elementToSign = (Element) xPath.compile(xPathExpression).evaluate(doc, XPathConstants.NODE);
if (elementToSign == null) {
?? ?writeError(.......);
?? ?return;
}
domSigningContext.setIdAttributeNS(elementToSign, xmlIdAttributeNamespace, xmlIdAttributeName);
The last line with the call setIdAttributeNS() makes all of this work.
The Validator Snap configuration could be:
The XML ID attribute name and XML ID attribute namespace properties have the same meaning as in the Signer Snap. Now, the second, even more tricky part, is related to the properties XML Element Reference Parent Name and XML Element Reference Parent Namespace. Why do we need these properties?
In the world of XML Digital Signatures exists well known attack known as XML Signature Wrapping. Simplified explanation of the attack is:
In order to prevent these situations, we can specify the XML element that is the expected parent of the signed references. The expected parent shall be somewhere within the business payload visible to the application logic. This way the attacker can't move the original signed reference into arbitrary place in the XML document and put malicious stubs on their place.
The Java XPath processing in the Validator Snap is similar with the one presented for the Signer:
String referencesExpression = String.format("https://%s:Signature/%s:SignedInfo/%s:Reference",
?? ??? ??? ?DigSigNamespaceContext.XMLDSIG, DigSigNamespaceContext.XMLDSIG, DigSigNamespaceContext.XMLDSIG);
NodeList references = (NodeList) xPath.compile(referencesExpression).evaluate(doc, XPathConstants.NODESET);
for (int i = 0; i < references.getLength(); i++) {
?? ?Element reference = (Element) references.item(i);
?? ?String uri = reference.getAttribute("URI");
?? ?if (uri == null || !(uri.startsWith("#") || "".equals(uri)))
?? ??? ?throw new SnapDataException("Invalid XML Digital Signature Reference URI:" + uri);
?? ?if ("".equals(uri))
?? ??? ?continue;
?? ?String nodeId = uri.substring(1);
?? ?String nodeExpression = "";
?? ?if (xmlElementReferenceParentName != null) {
?? ??? ?if (xmlElementReferenceParentNamespace == null)
?? ??? ??? ?nodeExpression = String.format("https://%s", xmlElementReferenceParentName);
?? ??? ?else
?? ??? ??? ?nodeExpression = String.format("https://%s:%s", P, xmlElementReferenceParentName);
?? ?}
?? ?if (xmlIdAttributeNamespace == null)
?? ??? ?nodeExpression = String.format("%s//*[@%s=\"%s\"]", nodeExpression, xmlIdAttributeName, nodeId);
?? ?else
?? ??? ?nodeExpression = String.format("%s//*[@%s:%s=\"%s\"]", nodeExpression, DigSigNamespaceContext.IDNS,
?? ??? ??? ??? ?xmlIdAttributeName, nodeId);
?? ?Node signedDataNode = (Node) xPath.compile(nodeExpression).evaluate(doc, XPathConstants.NODE);
?? ?if (signedDataNode == null)
?? ??? ?throw new SnapDataException(
?? ??? ??? ??? ?String.format("Signed data node with id '%s' in the search path '%s' does not exist!",
?? ??? ??? ??? ??? ??? ?nodeId, nodeExpression));
?? ?domValidateContext.setIdAttributeNS((Element) signedDataNode, xmlIdAttributeNamespace,
?? ??? ??? ?xmlIdAttributeName);
}
The output of the Validator Snap contains the XML string, the validation status and the public key the XML has been validated with (locally configured or embedded in the XML document as a KeyInfo element).
The public key information available in the output could be used in some downstream Snaps for the signer (subject) identification (a map of public-key <-> subject name for example).
If the document has not been validated, the errors are routed to the Error view.
It works! Probably there are more tricky use cases that can be added in the Validator Snap implementation, but we know where to look for if needed :). Tomorrow is a new day, brand new challenges and solutions for the IWConnect employees!
...because, you know, we connect the dots!...
Marjan
Software Engineer == Akta Manniskor
2 年Nice snap, keep on snapping to connect the dots.