Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Add enveloped signature to SOAP header

52 views
Skip to first unread message

Marcus Olk

unread,
Apr 1, 2024, 12:38:13 PM4/1/24
to ipf-user
Hi there.

I have to add a signature to an incoming ITI-18 query and forward it to a target XDS registry that expects this enriched header.


My current approach is to process the incoming message like follows:

@Override
public void process(Exchange exchange) throws Exception {
   final Message message = exchange.getIn();

   Security security = securityHeader();

   addToMessageHeaders(security, message.getHeaders());
}

This method is implemented in the org.apache.camel.Processor I added
and I am struggling to implement the methods used to add the security header.

So the route processing the incoming messages is defined like follows:

@Override
public void configure() {

  from("xds-iti18:xds-iti18?audit=true&secure=true")
    .process(iti18RequestValidator())
    .process(myProcessor())
    .to(appConfig.getXdsBackendEndpoint())
    ;

}

The expected transformation looks like follows:

<?xml version="1.0" encoding="UTF-8"?>

<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing"
    xmlns:s="http://www.w3.org/2003/05/soap-envelope">

    <s:Header>
        <a:Action s:mustUnderstand="1">urn:ihe:iti:2007:RegistryStoredQuery</a:Action>
        <a:MessageID>urn:uuid:6d63ca0c-4fb5-4bac-b60b-fcc4e4ef52b3</a:MessageID>
        <a:ReplyTo>
            <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
        </a:ReplyTo>
        <a:To s:mustUnderstand="1">http://localhost:9091/xds-iti18</a:To>
    </s:Header>

    <s:Body xmlns:s="http://www.w3.org/2003/05/soap-envelope">
        <query:AdhocQueryRequest xmlns:lcm="urn:oasis:names:tc:ebxml-regrep:xsd:lcm:3.0"
            xmlns:query="urn:oasis:names:tc:ebxml-regrep:xsd:query:3.0"
            xmlns:rim="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"
            xmlns:rs="urn:oasis:names:tc:ebxml-regrep:xsd:rs:3.0">

            <query:ResponseOption returnComposedObjects="true" returnType="LeafClass"></query:ResponseOption>

            <rim:AdhocQuery id="urn:uuid:14d4debf-8f97-4251-9a74-a90016b0af0d">
                <rim:Slot name="$XDSDocumentEntryPatientId">
                    <rim:ValueList>
                        <rim:Value>'121212^^^&amp;1.3.4.5&amp;ISO'</rim:Value>
                    </rim:ValueList>
                </rim:Slot>
                <rim:Slot name="$XDSDocumentEntryStatus">
                    <rim:ValueList>
                        <rim:Value>('urn:oasis:names:tc:ebxml-regrep:StatusType:Approved')</rim:Value>
                    </rim:ValueList>
                </rim:Slot>
            </rim:AdhocQuery>

        </query:AdhocQueryRequest>
    </s:Body>

</s:Envelope>


The body is left untouched and the enriched header is expected to have as Security
element with a digital signature like follows:

    <s:Header>
        <ns5:Security
            xmlns:ns5="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
            xmlns:ns4="urn:oasis:names:tc:SAML:2.0:assertion"
            xmlns:ns3="http://www.w3.org/2000/09/xmldsig#"
            xmlns:ns2="urn:hl7-org:v3">
            <ns4:Assertion ID="9f85f74e-81da-4119-977a-dc468b34db6b" Version="2.0" IssueInstant="2024-04-01T12:57:51.994Z">
                <Issuer .../>
                <Subject ... />
                <Conditions .../>
                <AuthnStatement .../>
                <AttributeStatement>
                    <Attribute Name="urn:oasis:names:tc:xacml:2.0:subject:role">
                        <AttributeValue ... />
                    </Attribute>
                </AttributeStatement>
 
                <ns3:Signature>
                    <SignedInfo .../>
                    <SignatureValue>ZyFG ...</SignatureValue>
                    <KeyInfo>
                        <X509Data>
                            <X509SubjectName>...</X509SubjectName>
                            <X509Certificate>MII...</X509Certificate>
                        </X509Data>
                    </KeyInfo>
                </ns3:Signature>

            </ns4:Assertion>
        </ns5:Security>
        ...
    </s:Header>

As XML security is a bag of hurt, and I need to intercept some how here,
did someone already realise something like this and could help here?

Thanks!

Marcus

Dmytro Rud

unread,
Apr 1, 2024, 12:57:44 PM4/1/24
to ipf-...@googlegroups.com
Hello Marcus

Do I understand that you need help in implementing the method addToMessageHeaders()?

What is the type "Security" in your code?  Is there any possibility to represent it as XML (e.g. DOM, or XML String)?

Best regards
Dmytro


--
You received this message because you are subscribed to the Google Groups "ipf-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ipf-user+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ipf-user/993f0b38-9394-4702-b245-360169e78d70n%40googlegroups.com.

Marcus Olk

unread,
Apr 1, 2024, 1:37:01 PM4/1/24
to ipf-user
Hey Dmytro.

Thanks for your reply. I realized the addToMessageHeaders method already, but I am stuggling to create a signature
and add it as part of the Security element, so here is the header I added so far:

<ns5:Security xmlns:ns5="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
    xmlns:ns4="urn:oasis:names:tc:SAML:2.0:assertion"
    xmlns:ns3="http://www.w3.org/2000/09/xmldsig#"
    xmlns:ns2="urn:hl7-org:v3">
    <ns4:Assertion ID="7c4b8df8-d5da-4da3-9190-286740ad30c5" Version="2.0" IssueInstant="2024-04-01T17:15:00.170Z">
        <Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:X509SubjectName">CN=ITI-18 Enricher,O=marcus-olk.net,OU=HC,L=Hamburg,ST=Hamburg,C=DE</Issuer>
        <Subject>
            <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName">CN=ITI-18 Enricher,O=marcus-olk.net,OU=HC,L=Hamburg,ST=Hamburg,C=DE</NameID>
            <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"/>
        </Subject>
        <Conditions NotBefore="2024-04-01T17:15:01.170Z" NotOnOrAfter="2024-04-01T17:16:00.170Z"/>
        <AuthnStatement AuthnInstant="2024-04-01T17:15:00.170Z">
            <SubjectLocality Address="1.1.1.1" DNSName="CARADIGM"/>
            <AuthnContext>
                <AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</AuthnContextClassRef>
            </AuthnContext>
        </AuthnStatement>
        <AttributeStatement>
            <Attribute Name="urn:oasis:names:tc:xspa:1.0:subject:subject-id">
                <AttributeValue>Keyhie Provider</AttributeValue>
            </Attribute>
            <Attribute Name="urn:oasis:names:tc:xspa:1.0:subject:organization">
                <AttributeValue>KeyHIE</AttributeValue>
            </Attribute>
            <Attribute Name="urn:oasis:names:tc:xspa:1.0:subject:organization-id">
                <AttributeValue>urn:oid:2.16.840.1.113883.3.189</AttributeValue>
            </Attribute>
            <Attribute Name="urn:nhin:names:saml:homeCommunityId">
                <AttributeValue>2.16.840.1.113883.3.189.2.215</AttributeValue>
            </Attribute>

            <Attribute Name="urn:oasis:names:tc:xacml:2.0:subject:role">
                <AttributeValue>
                    <ns2:Role xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CE" code="View Normal Medical Data" codeSystem="2.16.840.1.113883.3.37.4.1.9.202" codeSystemName="ICW Role Codes" displayName="View Normal Medical Data"/>
                </AttributeValue>
                <AttributeValue>
                    <ns2:Role xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CE" code="XDS Query Documents" codeSystem="2.16.840.1.113883.3.37.4.1.9.202" codeSystemName="ICW Role Codes" displayName="XDS Query Documents"/>
                </AttributeValue>
            </Attribute>
            <Attribute Name="urn:oasis:names:tc:xspa:1.0:subject:purposeofuse">
                <AttributeValue>
                    <PurposeOfUse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CE" code="TREATMENT" codeSystem="2.16.840.1.113883.3.18.7.1" codeSystemName="nhin-purpose" displayName="Treatment"/>
                </AttributeValue>
            </Attribute>
        </AttributeStatement>
    </ns4:Assertion>
</ns5:Security>


I need to sign the assertion element preventing it from manipulation (especially the Role attributes):

<ns4:Assertion ID="7c4b8df8-d5da-4da3-9190-286740ad30c5" Version="2.0" IssueInstant="2024-04-01T17:15:00.170Z">
...

As I am adding the elements I pasted as XML here above as Java objects (JAXB) like e.g. the Security element (see below),
I am wondering on how to calculate a valid signature now.

@NoArgsConstructor @AllArgsConstructor
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "Security")
public class Security {

   @XmlElementRef
   private Assertion assertion;

}


So, am I following the right route here?

Thanks in advance!

Marcus

Dmytro Rud

unread,
Apr 1, 2024, 1:49:39 PM4/1/24
to ipf-...@googlegroups.com
IPF does not provide the corresponding routines (yet), but you may take a look at the implementation in the IPF-based project Husky:
code -- https://github.com/project-husky/husky/tree/develop/husky-communication/husky-xua

(Disclaimer: I have not used Husky by myself, but it works from what I know.)

Best regards
Dmytro


Marcus Olk

unread,
Apr 1, 2024, 2:12:30 PM4/1/24
to ipf-user
Thanks for your reply pointing at project Husky.
But I have to admit, that I can't make up on how to integrate ITI-18 and ITI-40 here, as I was not aware of ITI-40 - yet.
Are there any examples out there, yet?

Regarding my approach: manipulating XML as flexible as I expected it to be offered by IPF/CXF is not possible, you'd reckon?

Marcus

Dmytro Rud

unread,
Apr 1, 2024, 2:52:16 PM4/1/24
to ipf-...@googlegroups.com
ITI-40 is 'just' a prescription to provide a XUA SAML token in a SOAP header, and to copy some contents of this token into ATNA audit record.  In that way, your code already does it, the only problem is to create the SAML token.  Consideration of ITI-40 in ATNA audit records can be enabled by adding "ipf-commons-ihe-xua" as a Maven dependency to your project.

The SAML token can be created using the Husky project, or -- on a lower layer -- using Apache WSS4J (https://ws.apache.org/wss4j/) and OpenSAML.  The most complicated task, that prevents the creation of SAML tokens using standard Java XML libraries or even just strings, is the creation of signatures.

Best regards
Dmytro



Marcus Olk

unread,
Apr 1, 2024, 5:43:00 PM4/1/24
to ipf-user
I have to admit, that I still don't see the way forward on how to add the required signature now.
What am I missing here? I've started reading the docs, but it seems as if Husky does not provide
some straight forward way to sign the desired element of the header and add the signature to it, does it?

Marcus

Michael Knapp

unread,
Apr 2, 2024, 12:23:13 AM4/2/24
to ipf-...@googlegroups.com
Am Mo., 1. Apr. 2024 um 23:43 Uhr schrieb Marcus Olk <to.mar...@gmail.com>:
I have to admit, that I still don't see the way forward on how to add the required signature now.
What am I missing here? I've started reading the docs, but it seems as if Husky does not provide
some straight forward way to sign the desired element of the header and add the signature to it, does it?


I don't know whether helpful or not:
This is how I add the security header to ITI-18 for example

List<SoapHeader> soapHeaders = CastUtils.cast((List<?>) exchange.getIn().getHeader(AbstractWsEndpoint.OUTGOING_SOAP_HEADERS));

if (soapHeaders == null) soapHeaders = new ArrayList<>();

final Document secDoc = XMLUtilities.createDocument();

final Element docElem = secDoc.createElementNS("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security");

docElem.setPrefix("wsse");

secDoc.appendChild(docElem);

final Element token = tokenStore.getToken(context.getAttributes());

if (token != null)

docElem.appendChild(secDoc.importNode(token, true));

final SoapHeader newHeader = new SoapHeader(new QName("soapHeader"), docElem);

newHeader.setDirection(Direction.DIRECTION_OUT);

soapHeaders.add(newHeader);

exchange.getIn().setHeader(AbstractWsEndpoint.OUTGOING_SOAP_HEADERS, soapHeaders);



  tokenStore.getToken(context.getAttributes()) creates the token and returns a org.w3c.dom.Element

Regards, Michael Knapp


 

Marcus Olk

unread,
Apr 2, 2024, 3:12:44 AM4/2/24
to ipf-user
Thanks Michael.
How do you create the "tokenStore", then?

Marcus

Michael Knapp

unread,
Apr 2, 2024, 5:27:58 AM4/2/24
to ipf-...@googlegroups.com


Marcus Olk <to.mar...@gmail.com> schrieb am Di., 2. Apr. 2024, 09:12:
Thanks Michael.
How do you create the "tokenStore", then?

I create it with opensaml

Michael

Marcus Olk

unread,
Apr 2, 2024, 5:59:55 AM4/2/24
to ipf-user
> I create it with opensaml

And you are using this in an IPF project? Do you have example code on how you create it?

Marcus

Michael Knapp

unread,
Apr 2, 2024, 4:11:02 PM4/2/24
to ipf-...@googlegroups.com
Am Di., 2. Apr. 2024 um 11:59 Uhr schrieb Marcus Olk <to.mar...@gmail.com>:
> I create it with opensaml

And you are using this in an IPF project? Do you have example code on how you create it?



// initialize

OpenSAMLUtil.initSamlEngine();




final Assertion assertion = SAML2ComponentBuilder.createAssertion();

// set all the necessary fields [...]

// set signature

final Signature signature = OpenSAMLUtil.buildSignature(); //(Signature)getSAMLBuilder().getBuilder(Signature.DEFAULT_ELEMENT_NAME).buildObject(Signature.DEFAULT_ELEMENT_NAME);

assertion.setSignature(signature);

final X509KeyInfoGeneratorFactory x509Factory = new X509KeyInfoGeneratorFactory();

x509Factory.setEmitEntityCertificate(true);

x509Factory.setEmitEntityCertificateChain(true);

final SignatureSigningParameters sigParams = new SignatureSigningParameters();

sigParams.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);

sigParams.setSignatureCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);

sigParams.setSignatureReferenceDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA1);

sigParams.setSigningCredential(signingCredential);

sigParams.setKeyInfoGenerator(x509Factory.newInstance());

signatureSupport.prepareSignatureParams(signature, sigParams);

return OpenSAMLUtil.toDom(assertion, null, true);


 
Reply all
Reply to author
Forward
0 new messages