Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable external key decryption #12

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions src/main/java/org/openeid/cdoc4j/CDOCDecrypter.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public class CDOCDecrypter {
private Token token;
private InputStream cdocInputStream;
private CDOCFileSystemHandler cdocFileSystemHandler;
private SecretKeySupplier secretKeySupplier;

/**
* Sets the decryption token
Expand Down Expand Up @@ -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)
*
Expand Down Expand Up @@ -183,8 +195,7 @@ private List<DataFile> 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<DataFile> dataFiles;
if (encryptedPayloadIsDDOC(mimeType)) {
Expand All @@ -208,9 +219,19 @@ private List<DataFile> decryptCdoc(PayloadParser payloadParser) throws CDOCExcep
}
}

private SecretKey getSecretKey(List<Recipient> 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) {
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/org/openeid/cdoc4j/SecretKeySupplier.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.openeid.cdoc4j;

import javax.crypto.SecretKey;
import java.util.List;

@FunctionalInterface
public interface SecretKeySupplier {
SecretKey get(List<Recipient> recipients);
}
36 changes: 34 additions & 2 deletions src/test/java/org/openeid/cdoc4j/CDOC10DecrypterTest.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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");
Expand All @@ -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<File> 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 {

Expand Down Expand Up @@ -272,7 +304,7 @@ private List<DataFile> buildAndDecryptToMemory(DataFile... dataFiles) throws CDO
.withToken(token)
.withCDOC(bais)
.decrypt();

assertTrue(decryptedDataFiles.size() == dataFiles.length);
for (DataFile dataFile : dataFiles) {
assertStreamClosed(dataFile.getContent());
Expand Down
17 changes: 15 additions & 2 deletions src/test/java/org/openeid/cdoc4j/CDOC11DecrypterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<File> 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");
Expand Down
19 changes: 17 additions & 2 deletions src/test/java/org/openeid/cdoc4j/TestUtil.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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) {
Expand Down Expand Up @@ -86,4 +89,16 @@ public static void closeInMemoryStreams(List<DataFile> 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);
}
};
}
}