com.mastercard.developer.encryption.FieldLevelEncryptionParams Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of client-encryption Show documentation
Show all versions of client-encryption Show documentation
Library for Mastercard API compliant payload encryption/decryption
package com.mastercard.developer.encryption;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.SecureRandom;
import java.security.spec.MGF1ParameterSpec;
import static com.mastercard.developer.utils.EncodingUtils.decodeValue;
import static com.mastercard.developer.utils.EncodingUtils.encodeBytes;
/**
* Encryption parameters for computing field level encryption/decryption.
*/
public final class FieldLevelEncryptionParams {
private static final Integer SYMMETRIC_KEY_SIZE = 128;
protected static final String SYMMETRIC_KEY_TYPE = "AES";
private static final String ASYMMETRIC_CYPHER = "RSA/ECB/OAEPWith{ALG}AndMGF1Padding";
private static final String SUN_JCE = "SunJCE";
private final String ivValue;
private final String encryptedKeyValue;
private final String oaepPaddingDigestAlgorithmValue;
private final FieldLevelEncryptionConfig config;
private Key secretKey;
private IvParameterSpec ivParameterSpec;
public FieldLevelEncryptionParams(String ivValue, String encryptedKeyValue, String oaepPaddingDigestAlgorithmValue, FieldLevelEncryptionConfig config) {
this.ivValue = ivValue;
this.encryptedKeyValue = encryptedKeyValue;
this.oaepPaddingDigestAlgorithmValue = oaepPaddingDigestAlgorithmValue;
this.config = config;
}
/**
* Generate encryption parameters.
* @param config A {@link com.mastercard.developer.encryption.FieldLevelEncryptionConfig} instance
* @return A {@link com.mastercard.developer.encryption.FieldLevelEncryptionParams} instance
* @throws EncryptionException
*/
public static FieldLevelEncryptionParams generate(FieldLevelEncryptionConfig config) throws EncryptionException {
// Generate a random IV
IvParameterSpec ivParameterSpec = generateIv();
String ivSpecValue = encodeBytes(ivParameterSpec.getIV(), config.fieldValueEncoding);
// Generate an AES secret key
SecretKey secretKey = generateSecretKey();
// Encrypt the secret key
byte[] encryptedSecretKeyBytes = wrapSecretKey(config, secretKey);
String encryptedKeyValue = encodeBytes(encryptedSecretKeyBytes, config.fieldValueEncoding);
// Compute the OAEP padding digest algorithm
String oaepPaddingDigestAlgorithmValue = config.oaepPaddingDigestAlgorithm.replace("-", "");
FieldLevelEncryptionParams params = new FieldLevelEncryptionParams(ivSpecValue, encryptedKeyValue,
oaepPaddingDigestAlgorithmValue,
config);
params.secretKey = secretKey;
params.ivParameterSpec = ivParameterSpec;
return params;
}
public String getIvValue() {
return ivValue;
}
public String getEncryptedKeyValue() {
return encryptedKeyValue;
}
public String getOaepPaddingDigestAlgorithmValue() {
return oaepPaddingDigestAlgorithmValue;
}
public Key getSecretKey() throws EncryptionException {
try {
if (secretKey != null) {
return secretKey;
}
// Decrypt the AES secret key
byte[] encryptedSecretKeyBytes = decodeValue(encryptedKeyValue, config.fieldValueEncoding);
secretKey = FieldLevelEncryptionParams.unwrapSecretKey(config, encryptedSecretKeyBytes, oaepPaddingDigestAlgorithmValue);
return secretKey;
} catch (EncryptionException e) {
throw e;
} catch (Exception e) {
throw new EncryptionException("Failed to decode and unwrap the provided secret key value!", e);
}
}
public IvParameterSpec getIvSpec() throws EncryptionException {
try {
if (ivParameterSpec != null) {
return ivParameterSpec;
}
// Decode the IV
byte[] ivByteArray = decodeValue(ivValue, config.fieldValueEncoding);
ivParameterSpec = new IvParameterSpec(ivByteArray);
return ivParameterSpec;
} catch (Exception e) {
throw new EncryptionException("Failed to decode the provided IV value!", e);
}
}
private static IvParameterSpec generateIv() throws EncryptionException {
try {
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG", "SUN");
byte[] ivBytes = new byte[16];
secureRandom.nextBytes(ivBytes);
return new IvParameterSpec(ivBytes);
} catch (GeneralSecurityException e) {
throw new EncryptionException("Failed to generate an IV value!", e);
}
}
private static SecretKey generateSecretKey() throws EncryptionException {
try {
KeyGenerator generator = KeyGenerator.getInstance(SYMMETRIC_KEY_TYPE, SUN_JCE);
generator.init(SYMMETRIC_KEY_SIZE);
return generator.generateKey();
} catch (GeneralSecurityException e) {
throw new EncryptionException("Failed to generate a secret key!", e);
}
}
protected static byte[] wrapSecretKey(FieldLevelEncryptionConfig config, Key key) throws EncryptionException {
try {
Key publicEncryptionKey = config.encryptionCertificate.getPublicKey();
MGF1ParameterSpec mgf1ParameterSpec = new MGF1ParameterSpec(config.oaepPaddingDigestAlgorithm);
String asymmetricCipher = ASYMMETRIC_CYPHER.replace("{ALG}", mgf1ParameterSpec.getDigestAlgorithm());
Cipher cipher = Cipher.getInstance(asymmetricCipher, SUN_JCE);
cipher.init(Cipher.WRAP_MODE, publicEncryptionKey, getOaepParameterSpec(mgf1ParameterSpec));
return cipher.wrap(key);
} catch (GeneralSecurityException e) {
throw new EncryptionException("Failed to wrap secret key!", e);
}
}
protected static Key unwrapSecretKey(FieldLevelEncryptionConfig config, byte[] keyBytes, String oaepDigestAlgorithm) throws EncryptionException {
if (!oaepDigestAlgorithm.contains("-")) {
oaepDigestAlgorithm = oaepDigestAlgorithm.replace("SHA", "SHA-");
}
try {
MGF1ParameterSpec mgf1ParameterSpec = new MGF1ParameterSpec(oaepDigestAlgorithm);
Key key = config.decryptionKey;
String asymmetricCipher = ASYMMETRIC_CYPHER.replace("{ALG}", mgf1ParameterSpec.getDigestAlgorithm());
Cipher cipher = Cipher.getInstance(asymmetricCipher, SUN_JCE);
cipher.init(Cipher.UNWRAP_MODE, key, getOaepParameterSpec(mgf1ParameterSpec));
return cipher.unwrap(keyBytes, SYMMETRIC_KEY_TYPE, Cipher.SECRET_KEY);
} catch (GeneralSecurityException e) {
throw new EncryptionException("Failed to unwrap secret key!", e);
}
}
private static OAEPParameterSpec getOaepParameterSpec(MGF1ParameterSpec mgf1ParameterSpec) {
return new OAEPParameterSpec(mgf1ParameterSpec.getDigestAlgorithm(), "MGF1", mgf1ParameterSpec, PSource.PSpecified.DEFAULT);
}
}