- WS-Security overview
- JBoss WS-Security support
- Apache CXF WS-Security implementation
- JBossWS configuration additions
- Apache CXF annotations
WS-Security provides the means to secure your services beyond transport level protocols such as HTTPS. Through a number of standards such as XML-Encryption, and headers defined in the WS-Security standard, it allows you to:
- Pass authentication tokens between services.
- Encrypt messages or parts of messages.
- Sign messages.
- Timestamp messages.
WS-Security makes heavy use of public and private key cryptography. It is helpful to understand these basics to really understand how to configure WS-Security. With public key cryptography, a user has a pair of public and private keys. These are generated using a large prime number and a key function.
The keys are related mathematically, but cannot be derived from one another. With these keys we can encrypt messages. For example, if Bob wants to send a message to Alice, he can encrypt a message using her public key. Alice can then decrypt this message using her private key. Only Alice can decrypt this message as she is the only one with the private key.
Messages can also be signed. This allows you to ensure the authenticity of the message. If Alice wants to send a message to Bob, and Bob wants to be sure that it is from Alice, Alice can sign the message using her private key. Bob can then verify that the message is from Alice by using her public key.
JBoss Web Services supports many real world scenarios requiring WS-Security functionalities. This includes signature and encryption support through X509 certificates, authentication and authorization through username tokens as well as all ws-security configurations covered by WS-SecurityPolicy specification.
As well as for other WS-* features, the core of WS-Security functionalities is provided through the Apache CXF engine. On top of that the JBossWS integration adds few configuration enhancements to simplify the setup of WS-Security enabled endpoints.
Apache CXF features a top class WS-Security module supporting multiple configurations and easily extendible.
The system is based on interceptors that delegate to Apache WSS4J for the low level security operations. Interceptors can be configured in different ways, either through Spring configuration files or directly using Apache CXF client API. Please refer to the Apache CXF documentation if you're looking for more details.
Recent versions of Apache CXF, however, introduced support for WS-Security Policy, which aims at moving most of the security configuration into the service contract (through policies), so that clients can easily be configured almost completely automatically from that. This way users do not need to manually deal with configuring / installing the required interceptors; the Apache CXF WS-Policy engine internally takes care of that instead.
WS-SecurityPolicy describes the actions that are required to securely communicate with a service advertised in a given WSDL contract. The WSDL bindings / operations reference WS-Policy fragments with the security requirements to interact with the service. The WS-SecurityPolicy specification allows for specifying things like asymmetric/symmetric keys, using transports (https) for encryption, which parts/headers to encrypt or sign, whether to sign then encrypt or encrypt then sign, whether to include timestamps, whether to use derived keys, etc.
However some mandatory configuration elements are not covered by WS-SecurityPolicy, basically because they're not meant to be public / part of the published endpoint contract; those include things such as keystore locations, usernames and passwords, etc. Apache CXF allows configuring these elements either through Spring xml descriptors or using the client API / annotations. Below is the list of supported configuration properties:
|ws-security.username||The username used for UsernameToken policy assertions|
|ws-security.password||The password used for UsernameToken policy assertions. If not specified, the callback handler will be called.|
|ws-security.callback-handler||The WSS4J security CallbackHandler that will be used to retrieve passwords for keystores and UsernameTokens.|
|ws-security.signature.properties||The properties file/object that contains the WSS4J properties for configuring the signature keystore and crypto objects|
|ws-security.encryption.properties||The properties file/object that contains the WSS4J properties for configuring the encryption keystore and crypto objects|
|ws-security.signature.username||The username or alias for the key in the signature keystore that will be used. If not specified, it uses the the default alias set in the properties file. If that's also not set, and the keystore only contains a single key, that key will be used.|
|ws-security.encryption.username||The username or alias for the key in the encryption keystore that will be used. If not specified, it uses the the default alias set in the properties file. If that's also not set, and the keystore only contains a single key, that key will be used. For the web service provider, the useReqSigCert keyword can be used to accept (encrypt to) any client whose public key is in the service's truststore (defined in ws-security.encryption.properties.)|
|ws-security.signature.crypto||Instead of specifying the signature properties, this can point to the full WSS4J Crypto object. This can allow easier "programmatic" configuration of the Crypto information."|
|ws-security.encryption.crypto||Instead of specifying the encryption properties, this can point to the full WSS4J Crypto object. This can allow easier "programmatic" configuration of the Crypto information."|
|ws-security.enable.streaming|| Enable streaming (StAX based) processing of WS-Security messages
Here is an example of configuration using the client API:
Please refer to the Apache CXF documentation for additional configuration details.
In order for removing the need of Spring on server side for setting up WS-Security configuration properties not covered by policies, the JBossWS integration allows for getting those pieces of information from a defined endpoint configuration. Endpoint configurations can include property declarations and endpoint implementations can be associated with a given endpoint configuration using the @EndpointConfig annotation.
The JBossWS configuration additions allow for a descriptor approach to the WS-Security Policy engine configuration. If you prefer to provide the same information through an annotation approach, you can leverage the Apache CXF @org.apache.cxf.annotations.EndpointProperties annotation:
In this section some sample of WS-Security service endpoints and clients are provided. Please note they're only meant as tutorials; you should really careful isolate the ws-security policies / assertion that best suite your security needs before going to production environment.
|The following sections provide directions and examples on understanding some of the configuration options for WS-Security engine. Please note the implementor remains responsible for assessing the application requirements and choosing the most suitable security policy for them.|
First of all you need to create the web service endpoint using JAX-WS. While this can generally be achieved in different ways, it's required to use a contract-first approach when using WS-Security, as the policies declared in the wsdl are parsed by the Apache CXF engine on both server and client sides. So, here is an example of WSDL contract enforcing signature and encryption using X 509 certificates (the referenced schema is omitted):
The service endpoint can be generated using the wsconsume tool and then enriched with a @EndpointConfig annotation:
The referenced jaxws-endpoint-config.xml descriptor is used to provide a custom endpoint configuration with the required server side configuration properties; this tells the engine which certificate / key to use for signature / signature verification and for encryption / decryption:
... the bob.properties configuration file is also referenced above; it includes the WSS4J Crypto properties which in turn link to the keystore file, type and the alias/password to use for accessing it:
A callback handler for the letting Apache CXF access the keystore is also provided:
Assuming the bob.jks keystore has been properly generated and contains Bob's (server) full key (private/certificate + public key) as well as Alice's (client) public key, we can proceed to packaging the endpoint. Here is the expected content (the endpoint is a POJO one in a war archive, but EJB3 endpoints in jar archives are of course also supported):
As you can see, the jaxws classes generated by the tools are of course also included, as well as a basic web.xml referencing the endpoint bean:
|If you're deploying the endpoint archive to WildFly, remember to add a dependency to org.apache.ws.security module in the MANIFEST.MF file.|
You start by consuming the published WSDL contract using the wsconsume tool on client side too. Then you simply invoke the the endpoint as a standard JAX-WS one:
As you can see, the WS-Security properties are set in the request context. Here the KeystorePasswordCallback is the same as on server side above, you might want/need different implementation in real world scenarios, of course.
The alice.properties file is the client side equivalent of the server side bob.properties and references the alice.jks keystore file, which has been populated with Alice's (client) full key (private/certificate + public key) as well as Bob's (server) public key.
The Apache CXF WS-Policy engine will digest the security requirements in the contract and ensure a valid secure communication is in place for interacting with the server endpoint.
The server side configuration described above implies the endpoint is configured for serving a given client which a service agreement has been established for. In some real world scenarios though, the same server might be expected to be able to deal with (including decrypting and encrypting) messages coming from and being sent to multiple clients. Apache CXF supports that through the useReqSigCert value for the ws-security.encryption.username configuration parameter.
Of course the referenced server side keystore then needs to contain the public key of all the clients that are expected to be served.
The Username Token Profile can be used to provide client's credentials to a WS-Security enabled target endpoint.
Apache CXF provides means for setting basic password callback handlers on both client and server sides to set/check passwords; the ws-security.username and ws-security.callback-handler properties can be used similarly as shown in the signature and encryption example. Things become more interesting when requiring a given user to be authenticated (and authorized) against a security domain on the target WildFly server.
On server side, you need to install two additional interceptors that act as bridges towards the application server authentication layer:
- an interceptor for performing authentication and populating a valid SecurityContext; the provided interceptor should extend org.apache.cxf.ws.interceptor.security.AbstractUsernameTokenInInterceptor, in particular JBossWS integration comes with org.jboss.wsf.stack.cxf.security.authentication.SubjectCreatingInterceptor for this;
- an interceptor for performing authorization; CXF requires that to extend org.apache.cxf.interceptor.security.AbstractAuthorizingInInterceptor, for instance the SimpleAuthorizingInterceptor can be used for simply mapping endpoint operations to allowed roles.
So, here follows an example of WS-SecurityPolicy endpoint using Username Token Profile for authenticating through the WildFly security domain system.
As in the other example, we start with a wsdl contract containing the proper WS-Security Policy:
|If you want to send hash / digest passwords, you can use a policy such as what follows:
Please note the specified JBoss security domain needs to be properly configured for computing digests.
The service endpoint can be generated using the wsconsume tool and then enriched with a @EndpointConfig annotation and @InInterceptors annotation to add the two interceptors mentioned above for JAAS integration:
The POJOEndpointAuthorizationInterceptor is included into the deployment and deals with the roles cheks:
The jaxws-endpoint-config.xml descriptor is used to provide a custom endpoint configuration with the required server side configuration properties; in particular for this Username Token case that's just a CXF configuration option for leaving the username token validation to the configured interceptors:
In order for requiring a given JBoss security domain to be used to protect access to the endpoint (a POJO one in this case), we declare that in a jboss-web.xml descriptor (the JBossWS security domain is used):
Finally, the web.xml is as simple as usual:
The endpoint is packaged into a war archive, including the JAXWS classes generated by wsconsume:
|If you're deploying the endpoint archive to WildFly, remember to add a dependency to org.apache.ws.security and org.apache.cxf module (due to the @InInterceptor annotation) in the MANIFEST.MF file.|
Here too you start by consuming the published WSDL contract using the wsconsume tool. Then you simply invoke the the endpoint as a standard JAX-WS one:
The UsernamePasswordCallback class is shown below and is responsible for setting the passwords on client side just before performing the invocations:
If everything has been done properly, you should expect to calls to sayHello() fail when done with user "snoopy" and pass with user "kermit" (and credential "thefrog"); moreover, you should get an authorization error when trying to call greetMe() with user "kermit", as that does not have the "snoopies" role.
Another quite common use case is using WS-Security Username Token Profile over a secure transport (HTTPS). A scenario like this is implemented similarly to what's described in the previous example, except for few differences explained below.
First of all, here is an excerpt of a wsdl wth a sample security policy for Username Token over HTTPS:
The endpoint then needs of course to be actually available on HTTPS only, so we have a web.xml setting the transport-guarantee such as below:
Apache CXF supports WS-SecureConversation specification, which is about improving performance by allowing client and server to negotiate initial security keys to be used for later communication encryption/signature. This is done by having two policies in the wsdl contract, an outer one setting the security requirements to actually communicate with the endpoint and a bootstrap one, related to the communication for establishing the secure conversation keys. The client will be automatically sending an initial message to the server for negotiating the keys, then the actual communication to the endpoint takes place. As a consequence, Apache CXF needs a way to specify which WS-Security configuration properties are intended for the bootstrap policy and which are intended for the actual service policy. To accomplish this, properties intended for the bootstrap policy are appended with .sct.