Monday, November 30, 2015

Javascript Object Signing and Encryption (JOSE) support in Apache CXF - part I

The Javascript Object Signing and Encryption (JOSE) specifications cover how to sign and encrypt data using JSON. Apache CXF has excellent support for all of the JOSE specifications (see the documentation here), thanks largely to the work done by my colleague Sergey Beryozkin. This is the first in a series of blog posts describing how to use and implement JOSE for your web services using Apache CXF. In this post, we will cover how to sign content using the JSON Web Signature (JWS) specification.

1) Test Cases:

Let's start by looking at some practical unit tests on github:
  • cxf-jaxrs-jose: This project contains a number of tests that show how to use the JSON Security functionality in Apache CXF to sign and encrypt JSON payloads to/from a JAX-RS service.
For now let's look at the JWSSignatureTest. These tests mostly follow the same basic format. The web service configuration defines a number of endpoints that map to the same web service implementation (a simple DoubleItService, which allows a user to POST a number and receive the doubled amount, where both the request and response are encoded via JSON). The client test code uses the CXF WebClient API to sign the message payload by adding a specific provider. The message in turn is validated by a provider on the receiving side. You can run the test via the command line "mvn test -Dtest=JWSSignatureTest", and the message requests and responses will appear in the console.

For example, here is the output of the "testSignatureCompact" request. The Payload consists of the concatenated (+ separated by a '.') BASE-64 URL encoded JWS header (e.g. containing the signature algorithm, amongst other values), the BASE-64 URL encoded message payload, and the BASE-64 URL encoded signature value.


2) Compact vs. JSON Serialization

The JWS specification defines two different ways of serializing the signatures. Compact serialization is a URL-safe representation that is convenient to use when you only have a single signing entity. JSON serialization resprents the JWS structures as JSON objects, and allows multiple signatures to be included in the request as a result. The JWSSignatureTest includes both examples ("testSignatureListProperties" and "testSignatureCompact"). It's very easy to experiment with both approaches, as you only have to use different providers on both the client + receiving sides:
  • JSON Serialization: JwsJsonWriterInterceptor (out) + JwsJsonContainerRequestFilter (in)
  • Compact: JwsWriterInterceptor (out) + JwsContainerRequestFilter (in)
3) Security Configuration

As well as adding the desired providers, it is also necessary to specify the security configuration to set the appropriate algorithms, keys, etc. to use. The CXF wiki has an extensive list of all of the different security configuration properties. For signature, we need to first load the signing key (either a JKS keystore or else a JSON Web Key (JWK) is supported).

As well as defining a signing key, we also need to configure the signing algorithm. The list of acceptable signing algorithms for JWS is defined by the JSON Web Algorithms (JWA) spec. For signature, this boils down to a public key signature scheme based on either RSA or EC-DSA, or a symmetric scheme using HMAC.

For example, the tests use the following configuration to load a private key from a Java KeyStore, and to use the signature scheme of RSASSA-PKCS1-v1_5 using SHA-256:
The service side configuration is largely the same, apart from the fact that we don't need to specify the "rs.security.key.password" configuration tag, as we don't need to load the private key. The signature algorithm must be specified to impose a constraint on the acceptable signature algorithm.

4) Signing XML payload

The JWS payload (the content to be signed) can be any binary content and not just a JSON Object. This means that we can use JWS to sign an XML message. This is a really cool feature of the specification in my opinion, as it essentially removes the need to use XML Signature to sign XML payload. An example of this is included in the JWSSignatureTest. The configuration is exactly the same as for the JSON case.

5) Including the signing key

It is possible to include the signing certificate/key in the JWS header by setting one of the following properties:
  • rs.security.signature.include.public.key -  Include the JWK public key for signature in the "jwk" header.
  • rs.security.signature.include.cert - Include the X.509 certificate for signature in the "x5c" header.
  • rs.security.signature.include.key.id - Include the JWK key id for signature in the "kid" header.
  • rs.security.signature.include.cert.sha1- Include the X.509 certificate SHA-1 digest for signature in the "x5t" header.
One advantage of including the entire certificate is that the service doesn't need to store the client certificate locally, but only the issuing certificate.

No comments:

Post a Comment