diff --git a/pom.xml b/pom.xml index bd9a2e2..e386ee1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.mastercard.developer client-encryption - 1.8.2 + 1.8.3 jar Library for Mastercard API compliant payload encryption/decryption https://github.com/Mastercard/client-encryption-java diff --git a/src/main/java/com/mastercard/developer/utils/EncryptionUtils.java b/src/main/java/com/mastercard/developer/utils/EncryptionUtils.java index 0ed7e56..8459d7c 100644 --- a/src/main/java/com/mastercard/developer/utils/EncryptionUtils.java +++ b/src/main/java/com/mastercard/developer/utils/EncryptionUtils.java @@ -1,8 +1,8 @@ package com.mastercard.developer.utils; -import java.io.FileInputStream; -import java.io.FileNotFoundException; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; @@ -32,17 +32,32 @@ private EncryptionUtils() { /** * Populate a X509 encryption certificate object with the certificate data at the given file path. */ - public static Certificate loadEncryptionCertificate(String certificatePath) throws CertificateException, FileNotFoundException { + public static Certificate loadEncryptionCertificate(String certificatePath) throws CertificateException, IOException { + return loadEncryptionCertificate(Files.newInputStream(Paths.get(certificatePath))); + } + + /** + * Populate a X509 encryption certificate object with the certificate data at the given certificate data in bytes. + */ + public static Certificate loadEncryptionCertificate(InputStream certificateStream) throws CertificateException { CertificateFactory factory = CertificateFactory.getInstance("X.509"); - return factory.generateCertificate(new FileInputStream(certificatePath)); + return factory.generateCertificate(certificateStream); } /** * Load a RSA decryption key from a file (PEM or DER). */ public static PrivateKey loadDecryptionKey(String keyFilePath) throws GeneralSecurityException, IOException { - byte[] keyDataBytes = Files.readAllBytes(Paths.get(keyFilePath)); - String keyDataString = new String(keyDataBytes, StandardCharsets.UTF_8); + InputStream keyStream = new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFilePath))); + return loadDecryptionKey(keyStream); + } + + /** + * Load a RSA decryption key from key data in bytes. + */ + public static PrivateKey loadDecryptionKey(InputStream keyDataStream) throws GeneralSecurityException, IOException { + byte[] keyBytes = keyDataStream.readAllBytes(); + String keyDataString = new String(keyBytes, StandardCharsets.UTF_8); if (keyDataString.contains(PKCS_1_PEM_HEADER)) { // OpenSSL / PKCS#1 Base64 PEM encoded file @@ -63,17 +78,17 @@ public static PrivateKey loadDecryptionKey(String keyFilePath) throws GeneralSec } // We assume it's a PKCS#8 DER encoded binary file - return readPkcs8PrivateKey(Files.readAllBytes(Paths.get(keyFilePath))); + return readPkcs8PrivateKey(keyBytes); } /** * Load a RSA decryption key out of a PKCS#12 container. */ public static PrivateKey loadDecryptionKey(String pkcs12KeyFilePath, - String decryptionKeyAlias, - String decryptionKeyPassword) throws GeneralSecurityException, IOException { + String decryptionKeyAlias, + String decryptionKeyPassword) throws GeneralSecurityException, IOException { KeyStore pkcs12KeyStore = KeyStore.getInstance("PKCS12"); - pkcs12KeyStore.load(new FileInputStream(pkcs12KeyFilePath), decryptionKeyPassword.toCharArray()); + pkcs12KeyStore.load(Files.newInputStream(Paths.get(pkcs12KeyFilePath)), decryptionKeyPassword.toCharArray()); return (PrivateKey) pkcs12KeyStore.getKey(decryptionKeyAlias, decryptionKeyPassword.toCharArray()); } diff --git a/src/test/java/com/mastercard/developer/utils/EncryptionUtilsTest.java b/src/test/java/com/mastercard/developer/utils/EncryptionUtilsTest.java index 132e6da..37255d1 100644 --- a/src/test/java/com/mastercard/developer/utils/EncryptionUtilsTest.java +++ b/src/test/java/com/mastercard/developer/utils/EncryptionUtilsTest.java @@ -6,7 +6,10 @@ import org.junit.jupiter.params.provider.CsvSource; import org.junit.rules.ExpectedException; +import java.io.InputStream; +import java.nio.file.Files; import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.spec.InvalidKeySpecException; @@ -68,6 +71,26 @@ void testLoadDecryptionKey_ShouldSupportPkcs8Der(String keyLocation, String expe assertArrayEquals(base64Decode(expectedEncoding), privateKey.getEncoded()); } + @ParameterizedTest + @CsvSource({ + "pkcs8/test_key_pkcs8-2048.der, MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD0ynqAQWn0T7/VJLletTJgoxsTt5TR3IkJ+Yk/Pxg6Q5hXuiGrBdC+OVo/9hrNnptuZh9rZYKto6lbSjYFiKMeBDvPZrYDPzusp0C0KllIoVbzYiOezD76XHsQAEje0UXbzZlXstPXef2bi2HkqV26ST167L5O4moK8+7jHMT80T6XgsUyvyt8PjsQ9CSu6fnD9NfCSYmt2cb16OXcEtA7To2zoGznXqB6JhntFjG0jxee7RkLR+moOqMI9kFM5GSIV4uhwQ9FtOCjUf7TFAU12wwfX/QXUEj6G93GVtzf6QdkVkWh4EyRHeMLyMNc5c0Iw1ZvXdOKfoeo9F47QpbzAgMBAAECggEAK3dMmzuCSdxjTsCPnc6E3H35z914Mm97ceb6RN26OpZIFcO6OLj2oOBkMxlLFxnDta2yhIpo0tZNuyUJRKBHfov35tLxHNB8kyK7rYIbincDjoHtm0PfJuuG+odiaRY11lrCkLzzOr6xlo4AWu7r8qkQnqQtAqrXc4xu7artG4rfMIunGnjjWQGzovtey1JgZctO97MU4Wvw18vgYBI6JM4eHJkZxgEhVQblBTKZs4OfiWk6MRHchgvqnWugwl213FgCzwy9cnyxTP13i9QKaFzL29TYmmN6bRWBH95z41M8IAa0CGahrSJjudZCFwsFh413YWv/pdqdkKHg1sqseQKBgQD641RYQkMn4G9vOiwB/is5M0OAhhUdWH1QtB8vvhY5ISTjFMqgIIVQvGmqDDk8QqFMOfFFqLtnArGn8HrKmBXMpRigS4ae/QgHEz34/RFjNDQ9zxIf/yoCRH5PmnPPU6x8j3bj/vJMRQA6/yngoca+9qvi3R32AtC5DUELnwyzNwKBgQD5x1iEV+albyCNNyLoT/f6LSH1NVcO+0IOvIaAVMtfy+hEEXz7izv3/AgcogVZzRARSK0qsQ+4WQN6Q2WG5cQYSyB92PR+VgwhnagVvA+QHNDL988xoMhB5r2D2IVSRuTB2EOg7LiWHUHIExaxVkbADODDj7YV2aQCJVv0gbDQJQKBgQCaABix5Fqci6NbPvXsczvM7K6uoZ8sWDjz5NyPzbqObs3ZpdWK3Ot4V270tnQbjTq9M4PqIlyGKp0qXO7ClQAskdq/6hxEU0UuMp2DzLNzlYPLvON/SH1czvZJnqEfzli+TMHJyaCpOGGf1Si7fhIk/f0cUGYnsCq2rHAU1hhRmQKBgE/BJTRs1MqyJxSwLEc9cZLCYntnYrr342nNLK1BZgbalvlVFDFFjgpqwTRTT54S6jR6nkBpdPmKAqBBcOOX7ftL0b4dTkQguZLqQkdeWyHK8aiPIetYyVixkoXM1xUkadqzcTSrIW1dPiniXnaVc9XSxtnqw1tKuSGuSCRUXN65AoGBAN/AmT1S4PAQpSWufC8NUJey8S0bURUNNjd52MQ7pWzGq2QC00+dBLkTPj3KOGYpXw9ScZPbxOthBFzHOxERWo16AFw3OeRtn4VB1QJ9XvoA/oz4lEhJKbwUfuFGGvSpYvg3vZcOHF2zlvcUu7C0ub/WhOjV9jZvU5B2Ev8x1neb", + "pkcs8/test_key_pkcs8-2048.pem, MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD0ynqAQWn0T7/VJLletTJgoxsTt5TR3IkJ+Yk/Pxg6Q5hXuiGrBdC+OVo/9hrNnptuZh9rZYKto6lbSjYFiKMeBDvPZrYDPzusp0C0KllIoVbzYiOezD76XHsQAEje0UXbzZlXstPXef2bi2HkqV26ST167L5O4moK8+7jHMT80T6XgsUyvyt8PjsQ9CSu6fnD9NfCSYmt2cb16OXcEtA7To2zoGznXqB6JhntFjG0jxee7RkLR+moOqMI9kFM5GSIV4uhwQ9FtOCjUf7TFAU12wwfX/QXUEj6G93GVtzf6QdkVkWh4EyRHeMLyMNc5c0Iw1ZvXdOKfoeo9F47QpbzAgMBAAECggEAK3dMmzuCSdxjTsCPnc6E3H35z914Mm97ceb6RN26OpZIFcO6OLj2oOBkMxlLFxnDta2yhIpo0tZNuyUJRKBHfov35tLxHNB8kyK7rYIbincDjoHtm0PfJuuG+odiaRY11lrCkLzzOr6xlo4AWu7r8qkQnqQtAqrXc4xu7artG4rfMIunGnjjWQGzovtey1JgZctO97MU4Wvw18vgYBI6JM4eHJkZxgEhVQblBTKZs4OfiWk6MRHchgvqnWugwl213FgCzwy9cnyxTP13i9QKaFzL29TYmmN6bRWBH95z41M8IAa0CGahrSJjudZCFwsFh413YWv/pdqdkKHg1sqseQKBgQD641RYQkMn4G9vOiwB/is5M0OAhhUdWH1QtB8vvhY5ISTjFMqgIIVQvGmqDDk8QqFMOfFFqLtnArGn8HrKmBXMpRigS4ae/QgHEz34/RFjNDQ9zxIf/yoCRH5PmnPPU6x8j3bj/vJMRQA6/yngoca+9qvi3R32AtC5DUELnwyzNwKBgQD5x1iEV+albyCNNyLoT/f6LSH1NVcO+0IOvIaAVMtfy+hEEXz7izv3/AgcogVZzRARSK0qsQ+4WQN6Q2WG5cQYSyB92PR+VgwhnagVvA+QHNDL988xoMhB5r2D2IVSRuTB2EOg7LiWHUHIExaxVkbADODDj7YV2aQCJVv0gbDQJQKBgQCaABix5Fqci6NbPvXsczvM7K6uoZ8sWDjz5NyPzbqObs3ZpdWK3Ot4V270tnQbjTq9M4PqIlyGKp0qXO7ClQAskdq/6hxEU0UuMp2DzLNzlYPLvON/SH1czvZJnqEfzli+TMHJyaCpOGGf1Si7fhIk/f0cUGYnsCq2rHAU1hhRmQKBgE/BJTRs1MqyJxSwLEc9cZLCYntnYrr342nNLK1BZgbalvlVFDFFjgpqwTRTT54S6jR6nkBpdPmKAqBBcOOX7ftL0b4dTkQguZLqQkdeWyHK8aiPIetYyVixkoXM1xUkadqzcTSrIW1dPiniXnaVc9XSxtnqw1tKuSGuSCRUXN65AoGBAN/AmT1S4PAQpSWufC8NUJey8S0bURUNNjd52MQ7pWzGq2QC00+dBLkTPj3KOGYpXw9ScZPbxOthBFzHOxERWo16AFw3OeRtn4VB1QJ9XvoA/oz4lEhJKbwUfuFGGvSpYvg3vZcOHF2zlvcUu7C0ub/WhOjV9jZvU5B2Ev8x1neb", + "pkcs1/test_key_pkcs1-2048.pem, MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDMJPwAG210B9bznVVU0xKSmBxZnXIVODE275yg+kvxSDU5mBFg6CD59yP8DwxNiz/JeAgmEjWChdUX/01k9+vKsE/F4Ug4l74IZ48YyBe/cXuj25XTXTNzIiaAqe2/c2ssJXD22vMghoo2C/CCY8OF6AexalUyvsuTYMvlCY8Tbwnx6Qmdh8cnMZRKUlgmkKgJW07ts88MJxaCuU7OhqBO6O2nIFFYA9EmgDUnZvu6/Ouqa5QZ/xiqDwwh43tS7GziNKTeuwNcwITlwJUpr5t+hNXVdFAnY8AF026/Af/CqixGDdhOAV1YzjoBjgOAjPOpj/FJ4uJt3UfODbAdBMzrAgMBAAECggEBAMuaO2eT81cdFopEKb3/AfAJG4VZXVXChHspAYsf96v+e28ktnhzK6iCj3YuP/P65LR4LZBi6tFxzzUu4K7KAXZW4EkYReKDnPle574smlrKwSiMseJrewviTIpYcJFYeNQ/x2m8t22CIciDoe05uOENqNaOmciRuBIWIWUeYn9aoDsiMQ56EaKpOOt/Jekvyttwa35yElvbPSxF2UAGOXUxPaz1wIdkvDPrHV4NAMNjLDalFGYR10xnlVa0B3fsjxFaXY0esyyCBxonMgDkud12xGqYxXDv7WoUggrRkc5OSe++BDz5Ts/6vy/v7ea5+9fglqQ+KeCPnjeyVs4ZqkECgYEA7UXhER4p8KkYyjuRZmwABICQRv7kVy7iOsTNmR/aOTlYLjORaWZICprLVYsgQY/bksKjSjy9MR/HS0426QkyYQl7BWLnnU3HP5yVytuIlFxfo/xFMJ5wm1CNQ6rAcO8o02lwATzPPRg4ui0nGEIflJPdoTTuxzXn0r0QYzN52uECgYEA3EG9uCEzE3uVO5K/Ew7A1A5aAp9bNX59NctDtKAWEgKoRXrudgebSv+P2U3ZW3G6HouGpnavWSHMQ6HIfPtgEg0BhSqOOgUBVR+wdntq4zux1AFnHVXBLZdE+CWCmyj3ASFMTPvkLssfj/ae7UEhUB24TZxz3nAo8RR7Gmz8TUsCgYEAimnEVK8K+kg6nObI+D2yeO3ivHe/DpjcAjqCUXxCWjV4mmMcxaaUChOo4Dsr0vMvvNpsVUc/eqO2J9j1sVXbHL5iFI9Q2/Pect5Oh6svbpTAejIUzrrup7wC3GGEp5zsbP/KBf7KSjKSDRGAB+ey8oKbvInbbTymAsql/6iswiECgYEAuukzFZFe5banMpHaglKvwoSXT8hpv2Ci4samox6C/C+zGpsyx4i2+RMcwHy26kn9drRSxOrM7OeojvA40g8EPO06kAZIAeaDdfhZaIJdd44N32p9VcCTGZxYE/jI9+Dwk83tERtlTWxkUWgpAA+YNIO0BnCxR1+I7uTBfvBjvzcCgYBDrjptyo8peY0Jzaijg7hWBwLZp/M27GhR8uAV6FVESP+1uG06k3g3CECxCE3Pi6HVSaW6UpNMZnrtVaKQCJDyKnkdIExFVP8DhkJSHmid1TXJXEfpDT57JD4UX6NOCcB0ynSyYvDvJ6bodx6SSyB03CEMqJ8VMjXeYpZSHyAF7A==" + }) + void testLoadDecryptionKey_ShouldSupportPkcs8DerBytes(String keyLocation, String expectedEncoding) throws Exception { + + // GIVEN + InputStream keyStream = Files.newInputStream(Paths.get(String.format("./src/test/resources/keys/%s", keyLocation))); + + // WHEN + PrivateKey privateKey = EncryptionUtils.loadDecryptionKey(keyStream); + + // THEN + assertNotNull(privateKey); + assertEquals("RSA", privateKey.getAlgorithm()); + assertArrayEquals(base64Decode(expectedEncoding), privateKey.getEncoded()); + } + @ParameterizedTest @CsvSource({ "test_key_pkcs1-512.pem", @@ -87,6 +110,25 @@ void testLoadDecryptionKey_ShouldSupportPkcs1Base64Pem_512bits(String fileName) assertEquals("RSA", privateKey.getAlgorithm()); } + @ParameterizedTest + @CsvSource({ + "test_key_pkcs1-512.pem", + "test_key_pkcs1-1024.pem", + "test_key_pkcs1-4096.pem" + }) + void testLoadDecryptionKey_ShouldSupportPkcs1Base64PemBytes_512bits(String fileName) throws Exception { + + // GIVEN + InputStream keyStream = Files.newInputStream(Paths.get(String.format("./src/test/resources/keys/pkcs1/%s", fileName))); + + // WHEN + PrivateKey privateKey = EncryptionUtils.loadDecryptionKey(keyStream); + + // THEN + assertNotNull(privateKey); + assertEquals("RSA", privateKey.getAlgorithm()); + } + @Test public void testLoadDecryptionKey_ShouldSupportPkcs12() throws Exception {