Need to encrypt certain fields in JSON Request payload

import java.security.Key;

import java.security.SecureRandom;

import java.util.Arrays;

import java.util.Base64;

import javax.crypto.Cipher;

import javax.crypto.KeyGenerator;

import javax.crypto.SecretKey;

import javax.crypto.SecretKeyFactory;

import javax.crypto.spec.GCMParameterSpec;

import javax.crypto.spec.PBEKeySpec;

import javax.crypto.spec.SecretKeySpec;

import com.nimbusds.jose.JWEHeader;

public class JweCreatorTest {

// 96. 120. import 128 are supported

private static final int AUTHENTICATION_TAG_LENGTH = 96;

// key length in bits

private final static int KEY_LENGTH = 256;

// iteration count

private final static int ITERATION_COUNT = 1000;

private final static String KDF_ALGO = "PBKDF2WithHmacSHA512";

private final static String KEY_WRAP_ALGO = "AESWrap_256";

private final static String AES_ALGO = "AES";

private static final String ENC_KEY = "ddsdsdfffff";

public static void main(String[] args) throws Exception {

// create JWE Compact using sample PAN

String jweCompact = cipher("4444111122223333");

System.out.println("Encrypted value is;" + jweCompact);

// now decipher the content from JwE Compart

// String plainText = decipher(jweCompact);

// System.out.println("Decrypted value is: " + plainText);

}

private static String cipher (String plainText) throws Exception {

//generate a salt value and this must be unique for each request

final byte[] salt = new byte[32];

new SecureRandom().nextBytes(salt);

// construct the JOSE Header

String jweHeader = "{\"p2s\": \"" + Base64.getUrlEncoder().encodeToString(salt)

+ "\", \"p2c\": 1000, \"enc\": \"A256GCM\", \"alg\": \"PBES2-H5512+4256KW\"}";

// define additional authentication data to be the Base64URL encoded JWE (JOSE) Header

byte[] aadBytes = Base64.getUrlEncoder().encode (jweHeader.getBytes());

// generate iv (the AES/GCM nonce)

final byte[] initializationVector = new byte[12];

new SecureRandom().nextBytes(initializationVector);

// generate the content encryption key

KeyGenerator keyGen = KeyGenerator.getInstance(AES_ALGO);

keyGen. init(KEY_LENGTH /* key-len in bits */, new SecureRandom ()) ;

SecretKey contentEncryptionKey = keyGen.generateKey() ;

// encrypt the plain-text sensitive data using the Content Encryption Key

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

cipher.init (Cipher.ENCRYPT_MODE, contentEncryptionKey, new GCMParameterSpec (AUTHENTICATION_TAG_LENGTH,initializationVector));

cipher.updateAAD(aadBytes);

final byte[] tmp = cipher.doFinal(plainText.getBytes());

// The cipher-text bytes include the ciphr-text AND the authentication tag.

// Need to separate these values

final byte[] authenticationTag = Arrays.copyOfRange(tmp, tmp.length - (AUTHENTICATION_TAG_LENGTH / 8),

tmp. length);

final byte[] cipherTextBytes = Arrays.copyOfRange(tmp, 0, tmp.length - (AUTHENTICATION_TAG_LENGTH / 8));

// Create the PBEKeySpec with the given password, salt, and iteration

PBEKeySpec pbeKeySpec = new PBEKeySpec(ENC_KEY.toCharArray(), salt, ITERATION_COUNT, KEY_LENGTH);

// derive a Key-Encrypting Key (KEK) based on password and other PBEKeySpec attributes

SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KDF_ALGO);

SecretKey derivedKEK = keyFactory.generateSecret(pbeKeySpec);

// wrap the CEK using derived KEK

Cipher contentEncKeyCipher = Cipher.getInstance(KEY_WRAP_ALGO);

contentEncKeyCipher.init(Cipher.WRAP_MODE, new SecretKeySpec(derivedKEK.getEncoded(),AES_ALGO));

byte[] encryptedContentEncryptionkey = contentEncKeyCipher.wrap(contentEncryptionKey);

// create compact serialization of JWE

String jweCompact = Base64.getEncoder().encodeToString(jweHeader.getBytes())+ "."

+ Base64.getEncoder().encodeToString(encryptedContentEncryptionkey) + "."

+ Base64.getEncoder().encodeToString(initializationVector) + "."

+ Base64.getEncoder().encodeToString(cipherTextBytes) + "."

+ Base64.getEncoder().encodeToString(authenticationTag);

return jweCompact;

}

private static String decipher (String jweCompact) throws Exception {

if (jweCompact == null)

throw new RuntimeException();

String[] parts = jweCompact.split("([.])");

if (parts.length != 5) {

throw new Exception("invalid input");

}




byte[] hdrBytes = Base64.getUrlDecoder().decode(parts[0]); // headerBytes

byte[] ekBytes = Base64.getUrlDecoder().decode(parts[1]); // encryptedcontentEncryptionkey

byte[] ivBytes = Base64.getUrlDecoder().decode(parts[2]); // initializationVector

byte[] ctBytes = Base64.getUrlDecoder().decode(parts[3]); // cipherTextBytes

byte[] atBytes = Base64.getUrlDecoder().decode(parts[4]); // authenticationTag

// get salt value, and iteration count from header

JWEHeader header = new JWEHeader(JWEHeader.parse(new String (hdrBytes)));




final byte[] salt = header.getPBES2Salt().decode();

final int iterationcount = header.getPBES2Count();

// Create the PBEKeySpec with the given password, salt, and iteration

PBEKeySpec pbeKeySpec= new PBEKeySpec (ENC_KEY.toCharArray(), salt, iterationcount, KEY_LENGTH);

//derive a key based on password and other PBEKeySpec attributes

SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KDF_ALGO);

SecretKey derivedKEK = keyFactory.generateSecret(pbeKeySpec);




// un-wrap the CEK using derived KEK

Cipher contentDecKeyCipher = Cipher.getInstance(KEY_WRAP_ALGO);

contentDecKeyCipher.init(Cipher.UNWRAP_MODE,new SecretKeySpec(derivedKEK.getEncoded(), AES_ALGO));




Key k = contentDecKeyCipher.unwrap(ekBytes, AES_ALGO, Cipher.SECRET_KEY);

SecretKeySpec contentDecryptionKey = new SecretKeySpec(k.getEncoded(), AES_ALGO);

//decrypt the content

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

cipher.init (Cipher.DECRYPT_MODE, contentDecryptionKey,new GCMParameterSpec(AUTHENTICATION_TAG_LENGTH, ivBytes));

byte[] aadBytes = Base64.getUrlEncoder().encode(hdrBytes);

cipher.updateAAD(aadBytes);




// need to append the authentication tag to the ciphertext

byte[] cipherTextAndAT = new byte[ ctBytes.length + atBytes.length];

System.arraycopy(ctBytes, 0, cipherTextAndAT,0, ctBytes.length);

System.arraycopy(ctBytes, 0, cipherTextAndAT, ctBytes.length,atBytes.length);




String plaintext = new String( cipher.doFinal( cipherTextAndAT));




return plaintext;

}

}

 

@dchiesa1 

As part of POC .This is my java code to cipher certain fields in my json request payload.

1. Could you please help me to get the js code for the same. I tried to use the below js code but it is throwing error: unsupported key type  .

2. After getting cipher value in both java and js will someone get the same result if he use java code to decipher ?

const jose = require('node-jose');

const crypto = require('crypto');

// 96, 120, or 128 are supported

const AUTHENTICATION_TAG_LENGTH = 96;

// key length in bits

const KEY_LENGTH = 256;

// iteration count

const ITERATION_COUNT = 1000;

const KDF_ALGO = 'PBKDF2WithHmacSHA512';

const KEY_WRAP_ALGO = 'A256KW';

const AES_ALGO = 'A256GCM';

const ENC_KEY = 'ddsdsdfffff';

async function createJwe(plainText) {

// generate a salt value and this must be unique for each request

const salt = crypto.randomBytes(32);

// construct the JOSE Header

const jweHeader = {

p2s: salt.toString('base64url'),

p2c: ITERATION_COUNT,

enc: AES_ALGO,

alg: 'PBES2-HS512+A256KW',

};

// define additional authentication data to be the Base64URL encoded JWE (JOSE) Header

const aadBytes = Buffer.from(JSON.stringify(jweHeader)).toString('base64url');

// generate iv (the AES/GCM nonce)

const initializationVector = crypto.randomBytes(12);

// generate the content encryption key

const contentEncryptionKey = await jose.JWK.createKey(KEY_LENGTH, AES_ALGO);

// encrypt the plain-text sensitive data using the Content Encryption Key

const cipher = await jose.JWE.createEncrypt(

{ format: 'compact', contentAlg: AES_ALGO, fields: { iat: Math.floor(Date.now() / 1000) } },

contentEncryptionKey,

);

cipher.update(aadBytes, 'utf8');

const encrypted = await cipher.final(Buffer.from(plainText, 'utf8'));

// The cipher-text bytes include the ciphr-text AND the authentication tag.

// Need to separate these values

const authenticationTag = encrypted.slice(-AUTHENTICATION_TAG_LENGTH / 8);

const cipherTextBytes = encrypted.slice(0, -AUTHENTICATION_TAG_LENGTH / 8);

// Create the PBEKeySpec with the given password, salt, and iteration

const pbeKeySpec = {

key: Buffer.from(ENC_KEY),

salt,

iterations: ITERATION_COUNT,

hash: 'sha512',

};

// derive a Key-Encrypting Key (KEK) based on password and other PBEKeySpec attributes

const derivedKEK = await jose.JWK.createKey('PBKDF2', pbeKeySpec, { alg: 'HS512' });

// wrap the CEK using derived KEK

const encryptedContentEncryptionkey = await jose.JWE.encrypt(contentEncryptionKey, derivedKEK, { format: 'compact', fields: { iat: Math.floor(Date.now() / 1000) } });

// create compact serialization of JWE

const jweCompact = `${aadBytes}.${

encryptedContentEncryptionkey.match(/.{1,64}/g).join('\n')

}.${

initializationVector.toString('base64url')

}.${

cipherTextBytes.toString('base64url')

}.${

authenticationTag.toString('base64url')

}`;

return jweCompact;

}

async function main() {

const plainText = '4444111122223333';

const jweCompact = await createJwe(plainText);

console.log('Encrypted value is:', jweCompact);

}

main().catch(err => console.error(err));

0 1 871
1 REPLY 1

You van use the builtin JWT policies in Apigee to Generate encrypted JWT, using whatever algorithms you like. Check the documentation. 

You do not need to write custom code to do that. 

And the resulting encrypted JWT will be decrypt-able by any compliant implementation.