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;
}
}
As part of POC .This is my java code to cipher certain fields in my json request payload.
-
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 .
-
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));