From 135ba81dde0fae4b8eb8785b623b5ecaee29aba6 Mon Sep 17 00:00:00 2001 From: jkarj Date: Tue, 1 Mar 2022 18:09:29 +0200 Subject: [PATCH] Enable external key decryption --- .../org/openeid/cdoc4j/CDOCDecrypter.java | 29 ++++++++++++--- .../org/openeid/cdoc4j/SecretKeySupplier.java | 9 +++++ .../openeid/cdoc4j/CDOC10DecrypterTest.java | 36 +++++++++++++++++-- .../openeid/cdoc4j/CDOC11DecrypterTest.java | 17 +++++++-- .../java/org/openeid/cdoc4j/TestUtil.java | 19 ++++++++-- 5 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/openeid/cdoc4j/SecretKeySupplier.java diff --git a/src/main/java/org/openeid/cdoc4j/CDOCDecrypter.java b/src/main/java/org/openeid/cdoc4j/CDOCDecrypter.java index 91e14ee..4c9e1e8 100644 --- a/src/main/java/org/openeid/cdoc4j/CDOCDecrypter.java +++ b/src/main/java/org/openeid/cdoc4j/CDOCDecrypter.java @@ -73,6 +73,7 @@ public class CDOCDecrypter { private Token token; private InputStream cdocInputStream; private CDOCFileSystemHandler cdocFileSystemHandler; + private SecretKeySupplier secretKeySupplier; /** * Sets the decryption token @@ -118,6 +119,17 @@ public CDOCDecrypter withCDOCFileSystemHandler(CDOCFileSystemHandler cdocFileSys return this; } + /** + * Sets the secret key supplier + * + * @param secretKeySupplier for decrypting CDOC + * @return the current instance + */ + public CDOCDecrypter withSecretKeySupplier(SecretKeySupplier secretKeySupplier) { + this.secretKeySupplier = secretKeySupplier; + return this; + } + /** * decrypts the CDOC into given directory and returns a list of decrypted file(s) * @@ -183,8 +195,7 @@ private List decryptCdoc(PayloadParser payloadParser) throws CDOCExcep String encryptionMethodUri = XmlEncParserUtil.getAttributeValue(xmlReader, "Algorithm"); EncryptionMethod encryptionMethod = EncryptionMethod.fromURI(encryptionMethodUri); XmlEncParser xmlParser = XmlEncParserFactory.getXmlEncParser(encryptionMethod, xmlReader); - Recipient recipient = chooseRecipient(xmlParser.getRecipients()); - SecretKey key = decryptKey(recipient, token); + SecretKey key = getSecretKey(xmlParser.getRecipients()); List dataFiles; if (encryptedPayloadIsDDOC(mimeType)) { @@ -208,9 +219,19 @@ private List decryptCdoc(PayloadParser payloadParser) throws CDOCExcep } } + private SecretKey getSecretKey(List recipients) throws CDOCException { + return token == null + ? secretKeySupplier.get(recipients) + : decryptKey(chooseRecipient(recipients), token); + } + private void validateParameters() throws DecryptionException { - if (token == null) { - throw new DecryptionException("Token used for decryption not set!"); + if (token != null && secretKeySupplier != null) { + throw new DecryptionException("Token and SecretKeySupplier can not be used together"); + } + + if (token == null && secretKeySupplier == null) { + throw new DecryptionException("Token or SecretKeySupplier used for decryption not set!"); } if (cdocInputStream == null) { diff --git a/src/main/java/org/openeid/cdoc4j/SecretKeySupplier.java b/src/main/java/org/openeid/cdoc4j/SecretKeySupplier.java new file mode 100644 index 0000000..7f8657b --- /dev/null +++ b/src/main/java/org/openeid/cdoc4j/SecretKeySupplier.java @@ -0,0 +1,9 @@ +package org.openeid.cdoc4j; + +import javax.crypto.SecretKey; +import java.util.List; + +@FunctionalInterface +public interface SecretKeySupplier { + SecretKey get(List recipients); +} diff --git a/src/test/java/org/openeid/cdoc4j/CDOC10DecrypterTest.java b/src/test/java/org/openeid/cdoc4j/CDOC10DecrypterTest.java index 8d3f108..a7475a3 100644 --- a/src/test/java/org/openeid/cdoc4j/CDOC10DecrypterTest.java +++ b/src/test/java/org/openeid/cdoc4j/CDOC10DecrypterTest.java @@ -1,15 +1,16 @@ package org.openeid.cdoc4j; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.*; import static org.openeid.cdoc4j.TestUtil.*; -import org.junit.Ignore; import org.junit.Test; import org.openeid.cdoc4j.exception.CDOCException; import org.openeid.cdoc4j.exception.DecryptionException; import org.openeid.cdoc4j.token.pkcs12.PKCS12Token; import org.openeid.cdoc4j.xml.exception.XmlParseException; +import javax.crypto.spec.SecretKeySpec; import java.io.*; import java.util.Arrays; import java.util.List; @@ -60,6 +61,22 @@ public void decryptCDOC10_buildToDirectory_destinationIsNotDirectory_shouldThrow } } + @Test + public void decrypt_withTokenAndSecretKeySupplier_shouldThrowException() { + DecryptionException exception = assertThrows(DecryptionException.class, () -> new CDOCDecrypter() + .withSecretKeySupplier(recipients -> new SecretKeySpec("".getBytes(UTF_8), "AES")) + .withToken(new PKCS12Token(new FileInputStream("src/test/resources/rsa/rsa.p12"), "test")) + .decrypt(new File("target/testdata"))); + assertEquals("Token and SecretKeySupplier can not be used together", exception.getMessage()); + } + + @Test + public void decrypt_withoutTokenAndSecretKeySupplier_shouldThrowException() { + DecryptionException exception = assertThrows(DecryptionException.class, () -> new CDOCDecrypter() + .decrypt(new File("target/testdata"))); + assertEquals("Token or SecretKeySupplier used for decryption not set!", exception.getMessage()); + } + @Test public void decryptValidCDOC10_withSingleFile_shouldSucceed() throws Exception { FileInputStream cdocInputStream = new FileInputStream("src/test/resources/cdoc/valid_cdoc10.cdoc"); @@ -74,6 +91,21 @@ public void decryptValidCDOC10_withSingleFile_shouldSucceed() throws Exception { deleteTestFiles(dataFiles); } + @Test + public void decryptValidCDOC10_RSA_toMemory_withRecipientSecretKeyResolver_shouldSucceed() throws Exception { + FileInputStream cdocInputStream = new FileInputStream("src/test/resources/cdoc/valid_cdoc10.cdoc"); + PKCS12Token token = new PKCS12Token(new FileInputStream("src/test/resources/rsa/rsa.p12"), "test"); + List dataFiles = new CDOCDecrypter() + .withSecretKeySupplier(getSecretKeySupplier(token)) + .withCDOC(cdocInputStream) + .decrypt(new File("target/testdata")); + + assertSame(1, dataFiles.size()); + assertFileDataFileContent(dataFiles.get(0), testFileName, "lorem ipsum"); + assertStreamClosed(cdocInputStream); + deleteTestFiles(dataFiles); + } + @Test public void decryptValidCDOC10_toMemory_withSingleFile_shouldSucceed() throws Exception { @@ -272,7 +304,7 @@ private List buildAndDecryptToMemory(DataFile... dataFiles) throws CDO .withToken(token) .withCDOC(bais) .decrypt(); - + assertTrue(decryptedDataFiles.size() == dataFiles.length); for (DataFile dataFile : dataFiles) { assertStreamClosed(dataFile.getContent()); diff --git a/src/test/java/org/openeid/cdoc4j/CDOC11DecrypterTest.java b/src/test/java/org/openeid/cdoc4j/CDOC11DecrypterTest.java index 989b89a..e10adf3 100644 --- a/src/test/java/org/openeid/cdoc4j/CDOC11DecrypterTest.java +++ b/src/test/java/org/openeid/cdoc4j/CDOC11DecrypterTest.java @@ -5,9 +5,7 @@ import org.junit.Assert; import org.junit.Ignore; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.openeid.cdoc4j.token.pkcs11.PKCS11Token; import org.openeid.cdoc4j.token.pkcs11.PKCS11TokenParams; import org.openeid.cdoc4j.token.pkcs12.PKCS12Token; @@ -49,6 +47,21 @@ public void decryptValidCDOC11_RSA_toMemory_withSingleFile_shouldSucceed() throw assertDataFileContent(dataFiles.get(0), "lorem2.txt", "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis urna, consequat vel eros vel, ornare aliquet ante. Integer justo dolor, egestas nec mi vitae, semper consectetur odio. Morbi sagittis egestas leo, vel molestie ligula condimentum vitae. Aliquam porttitor in turpis ornare venenatis. Cras vel nunc quis massa tristique consectetur. Vestibulum"); } + @Test + public void decryptValidCDOC11_RSA_toMemory_withRecipientSecretKeyResolver_shouldSucceed() throws Exception { + FileInputStream cdocInputStream = new FileInputStream("src/test/resources/cdoc/valid_cdoc11_RSA.cdoc"); + PKCS12Token token = new PKCS12Token(new FileInputStream("src/test/resources/rsa/rsa.p12"), "test"); + List dataFiles = new CDOCDecrypter() + .withSecretKeySupplier(getSecretKeySupplier(token)) + .withCDOC(cdocInputStream) + .decrypt(new File("target/testdata")); + + assertSame(1, dataFiles.size()); + assertFileDataFileContent(dataFiles.get(0), "lorem2.txt", "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce felis urna, consequat vel eros vel, ornare aliquet ante. Integer justo dolor, egestas nec mi vitae, semper consectetur odio. Morbi sagittis egestas leo, vel molestie ligula condimentum vitae. Aliquam porttitor in turpis ornare venenatis. Cras vel nunc quis massa tristique consectetur. Vestibulum"); + assertStreamClosed(cdocInputStream); + deleteTestFiles(dataFiles); + } + @Test public void decryptInvalidCDOC11_RSA_withMultipleFiles_shouldDeleteAllFiles() throws Exception { PKCS12Token token = new PKCS12Token(new FileInputStream("src/test/resources/rsa/rsa.p12"), "test"); diff --git a/src/test/java/org/openeid/cdoc4j/TestUtil.java b/src/test/java/org/openeid/cdoc4j/TestUtil.java index 6ea5236..b20816c 100644 --- a/src/test/java/org/openeid/cdoc4j/TestUtil.java +++ b/src/test/java/org/openeid/cdoc4j/TestUtil.java @@ -1,9 +1,10 @@ package org.openeid.cdoc4j; import org.apache.commons.io.IOUtils; +import org.openeid.cdoc4j.exception.DecryptionException; +import org.openeid.cdoc4j.token.pkcs12.PKCS12Token; -import static org.junit.Assert.*; - +import javax.crypto.spec.SecretKeySpec; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -13,6 +14,8 @@ import java.util.List; import java.util.UUID; +import static org.junit.Assert.*; + public class TestUtil { public static void assertStreamClosed(InputStream inputStream) { @@ -86,4 +89,16 @@ public static void closeInMemoryStreams(List dataFiles) { IOUtils.closeQuietly(dataFile.getContent()); } } + + public static SecretKeySupplier getSecretKeySupplier(PKCS12Token token) { + return recipients -> { + try { + RSARecipient recipient = (RSARecipient) recipients.get(0); + return new SecretKeySpec(token.decrypt(recipient), recipient.getCertificate().getSigAlgName()); + } + catch (DecryptionException e) { + throw new IllegalStateException("Unable to decrypt", e); + } + }; + } }