Wednesday, December 2, 2015

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

This is the second in a series of blog posts on the support for the Javascript Object Signing and Encryption (JOSE) specifications in Apache CXF. The first article covered how to sign content using the JSON Web Signature (JWS) specification. In this post we will look at how to encrypt content using the JSON Web Encryption (JWE) specification.

1) Test Cases:

As before, 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 tests contained in JWETest. 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 encrypt the message payload by adding a specific provider. The message in turn is decrypted by a provider on the receiving side. You can run the test via the command line "mvn test -Dtest=JWETest", and the message requests and responses will appear in the console.

2) Compact vs. JSON Serialization

Just like the JWS specification, there are two different ways of serializing JWE structures. Compact serialization is a URL-safe representation that is convenient to use when you only have a single encrypting entity. JSON serialization respresents the JWE structures as JSON objects. As of the time of publishing this post, CXF only has interceptors for the compact approach. However I'm told the JSON serialization case will be supported very soon :-)

The providers to use for the compact case on both the client + receiving sides are:
  • Compact: JweWriterInterceptor (out) + JweContainerRequestFilter (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 encryption, we need to first load the encrypting key (either a JKS keystore or else a JSON Web Key (JWK) is supported).

As well as defining the encryption key, we also need to configure the algorithms to encrypt the content as well as to encrypt the key. The list of acceptable encryption algorithms for JWE is defined by the JSON Web Algorithms (JWA) spec. For content encryption, the supported algorithms are all based on AES (CBC/GCM mode) with different key sizes. For key encryption, various schemes based on RSA, AES KeyWrap/GCM, Elliptic Curve Diffie-Hellman, PBES2 are supported, as well as a symmetric encryption option.

For example, the tests use the following configuration to load a public key from a Java KeyStore, and to use the RSA-OAEP key encryption algorithm, as well as a 128-bit AES content encryption algorithm in CBC mode, using HMAC-SHA256 to generate the authentication code:
The service side configuration is largely the same, apart from the fact that we need to specify the "rs.security.key.password" configuration tag to load the private key. The encryption algorithms must also be specified to impose a constraint on the desired algorithms.

4) Encrypting XML payload

Similar to the signature case, it is possible to use JWE to encrypt an XML message, and not just a JSON message. An example is included in JWETest ("testEncryptingXMLPayload").  The configuration is exactly the same as for the JSON case. 

5) Including the encrypting key

It is possible to include the encrypting certificate/key in the JWE header by setting one of the following properties:
  • rs.security.encryption.include.public.key - Include the JWK public key for encryption in the "jwk" header.
  • rs.security.encryption.include.cert - Include the X.509 certificate for encryption in the "x5c" header.
  • rs.security.encryption.include.key.id - Include the JWK key id for encryption in the "kid" header.
  • rs.security.encryption.include.cert.sha1 - Include the X.509 certificate SHA-1 digest for encryption in the "x5t" header.
It could be useful to include the encrypting certificate/key if the recipient has multiple decryption keys and doesn't know which one to use to decrypt the request.

6) Signing + encrypting a message

It is possible to both sign and encrypt a message by combining the JWS + JWE interceptors. For example, simply adding the JweWriterInterceptor and JwsWriterInterceptor providers on the client side will both sign and encrypt the request. An example is included in the github project above (JWEJWSTest). 

4 comments:

  1. In your opinion , why should I change XML Encryption for JOSE encryption ? Any additional advantage ? Regards

    ReplyDelete
    Replies
    1. The main advantages would be that it should be faster due to the lack of XML overhead. A substantial cost for XML decryption in profiling that has been done for Santuario is associated with XML parsing. You also have a wider range of modern encryption algorithms - the XML Encryption spec is quite old now.

      Delete
  2. Hello Colm,

    Thanks for your really interesting blog and your time. These few last days, I'm investigating on JOSE implementation and documentation. I know better XML signatures and discover the signatures with JSON, REST,...

    I read the spec JWA, JWS,... and discover some new algorithms for me (usually I use RSA with SHAXXX, DSA or ECDSA). I tried to generate some key pairs to test but I met some issues (Exceptions or original keys doesn't match the extracted ones).

    Do you have any sample or unit test which generates a key pair, stores it in JSON format (JwkReaderWriter) and extracts it after ?

    Thanks in advance for your reply,

    Kind regards

    ReplyDelete
  3. Hi,

    If you are running into exceptions/errors, then feel free to mail the CXF user list + someone will help you out.

    In terms of a JwkReaderWriter example, se the following test:

    https://git-wip-us.apache.org/repos/asf?p=cxf.git;a=blob;f=rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwkJoseCookBookTest.java;h=b9cb8bf9cd2ab1dc129c43350e0adbf08e71a9ae;hb=HEAD

    ReplyDelete