Skip to content

Commit

Permalink
Merge pull request #86 from Mastercard/supporting-iv-size-12
Browse files Browse the repository at this point in the history
Adding support for IV size 12
  • Loading branch information
karen-avetisyan-mc authored Apr 10, 2024
2 parents dafbd3e + 9c0257e commit f96fbb3
Show file tree
Hide file tree
Showing 13 changed files with 179 additions and 11 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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();
```

Expand Down
3 changes: 1 addition & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.mastercard.developer</groupId>
<artifactId>client-encryption</artifactId>
<version>1.7.13-SNAPSHOT</version>
<version>1.8.0</version>
<packaging>jar</packaging>
<description>Library for Mastercard API compliant payload encryption/decryption</description>
<url>https://github.com/Mastercard/client-encryption-java</url>
Expand Down Expand Up @@ -96,7 +96,6 @@
<version>4.13.1</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -107,4 +114,6 @@ Map<String, String> getDecryptionPaths() {
String getEncryptedValueFieldName() {
return encryptedValueFieldName;
}

public Integer getIVSize() { return ivSize; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ abstract class EncryptionConfigBuilder {
protected Map<String, String> decryptionPaths = new HashMap<>();
protected String encryptedValueFieldName;

protected Integer ivSize = 16;

void computeEncryptionKeyFingerprintWhenNeeded() throws EncryptionException {
try {
if ((encryptionCertificate == null && encryptionKey == null) || !isNullOrEmpty(encryptionKeyFingerprint)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand All @@ -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");
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@
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 {

@Rule
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())
Expand All @@ -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());
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
8 changes: 8 additions & 0 deletions src/test/resources/log4j.properties
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit f96fbb3

Please sign in to comment.