Generating a JSON Web Encryption Data Object

The JSON web encryption (JWE) data object is built using these elements:
  • header
    : Include the
    kid
    and
    alg
    parameters.
  • Content Encryption Key (CEK)
    : The unique encryption key used to encrypt the token.
  • ciphertext
    : The encrypted JSON payload.
  • initialization vector
    : A Base64-encoded randomly generated number that is used along with a secret key to encrypt data.
  • authentication tag
    : Created during the encryption, this tag enables the verifier to prove the integrity of the ciphertext and the header.
The payload should use this format:
header.cek.cyphertext.initialization_vector.auth_tag
For more information about JWE data objects, see RFC 7516.
Example
IMPORTANT
Line breaks have been added for readability and formatting.
eyJraWQiOiIwMFN2SWFHSWZ5YXc4OTdyRGVHOWVGZE9ES2FDS2MxcSIsImVuYyI6IkEyNTZHQ00iLCJhbGciOiJ SU0EtT0FFUCJ9.juQDhF5XcZ1rDbupn1nZ1qHhephzWpa8FumH4KrsD0yF1tCOD0L8WfpSyd5VGIewb4I1IipmS B5vV0O3Cb6FrNLipjFq-oexFRwSK92NbB88ySFO-7FyvPddiqaQFkA81xn8nwdoHMwUsQuqe8Ts_krLsvYghmsc xXKkwcEKqxoWbmD-yEfvKxGyHACLprAKLm-xusexaJLF42OTxYuEhzzrSe6MRll0zXuk2DAhtUL2oHCgu8P3shg JBJqsOPcAFtwtLBRoDwlDt0ybOHjd34Svbpgf_3ncFnDkEQYe5QeElEHaB2a0Nbwo61I1UETfhedHQc8IMtDmVu Kk9pgCTg.uWrwGp2jZxZd5wF0.oFzZ3I2ry77jf-3wB_2q8G-0tbYJWQj88NdzRmVNO34JbreX5WOCju7ntvN8h 83NJXEA_cQech2PEGIZV_tADBaLbSxJeitYKwaQhs_tRVrzrcd8Qhgs4OADfky2m310eV8bUG8D4GZBKRHL6ScL f5p30b6Hoa5fDYsU7IHNyCReiaiGPExlY4luwL9QQxrfY2LTv74Pcqyh-B4byNxR5hTw3SJm7DT7YQLl6_-2ROq JhJoweTdDJtmJoM-LxKEij2TLgHBdqso9f036dfn0SHLl1vG86C1-6DA9yFIZB3gLYnyom1jZuGxUOPXDojUfXo 0OpUj8OI6CnQWdhKpC9X19s8xAhIAUYYdvWrEqFfBzd9S-4E-ZdyUGfxG7fLQuLZKQJeYBbGCssLGSIXLOb15sK OopIgqCTU7M5EN_F7zW0IwJ4-b8OVf_J80-hW1e043RlzBoMr3aGdXFIaLmVbEIzTNeZrulYTTWWLbQlcLTXqAM 0yFlKmIrpq55VruvVR8i_iju5MFzzTYuLut9ecvYbFFeUkUaUBihNXg4Np57Ix23gaJuMcPBgUqkH3nCTZQE7yQ OynzO-lho_jAHy1xcwV_DJhhAJnACO5HUDAjVKmr-GKqxvDZWVzrqjFkPArX81eRSnn9Dr2Ahozehn9FTB37AJV 3BEC2i7WMvAbQE1EpPVGTdvVDhH2xlLAHqHTBeQakzY4e81h2L3EDCmdjx_yZdZOUUSG3mLQSp864OV5pHc2X22 ZRadGbrLwnA-m2W1oDZIzh2t5nZdJhePnNzHbNXTf0xWSklxdgJdfG52FVSH-cKiJQnDhmCH6nPVK7NKnL0vRuZ -uuOa4PJQDoT2H8eSjpvo8fo9rwfLYmQJa042t7OSE95bER9k1oJTUm83LNA3bxhWk5en2UFgcip3z3KlOmFwPL VNCpzitULzAEHwBJlrB0aGXkQi1bJMxo9XZNREnFyYAlX3-aruXIe47pwAyOEX-hd-3Y7UsxBVYB86se51q2-VU ldR0zj6cwZvrTxhFM_gAsD0HisAGa6E3n3n3w1JAvjuZdHRoQqaT00YFmTdSbocmTOEUammYmBjagKKycOzgmoZ SaYpffQl_R06tEZke6uhJrPQuTwLwivZMtnWE8O16VIRX4cG3OfzaRYs0GvPWumDlrSbM8FugMIEaUTng5T9Cdk ixegRmszDELzNjNTJLe2WwxJG4Kb_1-yGMRlhFys4FEwVMk8AWJJRDpwG0jdmHkBz9l7z1PFdIcidbIpmgH7m5R D6kwRSxaG_BJWDc2IkIFyNa2G_-gHjQh_utablUOL9CXxxFCKD9UHojtsHneFt1bhV2P_sfYYhtZo5XloKAAEXq mOSY2boYyj0hMlKNuVqukrnWG6-bV-LBf9DvpYNKO9YeU6rYD_WOxSQlliqVvEK8n9xLCmQQKsK2Xj2WGh7wWTQ TMh18hcsNENN3Loq9DofAbOrCXqdREAshxg_MOI5vGe0JvIR9Gj6kAhKGFf2DYBqMynbb9jWJnjCzFXBCqXXjTO uCoZdzlV9RbLxIBOOojIfLfdtVLGKPLKizXaSQ8YrLiBATarkpO7WFSSF66lvezwDZlfDErA-0kij1n2poKqDLY L3vNfX8vU33ef96VQc9I3auTpiWd0NLa5yw0RWREAjqa4pHYTEZDiLcD0vETt84_aon3U7co_8fAYrztokTIJ2O RuhN_xA0rV1MbOZIwW6m-duqYLFLQlcwjxNwTdaberNy6bCg9otljd5l7nSbzZ6UpHrHDF02LrM41NmQUx9tZFH ypYjFdgiKKgqk-kTe3pq6ithsTPvcDvDkNgCSb9H_X30qm2-0VXaGIcYBcmJdsbBt7VJuYVZ1I_2l4-_6glgvgQ z9d5KaHyZeJimSXqOsbqUQzNKWC7_K81Z5XmqCPJByrOiROkO6iEe_poqRgVzHETHYmstAzUlgUvPD3XocZdlHu PHArQe6GddVmxnhTDV1M0TmXwK03f0jGg7LMjWjU1k15X8xYZTk_HMo76IetUOdf9BIoaMBqMHJkk936uzjIeiW 1DbEb4ExLtpIeSoq_fnelAWoVEDMa_XoVkWCR5R7wTJjGyZKjJJkJ6UqYQguS9oO95MZp8N0Qa41wKCvztLbFKt EU7sPz3pU5oUVbn9cZS7WCzCUNWGxb3PO0nTzPsP_MhD71JcuAEFSLS05m1hkoNiYe_6pmLv8Rrgp71kFsTOIOU rcUvwdJRikDOLdNbO5b-_6HjczDPzx9PaM_Zn-34mfOQPthWAfum3YvpmthuKxAWfdBChZXe9oCMeBGewGl7mKM h9H5SP6su5yw-IFe7iBd338LVVPjRXif1rNsU631YXBu9Lz-l6o4cuGuYPVHPhHf4lifFXvlvi702wD7fbYn3cZ 55_yGVJvcFPq6OMUGJUSy5ncj-n7a8-IcGmSFpMtgnMc1ycJa_0N1vtwyjm0WvdzkUrBNC_OoCmHlLaG3XTRenL _WYhzxDUdQQBuSC3acFu28x3NL8cmR5iqy7sBGUKcwt_ogX9ZoQyFzUTFOw.QqKIuF8EnuhOTM8PvGEs8A
Example Java Code
This Java example includes the code that can be used to generate the JWE data object:
package com.
cybersource
.example.service; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.
cybersource
.example.config.ApplicationProperties; import com.
cybersource
.example.domain.CaptureContextResponseBody; import com.
cybersource
.example.domain.CaptureContextResponseHeader; import com.
cybersource
.example.domain.JWK; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.math.BigInteger; import java.security.KeyFactory; import java.security.interfaces.RSAPublicKey; import java.security.spec.RSAPublicKeySpec; import java.util.Base64; import java.util.Base64.Decoder; @Service @RequiredArgsConstructor public class JwtProcessorService { @Autowired private final ApplicationProperties applicationProperties; @SneakyThrows public String verifyJwtAndGetDecodedBody(final String jwt) { // Parse the JWT response into header, payload, and signature final String[] jwtChunks = jwt.split("\\."); final Decoder decoder = Base64.getUrlDecoder(); final String header = new String(decoder.decode(jwtChunks[0])); final String body = new String(decoder.decode(jwtChunks[1])); // Normally you'd want to cache the header and JWK, and only hit /flex/v2/public-keys/{kid} when the key rotates. // For simplicity and demonstration's sake let's retrieve it every time final JWK publicKeyJWK = getPublicKeyFromHeader(header); // Construct an RSA Key out of the response we got from the /public-keys endpoint final BigInteger modulus = new BigInteger(1, decoder.decode(publicKeyJWK.n())); final BigInteger exponent = new BigInteger(1, decoder.decode(publicKeyJWK.e())); final RSAPublicKey rsaPublicKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(modulus, exponent)); // Verify the JWT's signature using the public key final Algorithm algorithm = Algorithm.RSA256(rsaPublicKey, null); final JWTVerifier verifier = JWT.require(algorithm).build(); // This will throw a runtime exception if there's a signature mismatch. verifier.verify(jwt); return body; } @SneakyThrows public String getClientVersionFromDecodedBody(final String jwtBody) { // Map the JWT Body to a POJO final CaptureContextResponseBody mappedBody = new ObjectMapper().readValue(jwtBody, CaptureContextResponseBody.class); // Dynamically retrieve the client library return mappedBody.ctx().stream().findFirst() .map(wrapper -> wrapper.data().clientLibrary()) .orElseThrow(); } @SneakyThrows private JWK getPublicKeyFromHeader(final String jwtHeader) { // Again, this process should be cached so you don't need to hit /public-keys // You'd want to look for a difference in the header's value (e.g. new key id [kid]) to refresh your cache final CaptureContextResponseHeader mappedJwtHeader = new ObjectMapper().readValue(jwtHeader, CaptureContextResponseHeader.class); final RestTemplate restTemplate = new RestTemplate(); final ResponseEntity<String> response = restTemplate.getForEntity( "https://" + applicationProperties.getRequestHost() + "/flex/v2/public-keys/" + mappedJwtHeader.kid(), String.class); return new ObjectMapper().readValue(response.getBody(), JWK.class); } }