Wiki Markup |
---|
{span:style=font-size:2em;font-weight:bold} JAX-RS: XML Security {span} |
Table of Contents |
---|
Introduction
...
Maven dependencies
Code Block | ||||
---|---|---|---|---|
| ||||
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-security-xml</artifactId>
<version>2.5.2</version>
</dependency>
|
...
Enveloped signatures
Payload:
Code Block | ||||
---|---|---|---|---|
| ||||
<Book ID="4bd59819-7b78-47a5-bb61-cc08348e9d48">
<id>126</id>
<name>CXF</name>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#4bd59819-7b78-47a5-bb61-cc08348e9d48">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>eFduzs6Cg1/Wd6jagUmr8vRYxHY=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>DLD+wU85G+Q+H/SNoMr1I7tOCAZAjd3lYE84sBGU5tuMtzbwxKOIgg10g2F1SUbpujy1CZZ9BPkQNA+gA1CH4FE3uiBzp3DDSVv6o5l6Q76Ci0XI28ylO7O1OCY+q2nbP0WtERFWOn9f9nniVKbduz6YQHjv6cNLd8pf4+k2U3g=</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data><ds:X509Certificate>MIICGjCCAYOgAwIBAgIESVRgATANBgkqhkiG9w0BAQUFADAzMRMwEQYDVQQKEwphcGFjaGUub3JnMQwwCgYDVQQLEwNlbmcxDjAMBgNVBAMTBWN4ZmNhMB4XDTcwMDEwMTAwMDAwMFoXDTM4MDExOTAzMTQwN1owMzETMBEGA1UEChMKYXBhY2hlLm9yZzEMMAoGA1UECxMDZW5nMQ4wDAYDVQQDEwVhbGljZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvu747/VShQ85f16DGSc4Ixh9PVpGguyEqrCsK8q9XHOYX9l9/g5wEC6ZcR2FwfNsoaHcKNPjd5sSTzVtBWmQjfBEfIqwTR7vuihOxyNTwEzVwIJzvo7p8/aYxk+VdBtQxq4UweIcf/iFkUbM1cZ1oiXRQzciRBi+C1BQCQE0qzsCAwEAAaM7MDkwIQYDVR0SBBowGIIWTk9UX0ZPUl9QUk9EVUNUSU9OX1VTRTAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQEFBQADgYEAhLwkm+8psKt4gnbikGzV0TgpSWGcWxWKBi+z8tI2n6hFA5v1jVHHa4G9h3s0nxQ2TewzeR/k7gmgV2sI483NgrYHmTmLKaDBWza2pAuZuDhQH8GAEhJakFtKBP++EC9rNNpZnqqHxx3qb2tW25qRtBzDmK921gg9PMomMc7uqRQ=</ds:X509Certificate>
</ds:X509Data>
<ds:KeyValue>
<ds:RSAKeyValue>
<ds:Modulus>vu747/VShQ85f16DGSc4Ixh9PVpGguyEqrCsK8q9XHOYX9l9/g5wEC6ZcR2FwfNsoaHcKNPjd5sSTzVtBWmQjfBEfIqwTR7vuihOxyNTwEzVwIJzvo7p8/aYxk+VdBtQxq4UweIcf/iFkUbM1cZ1oiXRQzciRBi+C1BQCQE0qzs=</ds:Modulus>
<ds:Exponent>AQAB</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
</ds:KeyInfo>
</ds:Signature>
</Book>
|
...
Server Configuration fragment:
Code Block | ||||
---|---|---|---|---|
| ||||
<bean id="serviceBean" class="org.apache.cxf.systest.jaxrs.security.BookStore"/>
<bean id="xmlSigHandler" class="org.apache.cxf.rs.security.xml.XmlSigInHandler"/>
<bean id="xmlSigOutHandler" class="org.apache.cxf.rs.security.xml.XmlSigOutInterceptor"/>
<jaxrs:server address="/xmlsig">
<jaxrs:serviceBeans>
<ref bean="serviceBean"/>
</jaxrs:serviceBeans>
<!--
Required for validating the in signature and removing it from the payload.
It also persists the signature on the current Message which can be disabled.
-->
<jaxrs:providers>
<ref bean="xmlSigHandler"/>
</jaxrs:providers>
<!--
Required for adding a new signature to the outbound payload
-->
<jaxrs:outInterceptors>
<ref bean="xmlSigOutHandler"/>
</jaxrs:outInterceptors>
<jaxrs:properties>
<entry key="ws-security.callback-handler"
value="org.apache.cxf.systest.jaxrs.security.saml.KeystorePasswordCallback"/>
<entry key="ws-security.signature.properties"
value="org/apache/cxf/systest/jaxrs/security/alice.properties"/>
</jaxrs:properties>
</jaxrs:server>
|
...
Client code:
Code Block | ||||
---|---|---|---|---|
| ||||
String address = "https://localhost:8080/xmlsig/bookstore/books";
JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
bean.setAddress(address);
// setup properties
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("ws-security.callback-handler",
"org.apache.cxf.systest.jaxrs.security.saml.KeystorePasswordCallback");
properties.put("ws-security.signature.username", "alice");
properties.put("ws-security.signature.properties",
"org/apache/cxf/systest/jaxrs/security/alice.properties");
bean.setProperties(properties);
// add the interceptor which will add a signature to the outbound payload
XmlSigOutInterceptor sigOutInterceptor = new XmlSigOutInterceptor();
bean.getOutInterceptors().add(sigOutInterceptor);
// add the interceptor which will validate a signature in the inbound payload
XmlSigInInterceptor sigInInterceptor = new XmlSigInInterceptor();
bean.getInInterceptors().add(sigInInterceptor);
// load a bus with HTTPS configuration:
SpringBusFactory bf = new SpringBusFactory();
Bus bus = bf.createBus(configLocation);
bean.setBus(bus);
// use WebClient (or proxy) as usual
WebClient wc = bean.createWebClient();
Book book = wc.post(new Book("CXF", 126L), Book.class);
|
...
Enveloping signatures
Payload:
Code Block | ||||
---|---|---|---|---|
| ||||
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#88e688e6-6512-406f-9e88-a58e5d781ff0">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>Cq3zl3t3DqWTvuZ+4EtZgGs4ikk=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo><ds:SignatureValue>NvcCS8vx3YJkc8fHMf8bQkC+lwasC6CwiS7HfKSm8t+6TtYdM7TRbYxSuqfCTkF4vBIldWIzl6UngON592FfJdbvrgE2CusCkIybrP7BBmP7zTSV0GjH4/60L6ObkhGPkMNoKzw4V+zgF7Zo+F7ngsz5ZUWZX/GWETmTtYtcfT0=</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate><!-- Omitted for brevity--></ds:X509Certificate>
</ds:X509Data>
<ds:KeyValue>
<ds:RSAKeyValue><ds:Modulus>vu747/VShQ85f16DGSc4Ixh9PVpGguyEqrCsK8q9XHOYX9l9/g5wEC6ZcR2FwfNsoaHcKNPjd5sSTzVtBWmQjfBEfIqwTR7vuihOxyNTwEzVwIJzvo7p8/aYxk+VdBtQxq4UweIcf/iFkUbM1cZ1oiXRQzciRBi+C1BQCQE0qzs=</ds:Modulus>
<ds:Exponent>AQAB</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
</ds:KeyInfo>
<ds:Object ID="88e688e6-6512-406f-9e88-a58e5d781ff0">
<Book>
<id>126</id>
<name>CXF</name>
</Book>
</ds:Object>
</ds:Signature>
|
...
Client code is nearly identical to the one shown in the Enveloped signatures section except that XmlSigOutInterceptor need to have an additional property set:
Code Block | ||||
---|---|---|---|---|
| ||||
// add the interceptor dealing with adding a signature
XmlSigOutInterceptor sigInterceptor = new XmlSigOutInterceptor();
sigInterceptor.setStyle("enveloping");
|
Detached signatures
Payload:
Code Block | ||||
---|---|---|---|---|
| ||||
<env:Envelope xmlns:env="http://org.apache.cxf/rs/env">
<Book ID="e9836bc2-cb5a-453f-b967-a9ddbaf9a6de">
<id>125</id>
<name>CXF</name>
</Book>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#e9836bc2-cb5a-453f-b967-a9ddbaf9a6de">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>Pxz77Hlg6I/MRsJz4gixkaMFtYI=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>JSwgiVqZT1EtJ9xqtb90juS54pvZguzFMne7cQyGMQDvBW7b65aAAIfVx/PmFB7Tuy4qB4zqNFCzCwHlhDurNP9NYB7PEzFsA3v3vSyEcHnpUhu41xmBvjT5HWEKbuzqX0dHekizuUefbfzG5WpluVPmOgjashrm9DIhfEf+Hyg=</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate><!--Omitted for Brewity--></ds:X509Certificate>
</ds:X509Data>
<ds:KeyValue>
<ds:RSAKeyValue>
<ds:Modulus>vu747/VShQ85f16DGSc4Ixh9PVpGguyEqrCsK8q9XHOYX9l9/g5wEC6ZcR2FwfNsoaHcKNPjd5sSTzVtBWmQjfBEfIqwTR7vuihOxyNTwEzVwIJzvo7p8/aYxk+VdBtQxq4UweIcf/iFkUbM1cZ1oiXRQzciRBi+C1BQCQE0qzs=</ds:Modulus>
<ds:Exponent>AQAB</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
</ds:KeyInfo>
</ds:Signature>
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_E462768C678896CE9913202742137181" IssueInstant="2011-11-02T22:50:13.718Z" Version="2.0" xsi:type="saml2:AssertionType">
<saml2:Issuer>https://idp.example.org/SAML2</saml2:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<!--
Enveloped/embedded SAML Assertion XML Signature is omitted for brevity
See the JAX-RS SAML section for more info
-->
</ds:Signature>
<!-- the rest of SAML assertion -->
</saml2:Assertion>
</env:Envelope>
|
...
Client code is nearly identical to the one shown in the Enveloped signatures section except that XmlSigOutInterceptor need to have an additional property set:
Code Block | ||||
---|---|---|---|---|
| ||||
// add the interceptor dealing with adding a signature
XmlSigOutInterceptor sigInterceptor = new XmlSigOutInterceptor();
sigInterceptor.setStyle("detached");
|
...
"style": possible values are "enveloped" (default), "enveloping" and "detached"
"envelopedName": only used with the "detached" style, default is "{http://org.apache.cxf/rs/env}Envelope"
"signatureAlgorithm": default is "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
"digestAlgorithm": default is "http://www.w3.org/2000/09/xmldsig#sha1"
Signature Key Info Validation
By default ds:Signature is expected to contain ds:KeyInfo element.
Setting a "keyInfoMustBeAvailable" property to false on the out interceptors will lead to KeyInfo not included.
If the same property is set to false on the in interceptors then either an authenticated Principal name or a default store alias will be used to load the certificate for validating the signature.
XML Encryption
Encrypting XML payloads makes it possible to drop a requirement for HTTPS.
Here is a payload example:
Code Block | ||||
---|---|---|---|---|
| ||||
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:RetrievalMethod Type="http://www.w3.org/2001/04/xmlenc#EncryptedKey"/>
<xenc:EncryptedKey Id="EK-B353DDCEE7C575B6A213203188664772">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate><!-- Omitted for brevity --></ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
<xenc:CipherData><xenc:CipherValue>tPtZz4pnVWquaV2a7O0y+VrHoeWwk3Eu5Jnu3RHz5rGDB/MLyG6rBamhit03J2xWaV52zUtDAPEj8sr4oy5y2KLB09Hu317IbQjinePabUpd+DLnwNn5iHZpHWJPfndkh07JdYZSrMwqOvJ3fqrNJ+LQeLzZDneT8sC1vRyhSDU=</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedKey>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>3ZPQ3SapAxemJwqG58sWh+r8B5SMRf/DZ2w/REswgl0zr8kpk0x4tayC5hl7IbSE8CPQYYHX8sXVnUFUoHOtJA==</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
|
Here is a server configuration fragment:
Code Block | ||||
---|---|---|---|---|
| ||||
<bean id="serviceBean" class="org.apache.cxf.systest.jaxrs.security.BookStore"/>
<bean id="xmlSigInHandler" class="org.apache.cxf.rs.security.xml.XmlSigInHandler"/>
<bean id="xmlEncInHandler" class="org.apache.cxf.rs.security.xml.XmlEncInHandler"/>
<jaxrs:server address="/xmlsig">
<jaxrs:serviceBeans>
<ref bean="serviceBean"/>
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="xmlEncHandler"/>
<ref bean="xmlSigHandler"/>
</jaxrs:providers>
<jaxrs:properties>
<entry key="ws-security.callback-handler"
value="org.apache.cxf.systest.jaxrs.security.saml.KeystorePasswordCallback"/>
<entry key="ws-security.encryption.properties"
value="org/apache/cxf/systest/jaxrs/security/bob.properties"/>
<entry key="ws-security.signature.properties"
value="org/apache/cxf/systest/jaxrs/security/alice.properties"/>
</jaxrs:properties>
</jaxrs:server>
|
...
The code:
Code Block | ||||
---|---|---|---|---|
| ||||
String address = "https://localhost:8080/xmlencryption/bookstore/books";
JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
bean.setAddress(address);
// setup properties
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("ws-security.callback-handler",
"org.apache.cxf.systest.jaxrs.security.saml.KeystorePasswordCallback");
properties.put("ws-security.encryption.username", "bob");
properties.put("ws-security.encryption.properties",
"org/apache/cxf/systest/jaxrs/security/bob.properties");
// if signature required:
properties.put("ws-security.signature.username", "alice");
properties.put("ws-security.signature.properties",
"org/apache/cxf/systest/jaxrs/security/alice.properties");
bean.setProperties(properties);
// if signature required: add the interceptor dealing with adding a signature
XmlSigOutInterceptor sigInterceptor = new XmlSigOutInterceptor();
bean.getOutInterceptors().add(sigInterceptor);
// add the interceptor dealing with the encryption
XmlEncOutInterceptor encInterceptor = new XmlEncOutInterceptor();
encInterceptor.setSymmetricEncAlgorithm("http://www.w3.org/2001/04/xmlenc#aes128-cbc");
bean.getOutInterceptors().add(encInterceptor);
// use WebClient (or proxy) as usual
WebClient wc = bean.createWebClient();
Response r = wc.post(new Book("CXF", 126L), Book.class);
assertEquals(200, r.getStatus());
|
...
The actual application client code does not expect a payload such as Book back but if it did then configuring the server to encrypt the response would be straightforward:
Code Block | ||||
---|---|---|---|---|
| ||||
<bean id="serviceBean" class="org.apache.cxf.systest.jaxrs.security.BookStore"/>
<bean id="xmlSigInHandler" class="org.apache.cxf.rs.security.xml.XmlSigInHandler"/>
<bean id="xmlSigOutHandler" class="org.apache.cxf.rs.security.xml.XmlSigOutInterceptor"/>
<bean id="xmlEncInHandler" class="org.apache.cxf.rs.security.xml.XmlEncInHandler"/>
<bean id="xmlEncOutHandler" class="org.apache.cxf.rs.security.xml.XmlEncOutInterceptor">
<property name="symmetricEncAlgorithm" value="aes128-cbc"/>
</bean>
<jaxrs:server address="/xmlsec">
<jaxrs:serviceBeans>
<ref bean="serviceBean"/>
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="xmlEncInHandler"/>
<ref bean="xmlSigInHandler"/>
</jaxrs:providers>
<jaxrs:outInterceptors>
<ref bean="xmlSigOutHandler"/>
<ref bean="xmlEncOutHandler"/>
</jaxrs:outInterceptors>
<jaxrs:properties>
<entry key="ws-security.callback-handler"
value="org.apache.cxf.systest.jaxrs.security.saml.KeystorePasswordCallback"/>
<entry key="ws-security.encryption.properties"
value="org/apache/cxf/systest/jaxrs/security/alice.properties"/>
<entry key="ws-security.signature.properties"
value="org/apache/cxf/systest/jaxrs/security/bob.properties"/>
</jaxrs:properties>
</jaxrs:server>
|
Now the client code can be updated to expect an encrypted and signed Book back:
Code Block | ||||
---|---|---|---|---|
| ||||
// Use the previous code fragment, add the in interceptors:
XmlEncInInterceptor encInInterceptor = new XmlEncInInterceptor();
bean.getInInterceptors().add(encInInterceptor);
XmlSigInInterceptor sigInInterceptor = new XmlSigInInterceptor();
bean.getInInterceptors().add(sigInInterceptor);
|
...
When multiple clients are posting the encrypted and signed payloads, the following configuration will lead to the request signature certificates being utilized for encrypting the symmetric key used to encrypt the response:
Code Block | ||||
---|---|---|---|---|
| ||||
<!-- server -->
<jaxrs:server>
<jaxrs:properties>
<entry key="ws-security.callback-handler"
value="org.apache.cxf.systest.jaxrs.security.saml.KeystorePasswordCallback"/>
<entry key="ws-security.encryption.properties"
value="org/apache/cxf/systest/jaxrs/security/alice.properties"/>
<entry key="ws-security.encryption.username" value="useReqSigCert"/>
<entry key="ws-security.signature.properties"
value="org/apache/cxf/systest/jaxrs/security/bob.properties"/>
</jaxrs:properties>
</jaxrs:server>
<jaxrs:client>
<jaxrs:properties>
<entry key="ws-security.callback-handler"
value="org.apache.cxf.systest.jaxrs.security.saml.KeystorePasswordCallback"/>
<entry key="ws-security.encryption.properties"
value="org/apache/cxf/systest/jaxrs/security/bob.properties"/>
<entry key="ws-security.encryption.username" value="bob"/>
<entry key="ws-security.signature.properties"
value="org/apache/cxf/systest/jaxrs/security/alice.properties"/>
<entry key="ws-security.signature.username" value="alice"/>
</jaxrs:properties>
</jaxrs:client>
|
...
It is possible to configure the in encryption and signature handlers with the properties restricting the encryption and signature algorithms that clients can use, for example:
Code Block | ||||
---|---|---|---|---|
| ||||
<bean id="sigProps" class="org.apache.cxf.rs.security.xml.SignatureProperties">
<property name="signatureAlgo"
value="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<property name="signatureDigestAlgo"
value="http://www.w3.org/2000/09/xmldsig#sha1"/>
<property name="signatureC14Method"
value="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<property name="signatureC14Transform"
value="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</bean>
<bean id="encProps" class="org.apache.cxf.rs.security.xml.EncryptionProperties">
<property name="encryptionKeyTransportAlgo"
value="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/>
<property name="encryptionSymmetricKeyAlgo"
value="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
</bean>
<bean id="xmlSigInHandlerWithProps" class="org.apache.cxf.rs.security.xml.XmlSigInHandler">
<property name="signatureProperties" ref="sigProps"/>
</bean>
<bean id="xmlEncInHandlerWithProps" class="org.apache.cxf.rs.security.xml.XmlEncInHandler">
<property name="encryptionProperties" ref="encProps"/>
</bean>
<!-- the following ensures that the outbound handlers will use the same algorithms that the client used -->
<bean id="xmlSigOutHandlerWithProps" class="org.apache.cxf.rs.security.xml.XmlSigOutInterceptor">
<property name="signatureProperties" ref="sigProps"/>
</bean>
<bean id="xmlEncOutHandlerWithProps" class="org.apache.cxf.rs.security.xml.XmlEncOutInterceptor">
<property name="encryptionProperties" ref="encProps"/>
</bean>
|
...