diff --git a/README.md b/README.md
index 77ac69b..6f43478 100644
--- a/README.md
+++ b/README.md
@@ -170,6 +170,7 @@ JweConfig config = JweConfigBuilder.aJweEncryptionConfig()
.withEncryptionPath("$.path.to.foo", "$.path.to.encryptedFoo")
.withDecryptionPath("$.path.to.encryptedFoo.encryptedValue", "$.path.to.foo")
.withEncryptedValueFieldName("encryptedValue")
+ .withIVSize(16) // available values are 12 or 16. If not specified, default value is 16.
.build();
```
diff --git a/pom.xml b/pom.xml
index 378b13f..4187cfc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.mastercard.developer
client-encryption
- 1.7.13-SNAPSHOT
+ 1.8.0
jar
Library for Mastercard API compliant payload encryption/decryption
https://github.com/Mastercard/client-encryption-java
@@ -96,7 +96,6 @@
4.13.1
test
-
org.junit.jupiter
junit-jupiter-params
diff --git a/src/main/java/com/mastercard/developer/encryption/EncryptionConfig.java b/src/main/java/com/mastercard/developer/encryption/EncryptionConfig.java
index b2b78d4..7f6a1bf 100644
--- a/src/main/java/com/mastercard/developer/encryption/EncryptionConfig.java
+++ b/src/main/java/com/mastercard/developer/encryption/EncryptionConfig.java
@@ -46,6 +46,13 @@ public enum Scheme {
*/
PrivateKey decryptionKey;
+
+ /**
+ * IV size in bytes
+ */
+
+ Integer ivSize = 16;
+
/**
* A list of JSON paths to encrypt in request payloads.
* Example:
@@ -107,4 +114,6 @@ Map getDecryptionPaths() {
String getEncryptedValueFieldName() {
return encryptedValueFieldName;
}
+
+ public Integer getIVSize() { return ivSize; }
}
diff --git a/src/main/java/com/mastercard/developer/encryption/EncryptionConfigBuilder.java b/src/main/java/com/mastercard/developer/encryption/EncryptionConfigBuilder.java
index f0c02ed..8350483 100644
--- a/src/main/java/com/mastercard/developer/encryption/EncryptionConfigBuilder.java
+++ b/src/main/java/com/mastercard/developer/encryption/EncryptionConfigBuilder.java
@@ -23,6 +23,8 @@ abstract class EncryptionConfigBuilder {
protected Map decryptionPaths = new HashMap<>();
protected String encryptedValueFieldName;
+ protected Integer ivSize = 16;
+
void computeEncryptionKeyFingerprintWhenNeeded() throws EncryptionException {
try {
if ((encryptionCertificate == null && encryptionKey == null) || !isNullOrEmpty(encryptionKeyFingerprint)) {
diff --git a/src/main/java/com/mastercard/developer/encryption/FieldLevelEncryptionConfigBuilder.java b/src/main/java/com/mastercard/developer/encryption/FieldLevelEncryptionConfigBuilder.java
index 0f77351..c135c37 100644
--- a/src/main/java/com/mastercard/developer/encryption/FieldLevelEncryptionConfigBuilder.java
+++ b/src/main/java/com/mastercard/developer/encryption/FieldLevelEncryptionConfigBuilder.java
@@ -186,6 +186,16 @@ public FieldLevelEncryptionConfigBuilder withEncryptionKeyFingerprintHeaderName(
return this;
}
+ /**
+ * See: {@link EncryptionConfig#ivSize}.
+ */
+ public FieldLevelEncryptionConfigBuilder withEncryptionIVSize(Integer ivSize) {
+ if (ivSize == 12 || ivSize == 16) {
+ this.ivSize = ivSize;
+ return this;
+ }
+ throw new IllegalArgumentException("Supported IV Sizes are either 12 or 16!");
+ }
/**
* Build a {@link com.mastercard.developer.encryption.FieldLevelEncryptionConfig}.
* @throws EncryptionException
@@ -209,6 +219,7 @@ public FieldLevelEncryptionConfig build() throws EncryptionException {
config.encryptionCertificate = this.encryptionCertificate;
config.oaepPaddingDigestAlgorithm = this.oaepPaddingDigestAlgorithm;
config.ivFieldName = this.ivFieldName;
+ config.ivSize = this.ivSize;
config.oaepPaddingDigestAlgorithmFieldName = this.oaepPaddingDigestAlgorithmFieldName;
config.decryptionPaths = this.decryptionPaths;
config.encryptedKeyFieldName = this.encryptedKeyFieldName;
diff --git a/src/main/java/com/mastercard/developer/encryption/FieldLevelEncryptionParams.java b/src/main/java/com/mastercard/developer/encryption/FieldLevelEncryptionParams.java
index 1c09b83..8d30410 100644
--- a/src/main/java/com/mastercard/developer/encryption/FieldLevelEncryptionParams.java
+++ b/src/main/java/com/mastercard/developer/encryption/FieldLevelEncryptionParams.java
@@ -43,7 +43,7 @@ public FieldLevelEncryptionParams(String ivValue, String encryptedKeyValue, Stri
public static FieldLevelEncryptionParams generate(FieldLevelEncryptionConfig config) throws EncryptionException {
// Generate a random IV
- IvParameterSpec ivParameterSpec = AESEncryption.generateIv();
+ IvParameterSpec ivParameterSpec = AESEncryption.generateIv(config.getIVSize());
String ivSpecValue = encodeBytes(ivParameterSpec.getIV(), config.fieldValueEncoding);
// Generate an AES secret key
diff --git a/src/main/java/com/mastercard/developer/encryption/JweConfigBuilder.java b/src/main/java/com/mastercard/developer/encryption/JweConfigBuilder.java
index 5616632..59659cb 100644
--- a/src/main/java/com/mastercard/developer/encryption/JweConfigBuilder.java
+++ b/src/main/java/com/mastercard/developer/encryption/JweConfigBuilder.java
@@ -33,6 +33,7 @@ public JweConfig build() throws EncryptionException {
config.decryptionPaths = this.decryptionPaths.isEmpty() ? Collections.singletonMap("$.encryptedData", "$") : this.decryptionPaths;
config.encryptedValueFieldName = this.encryptedValueFieldName == null ? "encryptedData" : this.encryptedValueFieldName;
config.scheme = EncryptionConfig.Scheme.JWE;
+ config.ivSize = ivSize;
return config;
}
@@ -82,9 +83,8 @@ public JweConfigBuilder withDecryptionPath(String jsonPathIn, String jsonPathOut
return this;
}
- /**
- * See: {@link EncryptionConfig#encryptedValueFieldName}.
- */
+
+
public JweConfigBuilder withEncryptedValueFieldName(String encryptedValueFieldName) {
this.encryptedValueFieldName = encryptedValueFieldName;
return this;
@@ -95,9 +95,21 @@ public JweConfigBuilder withEncryptionKeyFingerprint(String encryptionKeyFingerp
return this;
}
+ /**
+ * See: {@link EncryptionConfig#ivSize}.
+ */
+ public JweConfigBuilder withEncryptionIVSize(Integer ivSize) {
+ if (ivSize == 12 || ivSize == 16) {
+ this.ivSize = ivSize;
+ return this;
+ }
+ throw new IllegalArgumentException("Supported IV Sizes are either 12 or 16!");
+ }
+
private void checkParameterValues() {
if (decryptionKey == null && encryptionCertificate == null && encryptionKey == null) {
throw new IllegalArgumentException("You must include at least an encryption key/certificate or a decryption key");
}
}
+
}
diff --git a/src/main/java/com/mastercard/developer/encryption/aes/AESEncryption.java b/src/main/java/com/mastercard/developer/encryption/aes/AESEncryption.java
index 9a39297..c68348d 100644
--- a/src/main/java/com/mastercard/developer/encryption/aes/AESEncryption.java
+++ b/src/main/java/com/mastercard/developer/encryption/aes/AESEncryption.java
@@ -14,10 +14,10 @@ private AESEncryption() {
// Nothing to do here
}
- public static IvParameterSpec generateIv() throws EncryptionException {
+ public static IvParameterSpec generateIv(Integer ivSize) throws EncryptionException {
try {
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
- byte[] ivBytes = new byte[16];
+ byte[] ivBytes = new byte[ivSize];
secureRandom.nextBytes(ivBytes);
return new IvParameterSpec(ivBytes);
} catch (GeneralSecurityException e) {
diff --git a/src/main/java/com/mastercard/developer/encryption/jwe/JweObject.java b/src/main/java/com/mastercard/developer/encryption/jwe/JweObject.java
index cd2d205..7e38a36 100644
--- a/src/main/java/com/mastercard/developer/encryption/jwe/JweObject.java
+++ b/src/main/java/com/mastercard/developer/encryption/jwe/JweObject.java
@@ -62,7 +62,7 @@ public static String encrypt(JweConfig config, String payload, JweHeader header)
byte[] encryptedSecretKeyBytes = RSA.wrapSecretKey(config.getEncryptionKey(), cek, "SHA-256");
String encryptedKey = EncodingUtils.base64UrlEncode(encryptedSecretKeyBytes);
- byte[] iv = AESEncryption.generateIv().getIV();
+ byte[] iv = AESEncryption.generateIv(config.getIVSize()).getIV();
byte[] payloadBytes = payload.getBytes();
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
diff --git a/src/test/java/com/mastercard/developer/encryption/FieldLevelEncryptionConfigBuilderTest.java b/src/test/java/com/mastercard/developer/encryption/FieldLevelEncryptionConfigBuilderTest.java
index 38aa471..13cb574 100644
--- a/src/test/java/com/mastercard/developer/encryption/FieldLevelEncryptionConfigBuilderTest.java
+++ b/src/test/java/com/mastercard/developer/encryption/FieldLevelEncryptionConfigBuilderTest.java
@@ -7,6 +7,9 @@
import org.junit.rules.ExpectedException;
import static com.mastercard.developer.encryption.FieldLevelEncryptionConfig.FieldValueEncoding.HEX;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertFalse;
public class FieldLevelEncryptionConfigBuilderTest {
@@ -14,7 +17,7 @@ public class FieldLevelEncryptionConfigBuilderTest {
public ExpectedException expectedException = ExpectedException.none();
@Test
- public void testBuild_Nominal() throws Exception {
+ public void testBuild_Nominal_iv12() throws Exception {
FieldLevelEncryptionConfig config = FieldLevelEncryptionConfigBuilder.aFieldLevelEncryptionConfig()
.withEncryptionPath("$.payload", "$.encryptedPayload")
.withEncryptionCertificate(TestUtils.getTestEncryptionCertificate())
@@ -35,6 +38,7 @@ public void testBuild_Nominal() throws Exception {
.withIvFieldName("iv")
.withIvHeaderName("x-iv")
.withFieldValueEncoding(HEX)
+ .withEncryptionIVSize(12)
.build();
Assert.assertNotNull(config);
Assert.assertEquals(1, config.encryptionPaths.size());
@@ -56,6 +60,85 @@ public void testBuild_Nominal() throws Exception {
Assert.assertEquals("oaepPaddingDigestAlgorithm", config.oaepPaddingDigestAlgorithmFieldName);
Assert.assertEquals("x-oaep-padding-digest-algorithm", config.oaepPaddingDigestAlgorithmHeaderName);
Assert.assertEquals(HEX, config.fieldValueEncoding);
+ assertThat(config.getIVSize().intValue(),equalTo(12));
+ }
+
+ @Test
+ public void testBuild_Nominal_iv16() throws Exception {
+ FieldLevelEncryptionConfig config = FieldLevelEncryptionConfigBuilder.aFieldLevelEncryptionConfig()
+ .withEncryptionPath("$.payload", "$.encryptedPayload")
+ .withEncryptionCertificate(TestUtils.getTestEncryptionCertificate())
+ .withEncryptionCertificateFingerprint("97A2FFE9F0D48960EF31E87FCD7A55BF7843FB4A9EEEF01BDB6032AD6FEF146B")
+ .withEncryptionKeyFingerprint("F806B26BC4870E26986C70B6590AF87BAF4C2B56BB50622C51B12212DAFF2810")
+ .withEncryptionCertificateFingerprintFieldName("publicCertificateFingerprint")
+ .withEncryptionCertificateFingerprintHeaderName("x-public-certificate-fingerprint")
+ .withEncryptionKeyFingerprintFieldName("publicKeyFingerprint")
+ .withEncryptionKeyFingerprintHeaderName("x-public-key-fingerprint")
+ .withDecryptionPath("$.encryptedPayload", "$.payload")
+ .withDecryptionKey(TestUtils.getTestDecryptionKey())
+ .withOaepPaddingDigestAlgorithm("SHA-512")
+ .withOaepPaddingDigestAlgorithmFieldName("oaepPaddingDigestAlgorithm")
+ .withOaepPaddingDigestAlgorithmHeaderName("x-oaep-padding-digest-algorithm")
+ .withEncryptedValueFieldName("encryptedValue")
+ .withEncryptedKeyFieldName("encryptedKey")
+ .withEncryptedKeyHeaderName("x-encrypted-key")
+ .withIvFieldName("iv")
+ .withIvHeaderName("x-iv")
+ .withFieldValueEncoding(HEX)
+ .withEncryptionIVSize(16)
+ .build();
+ Assert.assertNotNull(config);
+ Assert.assertEquals(1, config.encryptionPaths.size());
+ Assert.assertNotNull(config.encryptionCertificate);
+ Assert.assertEquals("97A2FFE9F0D48960EF31E87FCD7A55BF7843FB4A9EEEF01BDB6032AD6FEF146B", config.encryptionCertificateFingerprint);
+ Assert.assertEquals("F806B26BC4870E26986C70B6590AF87BAF4C2B56BB50622C51B12212DAFF2810", config.encryptionKeyFingerprint);
+ Assert.assertEquals("publicCertificateFingerprint", config.encryptionCertificateFingerprintFieldName);
+ Assert.assertEquals("x-public-certificate-fingerprint", config.encryptionCertificateFingerprintHeaderName);
+ Assert.assertEquals("publicKeyFingerprint", config.encryptionKeyFingerprintFieldName);
+ Assert.assertEquals("x-public-key-fingerprint", config.encryptionKeyFingerprintHeaderName);
+ Assert.assertEquals(1, config.decryptionPaths.size());
+ Assert.assertNotNull(config.decryptionKey);
+ Assert.assertEquals("SHA-512", config.oaepPaddingDigestAlgorithm);
+ Assert.assertEquals("encryptedValue", config.encryptedValueFieldName);
+ Assert.assertEquals("encryptedKey", config.encryptedKeyFieldName);
+ Assert.assertEquals("x-encrypted-key", config.encryptedKeyHeaderName);
+ Assert.assertEquals("iv", config.ivFieldName);
+ Assert.assertEquals("x-iv", config.ivHeaderName);
+ Assert.assertEquals("oaepPaddingDigestAlgorithm", config.oaepPaddingDigestAlgorithmFieldName);
+ Assert.assertEquals("x-oaep-padding-digest-algorithm", config.oaepPaddingDigestAlgorithmHeaderName);
+ Assert.assertEquals(HEX, config.fieldValueEncoding);
+ assertThat(config.getIVSize().intValue(),equalTo(16));
+ }
+
+ @Test
+ public void testBuild_FailedIV() throws Exception {
+ try {
+ FieldLevelEncryptionConfig config = FieldLevelEncryptionConfigBuilder.aFieldLevelEncryptionConfig()
+ .withEncryptionPath("$.payload", "$.encryptedPayload")
+ .withEncryptionCertificate(TestUtils.getTestEncryptionCertificate())
+ .withEncryptionCertificateFingerprint("97A2FFE9F0D48960EF31E87FCD7A55BF7843FB4A9EEEF01BDB6032AD6FEF146B")
+ .withEncryptionKeyFingerprint("F806B26BC4870E26986C70B6590AF87BAF4C2B56BB50622C51B12212DAFF2810")
+ .withEncryptionCertificateFingerprintFieldName("publicCertificateFingerprint")
+ .withEncryptionCertificateFingerprintHeaderName("x-public-certificate-fingerprint")
+ .withEncryptionKeyFingerprintFieldName("publicKeyFingerprint")
+ .withEncryptionKeyFingerprintHeaderName("x-public-key-fingerprint")
+ .withDecryptionPath("$.encryptedPayload", "$.payload")
+ .withDecryptionKey(TestUtils.getTestDecryptionKey())
+ .withOaepPaddingDigestAlgorithm("SHA-512")
+ .withOaepPaddingDigestAlgorithmFieldName("oaepPaddingDigestAlgorithm")
+ .withOaepPaddingDigestAlgorithmHeaderName("x-oaep-padding-digest-algorithm")
+ .withEncryptedValueFieldName("encryptedValue")
+ .withEncryptedKeyFieldName("encryptedKey")
+ .withEncryptedKeyHeaderName("x-encrypted-key")
+ .withIvFieldName("iv")
+ .withIvHeaderName("x-iv")
+ .withFieldValueEncoding(HEX)
+ .withEncryptionIVSize(23)
+ .build();
+ assertFalse("It should raise an exception, but it didn't", true);
+ } catch ( IllegalArgumentException e) {
+ assertThat(e.getMessage(), equalTo("Supported IV Sizes are either 12 or 16!"));
+ }
}
@Test
diff --git a/src/test/java/com/mastercard/developer/encryption/JweConfigBuilderTest.java b/src/test/java/com/mastercard/developer/encryption/JweConfigBuilderTest.java
index 511f377..76d7a4b 100644
--- a/src/test/java/com/mastercard/developer/encryption/JweConfigBuilderTest.java
+++ b/src/test/java/com/mastercard/developer/encryption/JweConfigBuilderTest.java
@@ -8,19 +8,44 @@
import java.util.Collections;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertFalse;
+
public class JweConfigBuilderTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
- public void testBuild_Nominal() throws Exception {
+ public void testBuild_Nominal_iv12() throws Exception {
+ JweConfig config = JweConfigBuilder.aJweEncryptionConfig()
+ .withEncryptionCertificate(TestUtils.getTestEncryptionCertificate())
+ .withDecryptionKey(TestUtils.getTestDecryptionKey())
+ .withEncryptionPath("$", "$")
+ .withDecryptionPath("$.encryptedPayload", "$")
+ .withEncryptedValueFieldName("encryptedPayload")
+ .withEncryptionIVSize(12)
+ .build();
+ Assert.assertNotNull(config);
+ Assert.assertEquals(EncryptionConfig.Scheme.JWE, config.getScheme());
+ Assert.assertEquals(TestUtils.getTestDecryptionKey(), config.getDecryptionKey());
+ Assert.assertEquals(TestUtils.getTestEncryptionCertificate(), config.getEncryptionCertificate());
+ Assert.assertEquals("encryptedPayload", config.getEncryptedValueFieldName());
+ Assert.assertEquals(Collections.singletonMap("$.encryptedPayload", "$"), config.getDecryptionPaths());
+ Assert.assertEquals(Collections.singletonMap("$", "$"), config.getEncryptionPaths());
+ assertThat(config.getIVSize().intValue(),equalTo(12));
+ }
+
+ @Test
+ public void testBuild_Nominal_iv16() throws Exception {
JweConfig config = JweConfigBuilder.aJweEncryptionConfig()
.withEncryptionCertificate(TestUtils.getTestEncryptionCertificate())
.withDecryptionKey(TestUtils.getTestDecryptionKey())
.withEncryptionPath("$", "$")
.withDecryptionPath("$.encryptedPayload", "$")
.withEncryptedValueFieldName("encryptedPayload")
+ .withEncryptionIVSize(16)
.build();
Assert.assertNotNull(config);
Assert.assertEquals(EncryptionConfig.Scheme.JWE, config.getScheme());
@@ -29,8 +54,25 @@ public void testBuild_Nominal() throws Exception {
Assert.assertEquals("encryptedPayload", config.getEncryptedValueFieldName());
Assert.assertEquals(Collections.singletonMap("$.encryptedPayload", "$"), config.getDecryptionPaths());
Assert.assertEquals(Collections.singletonMap("$", "$"), config.getEncryptionPaths());
+ assertThat(config.getIVSize().intValue(),equalTo(16));
}
+ @Test
+ public void testBuild_FailedIV() throws Exception {
+ try {
+ JweConfig config = JweConfigBuilder.aJweEncryptionConfig()
+ .withEncryptionCertificate(TestUtils.getTestEncryptionCertificate())
+ .withDecryptionKey(TestUtils.getTestDecryptionKey())
+ .withEncryptionPath("$", "$")
+ .withDecryptionPath("$.encryptedPayload", "$")
+ .withEncryptedValueFieldName("encryptedPayload")
+ .withEncryptionIVSize(24)
+ .build();
+ assertFalse("It should raise an exception, but it didn't", true);
+ } catch ( IllegalArgumentException e) {
+ assertThat(e.getMessage(), equalTo("Supported IV Sizes are either 12 or 16!"));
+ }
+ }
@Test
public void testBuild_EncryptionKeyNoDecryptionKey() throws Exception {
JweConfig config = JweConfigBuilder.aJweEncryptionConfig()
diff --git a/src/test/java/com/mastercard/developer/encryption/JweEncryptionWithDefaultJsonEngineTest.java b/src/test/java/com/mastercard/developer/encryption/JweEncryptionWithDefaultJsonEngineTest.java
index 0d3c053..9d9eefd 100644
--- a/src/test/java/com/mastercard/developer/encryption/JweEncryptionWithDefaultJsonEngineTest.java
+++ b/src/test/java/com/mastercard/developer/encryption/JweEncryptionWithDefaultJsonEngineTest.java
@@ -85,6 +85,7 @@ public void testDecryptPayload_ShouldDecryptRootArrays() throws Exception {
" \"encryptedData\": \"eyJraWQiOiI3NjFiMDAzYzFlYWRlM2E1NDkwZTUwMDBkMzc4ODdiYWE1ZTZlYzBlMjI2YzA3NzA2ZTU5OTQ1MWZjMDMyYTc5IiwiY3R5IjoiYXBwbGljYXRpb24vanNvbiIsImVuYyI6IkEyNTZHQ00iLCJhbGciOiJSU0EtT0FFUC0yNTYifQ.IcTIce59pgtjODJn4PhR7oK3F-gxcd7dishTrT7T9y5VC0U5ZS_JdMoRe59_UTkJMY8Nykb2rv3Oh_jSDYRmGB_CWMIciXYMLHQptLTF5xI1ZauDPnooDMWoOCBD_d3I0wTJNcM7I658rK0ZWSByVK9YqhEo8UaIf4e6egRHQdZ2_IGKgICwmglv_uXQrYewOWFTKR1uMpya1N50MDnWax2NtnW3SljP3mARUBLBnRmOyubQCg-Mgn8fsOWWXm-KL9RrQq9AF_HJceoJl1rRgzPW7g6SLK6EjiGW_ArTmrLaOHg9bYOY_LrbyokK_M1pMo9qup70DHvjHkMZqIL3aQ.vtma3jBIo2STkquxTUX9PQ.9ZoQG0sFvQ.ms4bW3OFd03neRlex-zZ8w\"" +
"}";
JweConfig config = getTestJweConfigBuilder()
+ .withEncryptedValueFieldName("encryptedResponse")
.withDecryptionPath("$.encryptedData", "$")
.build();
diff --git a/src/test/resources/log4j.properties b/src/test/resources/log4j.properties
new file mode 100644
index 0000000..393e087
--- /dev/null
+++ b/src/test/resources/log4j.properties
@@ -0,0 +1,8 @@
+# Root logger option
+log4j.rootLogger=INFO, stdout
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
\ No newline at end of file