From 252c2bbd2241be4f4cea8aa955ad215e7e1c0d3b Mon Sep 17 00:00:00 2001 From: Maxim Grischenko Date: Tue, 6 Feb 2024 15:07:18 +0100 Subject: [PATCH] write to inbox with ECDH --- README.md | 3 +- .../business/impl/e2e/BaseE2ETest.java | 9 +-- .../impl/e2e/BasicFunctionalityTest.java | 19 ++++-- .../impl/e2e/BasicFunctionalityUtf8Test.java | 9 +-- ...icFunctionalityWithPasswordChangeTest.java | 13 ++-- .../impl/e2e/DataTamperingResistanceTest.java | 3 +- .../datasafe/cli/commands/inbox/Share.java | 5 +- .../keys/DocumentKeyStoreOperations.java | 3 + .../api/profile/keys/PrivateKeyService.java | 3 + .../keys/DFSPrivateKeyServiceImpl.java | 6 ++ .../keys/DocumentKeyStoreOperationsImpl.java | 12 ++++ .../cmsencryption/CMSEncryptionService.java | 4 +- .../EncryptedDocumentWriteService.java | 3 +- .../types/encryption/KeyCreationConfig.java | 4 +- .../encryption/MutableEncryptionConfig.java | 10 +++ .../CMSEncryptionServiceImpl.java | 33 ++++++++-- .../decryptors/DecryptorFactory.java | 7 +- .../decryptors/KeyAgreeDecryptor.java | 32 +++++++++ .../document/CMSDocumentWriteService.java | 6 +- .../impl/keystore/KeyStoreServiceImpl.java | 6 +- .../impl/keystore/PublicKeySerdeImpl.java | 15 ++++- .../CmsEncryptionServiceImplTest.java | 65 ++++++++++++++----- ...OperationsTestWithDefaultDatasafeTest.java | 26 ++++++-- .../inbox/api/actions/WriteToInbox.java | 5 +- .../inbox/impl/actions/WriteToInboxImpl.java | 17 +++-- .../api/actions/WriteToInboxImplTest.java | 18 +++-- ...domActionsOnSimpleDatasafeAdapterTest.java | 3 +- .../framework/services/OperationExecutor.java | 34 +++++++--- .../rest/impl/controller/InboxController.java | 13 ++-- .../src/main/resources/application.properties | 4 +- .../SwitchableCmsEncryptionImpl.java | 5 +- .../types/api/actions/WriteInboxRequest.java | 59 +++++++++++++++++ .../types/api/actions/WriteRequest.java | 37 +++-------- pom.xml | 24 ++----- 34 files changed, 375 insertions(+), 140 deletions(-) create mode 100644 datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/decryptors/KeyAgreeDecryptor.java create mode 100644 datasafe-types-api/src/main/java/de/adorsys/datasafe/types/api/actions/WriteInboxRequest.java diff --git a/README.md b/README.md index 6a867c696..f160362dd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ -[![Build Status](https://travis-ci.com/adorsys/datasafe.svg?branch=develop)](https://travis-ci.com/adorsys/datasafe) [![codecov](https://codecov.io/gh/adorsys/datasafe/branch/develop/graph/badge.svg)](https://codecov.io/gh/adorsys/datasafe) -[![Maintainability](https://api.codeclimate.com/v1/badges/06ae7d4cafc3012cee85/maintainability)](https://codeclimate.com/github/adorsys/datasafe/maintainability) +[![Maintainability](https://codeclimate.com/github/adorsys/datasafe.png)](https://codeclimate.com/github/adorsys/datasafe/maintainability) # Secure, Encrypted and Versioned Data Storage Library diff --git a/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BaseE2ETest.java b/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BaseE2ETest.java index d544e2842..b8a81c126 100644 --- a/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BaseE2ETest.java +++ b/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BaseE2ETest.java @@ -23,6 +23,7 @@ import de.adorsys.datasafe.types.api.actions.ListRequest; import de.adorsys.datasafe.types.api.actions.ReadRequest; import de.adorsys.datasafe.types.api.actions.RemoveRequest; +import de.adorsys.datasafe.types.api.actions.WriteInboxRequest; import de.adorsys.datasafe.types.api.actions.WriteRequest; import de.adorsys.datasafe.types.api.resource.AbsoluteLocation; import de.adorsys.datasafe.types.api.resource.BasePrivateResource; @@ -117,9 +118,9 @@ protected void writeDataToPrivate(UserIDAuth auth, String path, String data) { } @SneakyThrows - protected void writeDataToInbox(UserIDAuth auth, String path, String data) { + protected void writeDataToInbox(UserIDAuth owner, UserIDAuth auth, String path, String data) { try (OutputStream stream = writeToInbox.write( - WriteRequest.forDefaultPublic(Collections.singleton(auth.getUserID()), path) + WriteInboxRequest.forDefaultPublic(owner, Collections.singleton(auth.getUserID()), path) )) { stream.write(data.getBytes(UTF_8)); @@ -191,9 +192,9 @@ protected void registerJohnAndJane() { } @SneakyThrows - protected void sendToInbox(UserID to, String filename, String data) { + protected void sendToInbox(UserIDAuth from, UserID to, String filename, String data) { try (OutputStream stream = writeToInbox.write( - WriteRequest.forDefaultPublic(Collections.singleton(to), "./" + filename) + WriteInboxRequest.forDefaultPublic(from, Collections.singleton(to), "./" + filename) )) { stream.write(data.getBytes()); } diff --git a/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BasicFunctionalityTest.java b/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BasicFunctionalityTest.java index 8ee49c217..7ebeabe27 100644 --- a/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BasicFunctionalityTest.java +++ b/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BasicFunctionalityTest.java @@ -8,6 +8,7 @@ import de.adorsys.datasafe.types.api.actions.ListRequest; import de.adorsys.datasafe.types.api.actions.ReadRequest; import de.adorsys.datasafe.types.api.actions.RemoveRequest; +import de.adorsys.datasafe.types.api.actions.WriteInboxRequest; import de.adorsys.datasafe.types.api.actions.WriteRequest; import de.adorsys.datasafe.types.api.global.Version; import de.adorsys.datasafe.types.api.resource.AbsoluteLocation; @@ -194,12 +195,15 @@ void testUserIsRemovedWithFiles(WithStorageProvider.StorageDescriptor descriptor void testMultipleRecipientsSharing(WithStorageProvider.StorageDescriptor descriptor) { init(descriptor); + UserIDAuth owner = registerUser("owner"); + UserIDAuth john = registerUser("john"); UserIDAuth jane = registerUser("jane"); UserIDAuth jamie = registerUser("jamie"); String multiShareFile = "multishare.txt"; - try (OutputStream os = writeToInbox.write(WriteRequest.forDefaultPublic( + try (OutputStream os = writeToInbox.write(WriteInboxRequest.forDefaultPublic( + owner, ImmutableSet.of(john.getUserID(), jane.getUserID(), jamie.getUserID()), multiShareFile)) ) { @@ -217,6 +221,8 @@ void testMultipleRecipientsSharing(WithStorageProvider.StorageDescriptor descrip void testMultipleRecipientsSharingLargeChunk(WithStorageProvider.StorageDescriptor descriptor) { init(descriptor); + UserIDAuth owner = registerUser("owner"); + UserIDAuth john = registerUser("john"); UserIDAuth jane = registerUser("jane"); UserIDAuth jamie = registerUser("jamie"); @@ -224,7 +230,8 @@ void testMultipleRecipientsSharingLargeChunk(WithStorageProvider.StorageDescript String multiShareFile = "multishare.txt"; byte[] bytes = new byte[LARGE_SIZE]; ThreadLocalRandom.current().nextBytes(bytes); - try (OutputStream os = writeToInbox.write(WriteRequest.forDefaultPublic( + try (OutputStream os = writeToInbox.write(WriteInboxRequest.forDefaultPublic( + owner, ImmutableSet.of(john.getUserID(), jane.getUserID(), jamie.getUserID()), multiShareFile)) ) { @@ -253,7 +260,7 @@ void testWriteToPrivateListPrivateReadPrivateAndSendToAndReadFromInbox( String privateContentJane = readPrivateUsingPrivateKey(jane, privateJane.getResource().asPrivate()); - sendToInbox(john.getUserID(), SHARED_FILE_PATH, privateContentJane); + sendToInbox(jane, john.getUserID(), SHARED_FILE_PATH, privateContentJane); AbsoluteLocation inboxJohn = getFirstFileInInbox(john); @@ -308,9 +315,9 @@ void listingInboxValidation(WithStorageProvider.StorageDescriptor descriptor) { registerJohnAndJane(); - writeDataToInbox(jane, "root.file", MESSAGE_ONE); - writeDataToInbox(jane, "level1/file", MESSAGE_ONE); - writeDataToInbox(jane, "level1/level2/file", MESSAGE_ONE); + writeDataToInbox(john, jane, "root.file", MESSAGE_ONE); + writeDataToInbox(john, jane, "level1/file", MESSAGE_ONE); + writeDataToInbox(john, jane, "level1/level2/file", MESSAGE_ONE); assertInboxSpaceList(jane, "", "root.file", "level1/file", "level1/level2/file"); assertInboxSpaceList(jane, "./", "root.file", "level1/file", "level1/level2/file"); diff --git a/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BasicFunctionalityUtf8Test.java b/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BasicFunctionalityUtf8Test.java index 7d9c233c1..089ed2362 100644 --- a/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BasicFunctionalityUtf8Test.java +++ b/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BasicFunctionalityUtf8Test.java @@ -86,11 +86,12 @@ void listingPrivatePathWithUnicode(WithStorageProvider.StorageDescriptor descrip void readInboxContentWithUnicodeUsingUnicodePath(WithStorageProvider.StorageDescriptor descriptor) { init(descriptor); + john = registerUser("john"); jane = registerUser("jane"); String unicodeMessage = "привет мир!"; - writeDataToInbox(jane, " привет/prüfungsdokument=/файл:&? с пробелом.док", unicodeMessage); + writeDataToInbox(john, jane, " привет/prüfungsdokument=/файл:&? с пробелом.док", unicodeMessage); String inboxContentJane = readInboxUsingPrivateKey( jane, @@ -107,9 +108,9 @@ void listingInboxPathWithUnicode(WithStorageProvider.StorageDescriptor descripto registerJohnAndJane(); - writeDataToInbox(jane, "prüfungsdokument.doc+doc", MESSAGE_ONE); - writeDataToInbox(jane, "уровень1/?файл+doc", MESSAGE_ONE); - writeDataToInbox(jane, "уровень1/уровень 2=+/&файл пробел+плюс", MESSAGE_ONE); + writeDataToInbox(john, jane, "prüfungsdokument.doc+doc", MESSAGE_ONE); + writeDataToInbox(john, jane, "уровень1/?файл+doc", MESSAGE_ONE); + writeDataToInbox(john, jane, "уровень1/уровень 2=+/&файл пробел+плюс", MESSAGE_ONE); assertInboxSpaceList(jane, "", "prüfungsdokument.doc+doc", "уровень1/?файл+doc", "уровень1/уровень 2=+/&файл пробел+плюс"); assertInboxSpaceList(jane, "./", "prüfungsdokument.doc+doc", "уровень1/?файл+doc", "уровень1/уровень 2=+/&файл пробел+плюс"); diff --git a/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BasicFunctionalityWithPasswordChangeTest.java b/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BasicFunctionalityWithPasswordChangeTest.java index e970bcd3c..9bb06c6ed 100644 --- a/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BasicFunctionalityWithPasswordChangeTest.java +++ b/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/BasicFunctionalityWithPasswordChangeTest.java @@ -5,7 +5,7 @@ import de.adorsys.datasafe.encrypiton.api.types.UserIDAuth; import de.adorsys.datasafe.teststorage.WithStorageProvider; import de.adorsys.datasafe.types.api.actions.ReadRequest; -import de.adorsys.datasafe.types.api.actions.WriteRequest; +import de.adorsys.datasafe.types.api.actions.WriteInboxRequest; import de.adorsys.datasafe.types.api.resource.AbsoluteLocation; import de.adorsys.datasafe.types.api.resource.ResolvedResource; import de.adorsys.datasafe.types.api.resource.Uri; @@ -60,12 +60,14 @@ void testUserIsRemovedWithFiles(WithStorageProvider.StorageDescriptor descriptor void testMultipleRecipientsSharing(WithStorageProvider.StorageDescriptor descriptor) { init(descriptor); + UserIDAuth sender = registerUser("sender"); + UserIDAuth john = registerUser("john"); UserIDAuth jane = registerUser("jane"); UserIDAuth jamie = registerUser("jamie"); String multiShareFile = "multishare.txt"; - multishareFiles(john, jane, jamie, multiShareFile); + multishareFiles(sender, john, jane, jamie, multiShareFile); Stream.of(john, jane, jamie).forEach( it -> checkUpdatedCredsWorkAndOldDont( @@ -102,7 +104,7 @@ void testWriteToPrivateListPrivateReadPrivateAndSendToAndReadFromInbox( String privateContentJane = readPrivateUsingPrivateKey(jane, privateJane.getResource().asPrivate()); - sendToInbox(john.getUserID(), SHARED_FILE_PATH, privateContentJane); + sendToInbox(jane, john.getUserID(), SHARED_FILE_PATH, privateContentJane); AbsoluteLocation inboxJohn = getFirstFileInInbox(john); @@ -188,8 +190,9 @@ void listingValidation(WithStorageProvider.StorageDescriptor descriptor) { } @SneakyThrows - private void multishareFiles(UserIDAuth userOne, UserIDAuth userTwo, UserIDAuth userThree, String multiShareFile) { - try (OutputStream os = writeToInbox.write(WriteRequest.forDefaultPublic( + private void multishareFiles(UserIDAuth sender, UserIDAuth userOne, UserIDAuth userTwo, UserIDAuth userThree, String multiShareFile) { + try (OutputStream os = writeToInbox.write(WriteInboxRequest.forDefaultPublic( + sender, ImmutableSet.of(userOne.getUserID(), userTwo.getUserID(), userThree.getUserID()), multiShareFile)) ) { diff --git a/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/DataTamperingResistanceTest.java b/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/DataTamperingResistanceTest.java index 9fcedfe97..e7b51ac5a 100644 --- a/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/DataTamperingResistanceTest.java +++ b/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/DataTamperingResistanceTest.java @@ -3,6 +3,7 @@ import de.adorsys.datasafe.business.impl.service.DefaultDatasafeServices; import de.adorsys.datasafe.types.api.actions.ListRequest; import de.adorsys.datasafe.types.api.actions.ReadRequest; +import de.adorsys.datasafe.types.api.actions.WriteInboxRequest; import de.adorsys.datasafe.types.api.actions.WriteRequest; import de.adorsys.datasafe.types.api.resource.AbsoluteLocation; import de.adorsys.datasafe.types.api.resource.ResolvedResource; @@ -82,7 +83,7 @@ void testPrivateDocumentContentTamperResistance() { @SneakyThrows void testInboxDocumentContentTamperResistance() { try (OutputStream os = writeToInbox.write( - WriteRequest.forDefaultPublic(Collections.singleton(john.getUserID()), FILENAME)) + WriteInboxRequest.forDefaultPublic(jane, Collections.singleton(john.getUserID()), FILENAME)) ) { os.write(FILE_TEXT.getBytes(StandardCharsets.UTF_8)); } diff --git a/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/commands/inbox/Share.java b/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/commands/inbox/Share.java index 244bdf4d0..e64dc360e 100644 --- a/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/commands/inbox/Share.java +++ b/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/commands/inbox/Share.java @@ -3,7 +3,7 @@ import com.google.common.io.ByteStreams; import com.google.common.io.MoreFiles; import de.adorsys.datasafe.encrypiton.api.types.UserID; -import de.adorsys.datasafe.types.api.actions.WriteRequest; +import de.adorsys.datasafe.types.api.actions.WriteInboxRequest; import lombok.SneakyThrows; import picocli.CommandLine; @@ -41,7 +41,8 @@ public class Share implements Runnable { public void run() { try (OutputStream os = inbox.getCli().datasafe().inboxService() .write( - WriteRequest.forDefaultPublic( + WriteInboxRequest.forDefaultPublic( + inbox.getCli().auth(), recipients.stream().map(UserID::new).collect(Collectors.toSet()), filename ) diff --git a/datasafe-directory/datasafe-directory-api/src/main/java/de/adorsys/datasafe/directory/api/profile/keys/DocumentKeyStoreOperations.java b/datasafe-directory/datasafe-directory-api/src/main/java/de/adorsys/datasafe/directory/api/profile/keys/DocumentKeyStoreOperations.java index c7fa71171..3b3f7d11c 100644 --- a/datasafe-directory/datasafe-directory-api/src/main/java/de/adorsys/datasafe/directory/api/profile/keys/DocumentKeyStoreOperations.java +++ b/datasafe-directory/datasafe-directory-api/src/main/java/de/adorsys/datasafe/directory/api/profile/keys/DocumentKeyStoreOperations.java @@ -5,6 +5,7 @@ import de.adorsys.datasafe.types.api.types.ReadKeyPassword; import java.security.Key; +import java.security.KeyPair; import java.util.List; import java.util.Set; @@ -42,4 +43,6 @@ public interface DocumentKeyStoreOperations { * @return Key aliases from keystore. */ Set readAliases(UserIDAuth forUser); + + KeyPair getKeyPair(UserIDAuth forUser); } diff --git a/datasafe-directory/datasafe-directory-api/src/main/java/de/adorsys/datasafe/directory/api/profile/keys/PrivateKeyService.java b/datasafe-directory/datasafe-directory-api/src/main/java/de/adorsys/datasafe/directory/api/profile/keys/PrivateKeyService.java index 2acdc65de..7111415b0 100644 --- a/datasafe-directory/datasafe-directory-api/src/main/java/de/adorsys/datasafe/directory/api/profile/keys/PrivateKeyService.java +++ b/datasafe-directory/datasafe-directory-api/src/main/java/de/adorsys/datasafe/directory/api/profile/keys/PrivateKeyService.java @@ -5,6 +5,7 @@ import de.adorsys.datasafe.encrypiton.api.types.keystore.SecretKeyIDWithKey; import java.security.Key; +import java.security.KeyPair; import java.util.Map; import java.util.Set; @@ -45,4 +46,6 @@ public interface PrivateKeyService { * {@code keyIds} that are missing - they are silently ignored and not returned in result. */ Map keysByIds(UserIDAuth forUser, Set keyIds); + + KeyPair getKeyPair(UserIDAuth forUser); } diff --git a/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/keys/DFSPrivateKeyServiceImpl.java b/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/keys/DFSPrivateKeyServiceImpl.java index 94eb20ce5..9df1a2b23 100644 --- a/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/keys/DFSPrivateKeyServiceImpl.java +++ b/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/keys/DFSPrivateKeyServiceImpl.java @@ -14,6 +14,7 @@ import javax.crypto.SecretKey; import javax.inject.Inject; import java.security.Key; +import java.security.KeyPair; import java.security.KeyStoreException; import java.security.UnrecoverableKeyException; import java.util.Collection; @@ -100,6 +101,11 @@ public Map keysByIds(UserIDAuth forUser, Set keyIds) { ); } + @Override + public KeyPair getKeyPair(UserIDAuth forUser) { + return keyStoreOper.getKeyPair(forUser); + } + protected SecretKeyIDWithKey keyByPrefix(UserIDAuth forUser, String prefix) { return keyByPrefix( forUser, diff --git a/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/keys/DocumentKeyStoreOperationsImpl.java b/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/keys/DocumentKeyStoreOperationsImpl.java index 51f968c23..961399634 100644 --- a/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/keys/DocumentKeyStoreOperationsImpl.java +++ b/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/keys/DocumentKeyStoreOperationsImpl.java @@ -25,7 +25,9 @@ import javax.inject.Inject; import java.io.OutputStream; import java.security.Key; +import java.security.KeyPair; import java.security.KeyStore; +import java.security.PrivateKey; import java.util.List; import java.util.Set; @@ -103,6 +105,16 @@ public void updateReadKeyPassword(UserIDAuth forUser, ReadKeyPassword newPasswor genericOper.updateReadKeyPassword(keyStore(forUser), location, forUser, newPassword); } + @Override + @SneakyThrows + public KeyPair getKeyPair(UserIDAuth forUser) { + KeyStore keyStore = keyStore(forUser); + KeyStoreAuth auth = keystoreAuth(forUser, forUser.getReadKeyPassword()); + List publicKeys = keyStoreService.getPublicKeys(new KeyStoreAccess(keyStore, auth)); + Key key = genericOper.getKey(() -> keyStore(forUser), forUser, publicKeys.get(0).getKeyID().getValue()); + return new KeyPair(publicKeys.get(0).getPublicKey(), (PrivateKey) key); + } + private AbsoluteLocation keystoreLocationWithAccess(UserIDAuth forUser) { return this.access.privateAccessFor( forUser, diff --git a/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/cmsencryption/CMSEncryptionService.java b/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/cmsencryption/CMSEncryptionService.java index 6c051bd26..96e7966b8 100644 --- a/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/cmsencryption/CMSEncryptionService.java +++ b/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/cmsencryption/CMSEncryptionService.java @@ -7,6 +7,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.security.Key; +import java.security.KeyPair; import java.util.Map; import java.util.Set; import java.util.function.Function; @@ -25,7 +26,8 @@ public interface CMSEncryptionService { * @return Encrypted stream that wraps {@code dataContentStream} * @apiNote Closes underlying stream when result is closed */ - OutputStream buildEncryptionOutputStream(OutputStream dataContentStream, Set publicKeys); + OutputStream buildEncryptionOutputStream(OutputStream dataContentStream, Set publicKeys, + KeyPair senderKeyPair); /** * Builds symmetrically encrypted stream. diff --git a/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/document/EncryptedDocumentWriteService.java b/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/document/EncryptedDocumentWriteService.java index 257dd77e9..70830ea8a 100644 --- a/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/document/EncryptedDocumentWriteService.java +++ b/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/document/EncryptedDocumentWriteService.java @@ -8,6 +8,7 @@ import de.adorsys.datasafe.types.api.resource.WithCallback; import java.io.OutputStream; +import java.security.KeyPair; import java.util.Map; /** @@ -22,7 +23,7 @@ public interface EncryptedDocumentWriteService { * @param recipientsWithInbox Map of (recipient public key - recipients' inbox) of users with whom to share file. * @return Sink where you can send unencrypted data that will be encrypted and stored */ - OutputStream write(Map recipientsWithInbox); + OutputStream write(Map recipientsWithInbox, KeyPair senderKeyPair); /** * Writes and encrypts data using symmetric cryptography. diff --git a/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/types/encryption/KeyCreationConfig.java b/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/types/encryption/KeyCreationConfig.java index 7d20aaaa2..97955e4c1 100644 --- a/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/types/encryption/KeyCreationConfig.java +++ b/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/types/encryption/KeyCreationConfig.java @@ -54,7 +54,7 @@ public static class EncryptingKeyCreationCfg { private final String sigAlgo = "SHA256withECDSA"; @Builder.Default - private final String customNamedCurve = "Curve25519"; + private final String curve = "Curve25519"; } @Getter @@ -71,6 +71,6 @@ public static class SigningKeyCreationCfg { private final String sigAlgo = "SHA256withECDSA"; @Builder.Default - private final String customNamedCurve = "Curve25519"; + private final String curve = "Curve25519"; } } diff --git a/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/types/encryption/MutableEncryptionConfig.java b/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/types/encryption/MutableEncryptionConfig.java index b2ffd8872..37f42ae24 100644 --- a/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/types/encryption/MutableEncryptionConfig.java +++ b/datasafe-encryption/datasafe-encryption-api/src/main/java/de/adorsys/datasafe/encrypiton/api/types/encryption/MutableEncryptionConfig.java @@ -183,6 +183,7 @@ public static class MutableEncryptingKeyCreationCfg { private String algo; private Integer size; private String sigAlgo; + private String curve; KeyCreationConfig.EncryptingKeyCreationCfg toEncryptingKeyCreationCfg() { KeyCreationConfig.EncryptingKeyCreationCfg.EncryptingKeyCreationCfgBuilder builder = @@ -199,6 +200,10 @@ KeyCreationConfig.EncryptingKeyCreationCfg toEncryptingKeyCreationCfg() { builder.sigAlgo(sigAlgo); } + if (null != curve) { + builder.curve(curve); + } + return builder.build(); } } @@ -209,6 +214,7 @@ public static class MutableSigningKeyCreationCfg { private String algo; private Integer size; private String sigAlgo; + private String curve; KeyCreationConfig.SigningKeyCreationCfg toSigningKeyCreationCfg() { KeyCreationConfig.SigningKeyCreationCfg.SigningKeyCreationCfgBuilder builder = @@ -225,6 +231,10 @@ KeyCreationConfig.SigningKeyCreationCfg toSigningKeyCreationCfg() { builder.sigAlgo(sigAlgo); } + if (null != curve) { + builder.curve(curve); + } + return builder.build(); } } diff --git a/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/CMSEncryptionServiceImpl.java b/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/CMSEncryptionServiceImpl.java index abca2737d..99d698d7d 100644 --- a/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/CMSEncryptionServiceImpl.java +++ b/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/CMSEncryptionServiceImpl.java @@ -11,6 +11,7 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.cms.CMSAlgorithm; import org.bouncycastle.cms.CMSEnvelopedDataParser; import org.bouncycastle.cms.CMSEnvelopedDataStreamGenerator; import org.bouncycastle.cms.CMSException; @@ -19,6 +20,7 @@ import org.bouncycastle.cms.RecipientInformationStore; import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder; import org.bouncycastle.cms.jcajce.JceKEKRecipientInfoGenerator; +import org.bouncycastle.cms.jcajce.JceKeyAgreeRecipientInfoGenerator; import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator; import javax.crypto.SecretKey; @@ -27,6 +29,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.security.Key; +import java.security.KeyPair; import java.util.Collections; import java.util.Map; import java.util.Set; @@ -57,18 +60,36 @@ public CMSEncryptionServiceImpl(ASNCmsEncryptionConfig encryptionConfig) { @Override @SneakyThrows public OutputStream buildEncryptionOutputStream(OutputStream dataContentStream, - Set publicKeys) { + Set publicKeys, + KeyPair senderKeyPair) { return streamEncrypt( dataContentStream, - publicKeys.stream().map( - it -> new JceKeyTransRecipientInfoGenerator( - it.getKeyID().getValue().getBytes(), it.getPublicKey() - ) - ).collect(Collectors.toSet()), + publicKeys.stream().map(it -> getRecipientInfoGenerator(it, senderKeyPair)).collect(Collectors.toSet()), encryptionConfig.getAlgorithm() ); } + private RecipientInfoGenerator getRecipientInfoGenerator(PublicKeyIDWithPublicKey keyWithId, KeyPair senderKeyPair) { + if ("RSA".equals(keyWithId.getPublicKey().getAlgorithm())) { + return new JceKeyTransRecipientInfoGenerator(keyWithId.getKeyID().getValue().getBytes(), keyWithId.getPublicKey()); + } + if (Set.of("ECDH", "EC").contains(keyWithId.getPublicKey().getAlgorithm())) { + return getJceKeyAgreeRecipientInfoGenerator(senderKeyPair, keyWithId); + } + return null; + } + + @SneakyThrows + public JceKeyAgreeRecipientInfoGenerator getJceKeyAgreeRecipientInfoGenerator(KeyPair senderKeyPair, PublicKeyIDWithPublicKey publicKeyWithId) { + var jceKeyAgreeRecipientInfoGenerator = new JceKeyAgreeRecipientInfoGenerator( + CMSAlgorithm.ECDH_SHA1KDF, + senderKeyPair.getPrivate(), + senderKeyPair.getPublic(), + CMSAlgorithm.AES128_WRAP); + jceKeyAgreeRecipientInfoGenerator.addRecipient(publicKeyWithId.getKeyID().getValue().getBytes(), publicKeyWithId.getPublicKey()); + return jceKeyAgreeRecipientInfoGenerator; + } + /** * Symmetrical encryption-based stream, algorithm is provided by {@link ASNCmsEncryptionConfig#getAlgorithm()} * Uses {@link RecipientId#kek} recipient id. diff --git a/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/decryptors/DecryptorFactory.java b/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/decryptors/DecryptorFactory.java index 660df2d6c..404d18025 100644 --- a/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/decryptors/DecryptorFactory.java +++ b/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/decryptors/DecryptorFactory.java @@ -1,6 +1,5 @@ package de.adorsys.datasafe.encrypiton.impl.cmsencryption.decryptors; -import com.google.common.collect.ImmutableMap; import de.adorsys.datasafe.encrypiton.impl.cmsencryption.exceptions.DecryptionException; import lombok.experimental.UtilityClass; import org.bouncycastle.cms.RecipientInformation; @@ -15,13 +14,15 @@ public class DecryptorFactory { private static final Map> DECRYPTORS = - ImmutableMap.of( + Map.of( KekDecryptor.KEY_ID, KekDecryptor::new, - KeyTransDecryptor.KEY_ID, KeyTransDecryptor::new + KeyTransDecryptor.KEY_ID, KeyTransDecryptor::new, + KeyAgreeDecryptor.KEY_ID, KeyAgreeDecryptor::new ); /** * Get decryptor instance to decrypt data on {@code recipient} + * * @param information Container with encrypted data * @return Decryptor that, when supplied by proper key can decrypt data in {@code recipient} */ diff --git a/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/decryptors/KeyAgreeDecryptor.java b/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/decryptors/KeyAgreeDecryptor.java new file mode 100644 index 000000000..61ed4a32b --- /dev/null +++ b/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/decryptors/KeyAgreeDecryptor.java @@ -0,0 +1,32 @@ +package de.adorsys.datasafe.encrypiton.impl.cmsencryption.decryptors; + +import lombok.SneakyThrows; +import org.bouncycastle.cms.KeyAgreeRecipientId; +import org.bouncycastle.cms.RecipientId; +import org.bouncycastle.cms.RecipientInformation; +import org.bouncycastle.cms.jcajce.JceKeyAgreeEnvelopedRecipient; + +import java.io.InputStream; +import java.security.Key; +import java.security.PrivateKey; + +public class KeyAgreeDecryptor extends Decryptor { + + static final int KEY_ID = RecipientId.keyAgree; + + KeyAgreeDecryptor(RecipientInformation recipientInformation) { + super( + new String(((KeyAgreeRecipientId) recipientInformation.getRID()).getSubjectKeyIdentifier()), + KEY_ID, + recipientInformation + ); + } + + @Override + @SneakyThrows + public InputStream decryptionStream(Key key) { + return recipientInfo + .getContentStream(new JceKeyAgreeEnvelopedRecipient((PrivateKey) key)) + .getContentStream(); + } +} \ No newline at end of file diff --git a/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/document/CMSDocumentWriteService.java b/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/document/CMSDocumentWriteService.java index ce592ddd7..96bf8d22b 100644 --- a/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/document/CMSDocumentWriteService.java +++ b/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/document/CMSDocumentWriteService.java @@ -18,6 +18,7 @@ import javax.inject.Inject; import java.io.IOException; import java.io.OutputStream; +import java.security.KeyPair; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -41,7 +42,7 @@ public CMSDocumentWriteService(StorageWriteService writeService, } @Override - public OutputStream write(Map recipientsWithInbox) { + public OutputStream write(Map recipientsWithInbox, KeyPair senderKeyPair) { int maxChunkSize = recipientsWithInbox.values().stream() .map(writeService::flushChunkSize) @@ -59,7 +60,8 @@ public OutputStream write(Map recipi OutputStream encryptionSink = cms.buildEncryptionOutputStream( dfsSink, - recipientsWithInbox.keySet() + recipientsWithInbox.keySet(), + senderKeyPair ); return new CloseCoordinatingStream(encryptionSink, ImmutableList.of(encryptionSink, dfsSink)); diff --git a/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/keystore/KeyStoreServiceImpl.java b/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/keystore/KeyStoreServiceImpl.java index 622a624d9..6044df2ae 100644 --- a/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/keystore/KeyStoreServiceImpl.java +++ b/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/keystore/KeyStoreServiceImpl.java @@ -1,5 +1,6 @@ package de.adorsys.datasafe.encrypiton.impl.keystore; +import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import de.adorsys.datasafe.encrypiton.api.keystore.KeyStoreService; import de.adorsys.datasafe.encrypiton.api.types.encryption.KeyCreationConfig; @@ -30,6 +31,7 @@ import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.security.spec.ECParameterSpec; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; @@ -84,6 +86,8 @@ public KeyStore createKeyStore(KeyStoreAuth keyStoreAuth, EncryptingKeyCreationCfg encConf = keyConfig.getEncrypting(); Supplier passSupplier = () -> keyStoreAuth.getReadKeyPassword().getValue(); + ECParameterSpec paramSpec = Strings.isNullOrEmpty(encConf.getCurve()) ? + null : EC5Util.convertToSpec(CustomNamedCurves.getByName(encConf.getCurve())); KeySetTemplate template = KeySetTemplate.builder() .generatedEncryptionKeys(Encrypting.with() .algo(encConf.getAlgo()) @@ -91,7 +95,7 @@ public KeyStore createKeyStore(KeyStoreAuth keyStoreAuth, .keySize(encConf.getSize()) .prefix("ENC") .password(passSupplier) - .paramSpec(EC5Util.convertToSpec(CustomNamedCurves.getByName(encConf.getCustomNamedCurve()))) + .paramSpec(paramSpec) .build() .repeat(keyConfig.getEncKeyNumber()) ) diff --git a/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/keystore/PublicKeySerdeImpl.java b/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/keystore/PublicKeySerdeImpl.java index c64e3fe9f..802fdf9b4 100644 --- a/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/keystore/PublicKeySerdeImpl.java +++ b/datasafe-encryption/datasafe-encryption-impl/src/main/java/de/adorsys/datasafe/encrypiton/impl/keystore/PublicKeySerdeImpl.java @@ -1,10 +1,13 @@ package de.adorsys.datasafe.encrypiton.impl.keystore; import de.adorsys.datasafe.encrypiton.api.keystore.PublicKeySerde; +import de.adorsys.datasafe.encrypiton.impl.cmsencryption.exceptions.DecryptionException; import de.adorsys.datasafe.types.api.context.annotations.RuntimeDelegate; import lombok.SneakyThrows; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi; +import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import javax.inject.Inject; import java.security.PublicKey; @@ -16,6 +19,8 @@ @RuntimeDelegate public class PublicKeySerdeImpl implements PublicKeySerde { + private static final ASN1ObjectIdentifier RSA = PKCSObjectIdentifiers.rsaEncryption; + private static final ASN1ObjectIdentifier EC = X9ObjectIdentifiers.id_ecPublicKey; @Inject public PublicKeySerdeImpl() { @@ -26,7 +31,13 @@ public PublicKeySerdeImpl() { public PublicKey readPubKey(String encoded) { // FIXME: Legacy stuff byte[] bytes = Base64.getDecoder().decode(encoded); - return new KeyFactorySpi().generatePublic(SubjectPublicKeyInfo.getInstance(bytes)); + SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(bytes); + if (RSA.equals(subjectPublicKeyInfo.getAlgorithm())) { + return new org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi().generatePublic(SubjectPublicKeyInfo.getInstance(bytes)); + } else if (EC.equals(subjectPublicKeyInfo.getAlgorithm())) { + return new org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi.ECDH().generatePublic(subjectPublicKeyInfo); + } + throw new DecryptionException("PublicKeySerdeImpl.UnsupportedEncodedKey"); } @Override diff --git a/datasafe-encryption/datasafe-encryption-impl/src/test/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/CmsEncryptionServiceImplTest.java b/datasafe-encryption/datasafe-encryption-impl/src/test/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/CmsEncryptionServiceImplTest.java index 0857d8ae7..3c0168b08 100644 --- a/datasafe-encryption/datasafe-encryption-impl/src/test/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/CmsEncryptionServiceImplTest.java +++ b/datasafe-encryption/datasafe-encryption-impl/src/test/java/de/adorsys/datasafe/encrypiton/impl/cmsencryption/CmsEncryptionServiceImplTest.java @@ -21,9 +21,8 @@ import org.assertj.core.util.Sets; import org.bouncycastle.cms.CMSAlgorithm; import org.bouncycastle.cms.CMSEnvelopedDataStreamGenerator; -import org.bouncycastle.cms.RecipientInfoGenerator; import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder; -import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator; +import org.bouncycastle.cms.jcajce.JceKeyAgreeRecipientInfoGenerator; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.encoders.Hex; import org.junit.jupiter.api.BeforeAll; @@ -42,10 +41,14 @@ import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Paths; +import java.security.Key; +import java.security.KeyPair; import java.security.KeyStore; import java.security.MessageDigest; +import java.security.PrivateKey; import java.util.Arrays; import java.util.Collections; +import java.util.List; import static com.google.common.io.ByteStreams.toByteArray; import static de.adorsys.datasafe.encrypiton.impl.cmsencryption.KeyStoreUtil.getKeys; @@ -83,9 +86,10 @@ void testCmsStreamEnvelopeEncryptAndDecryptTestWithMultipleRecipients() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - OutputStream encryptionStream = cmsEncryptionService.buildEncryptionOutputStream(outputStream, Sets.newHashSet( - Arrays.asList(getPublicKeyIDWithPublicKey(keyStoreAccess1), getPublicKeyIDWithPublicKey(keyStoreAccess2)) - )); + OutputStream encryptionStream = cmsEncryptionService.buildEncryptionOutputStream(outputStream, + Sets.newHashSet(Arrays.asList(getPublicKeyIDWithPublicKey(keyStoreAccess1), getPublicKeyIDWithPublicKey(keyStoreAccess2))), + getKeyPair(keyStoreAccess3, "Suzanne") + ); encryptionStream.write(TEST_MESSAGE_CONTENT.getBytes()); encryptionStream.close(); @@ -117,11 +121,15 @@ void cmsStreamEnvelopeEncryptAndDecryptTest() { PublicKeyIDWithPublicKey publicKeyIDWithPublicKey = keyStoreService.getPublicKeys(keyStoreAccess).get(0); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - OutputStream encryptionStream = cmsEncryptionService.buildEncryptionOutputStream(outputStream, + KeyStoreAccess keyStoreAccessSender = getKeyStoreAccess("Sender"); + + OutputStream encryptionStream = cmsEncryptionService.buildEncryptionOutputStream( + outputStream, Collections.singleton(new PublicKeyIDWithPublicKey( publicKeyIDWithPublicKey.getKeyID(), publicKeyIDWithPublicKey.getPublicKey() - )) + )), + getKeyPair(keyStoreAccessSender, "Sender") ); encryptionStream.write(TEST_MESSAGE_CONTENT.getBytes()); @@ -148,11 +156,14 @@ void cmsStreamEnvelopeZeroKeyPairFailTest() { gen.open(outputStream, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC) .setProvider(BouncyCastleProvider.PROVIDER_NAME).build()); + KeyStoreAccess keyStoreAccessSender = getKeyStoreAccess("Sender"); + OutputStream encryptionStream = cmsEncryptionService.buildEncryptionOutputStream(outputStream, Collections.singleton(new PublicKeyIDWithPublicKey( publicKeyIDWithPublicKey.getKeyID(), publicKeyIDWithPublicKey.getPublicKey() - )) + )), + getKeyPair(keyStoreAccessSender, "Sender") ); encryptionStream.write(TEST_MESSAGE_CONTENT.getBytes()); @@ -170,12 +181,19 @@ void cmsStreamEnvelopeTwoKeysPairFailTest() { PublicKeyIDWithPublicKey publicKeyIDWithPublicKey = keyStoreService.getPublicKeys(keyStoreAccess).get(0); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - RecipientInfoGenerator recipientInfo1 = new JceKeyTransRecipientInfoGenerator("key1".getBytes(), publicKeyIDWithPublicKey.getPublicKey()); - RecipientInfoGenerator recipientInfo2 = new JceKeyTransRecipientInfoGenerator("key2".getBytes(), publicKeyIDWithPublicKey.getPublicKey()); + KeyStoreAccess keyStoreAccessSender = getKeyStoreAccess("Sender"); + KeyPair senderKeyPair = getKeyPair(keyStoreAccessSender, "Sender"); + + JceKeyAgreeRecipientInfoGenerator keyAgreeRecipientInfoGenerator = new JceKeyAgreeRecipientInfoGenerator( + CMSAlgorithm.ECDH_SHA1KDF, + senderKeyPair.getPrivate(), + senderKeyPair.getPublic(), + CMSAlgorithm.AES128_WRAP); + keyAgreeRecipientInfoGenerator.addRecipient("key1".getBytes(), publicKeyIDWithPublicKey.getPublicKey()); + keyAgreeRecipientInfoGenerator.addRecipient("key2".getBytes(), publicKeyIDWithPublicKey.getPublicKey()); CMSEnvelopedDataStreamGenerator gen = new CMSEnvelopedDataStreamGenerator(); - gen.addRecipientInfoGenerator(recipientInfo1); - gen.addRecipientInfoGenerator(recipientInfo2); + gen.addRecipientInfoGenerator(keyAgreeRecipientInfoGenerator); gen.open(outputStream, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC) .setProvider(BouncyCastleProvider.PROVIDER_NAME).build()); @@ -183,7 +201,8 @@ void cmsStreamEnvelopeTwoKeysPairFailTest() { Collections.singleton(new PublicKeyIDWithPublicKey( publicKeyIDWithPublicKey.getKeyID(), publicKeyIDWithPublicKey.getPublicKey() - )) + )), + senderKeyPair ); encryptionStream.write(TEST_MESSAGE_CONTENT.getBytes()); @@ -202,11 +221,15 @@ void cmsStreamEnvelopeOneKeyPairFailTest() { PublicKeyIDWithPublicKey publicKeyIDWithPublicKey = keyStoreService.getPublicKeys(keyStoreAccess).get(0); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + KeyStoreAccess keyStoreAccessSender = getKeyStoreAccess("Sender"); + OutputStream encryptionStream = cmsEncryptionService.buildEncryptionOutputStream(outputStream, Collections.singleton(new PublicKeyIDWithPublicKey( publicKeyIDWithPublicKey.getKeyID(), publicKeyIDWithPublicKey.getPublicKey() - )) + )), + getKeyPair(keyStoreAccessSender, "Sender") ); encryptionStream.write(TEST_MESSAGE_CONTENT.getBytes()); @@ -230,6 +253,15 @@ private static KeyStoreAccess getKeyStoreAccess(String label) { return new KeyStoreAccess(keyStore, keyStoreAuth); } + @SneakyThrows + private static KeyPair getKeyPair(KeyStoreAccess keyStoreAccess, String label) { + KeyStore keyStore = keyStoreAccess.getKeyStore(); + ReadKeyPassword readKeyPassword = ReadKeyPasswordTestFactory.getForString(label); + List publicKeys = keyStoreService.getPublicKeys(keyStoreAccess); + Key key = keyStore.getKey(publicKeys.get(0).getKeyID().getValue(), readKeyPassword.getValue()); + return new KeyPair(publicKeys.get(0).getPublicKey(), (PrivateKey) key); + } + private static KeyStoreAccess getKeyStoreAccess() { return getKeyStoreAccess("readkeypassword"); } @@ -262,12 +294,15 @@ void cmsEnvelopeEncryptAndDecryptFileStreamTest() { File encryptedFile = new File(encryptedFilePath); FileOutputStream fosEnFile = new FileOutputStream(encryptedFile); + KeyStoreAccess keyStoreAccessSender = getKeyStoreAccess("Sender"); + OutputStream encryptionStream = cmsEncryptionService.buildEncryptionOutputStream( fosEnFile, Collections.singleton(new PublicKeyIDWithPublicKey( publicKeyIDWithPublicKey.getKeyID(), publicKeyIDWithPublicKey.getPublicKey() - )) + )), + getKeyPair(keyStoreAccessSender, "Sender") ); Files.copy(Paths.get(testFilePath), encryptionStream); diff --git a/datasafe-examples/datasafe-examples-business/src/test/java/de/adorsys/datasafe/examples/business/filesystem/BaseUserOperationsTestWithDefaultDatasafeTest.java b/datasafe-examples/datasafe-examples-business/src/test/java/de/adorsys/datasafe/examples/business/filesystem/BaseUserOperationsTestWithDefaultDatasafeTest.java index 177f6ed2d..76273efd5 100644 --- a/datasafe-examples/datasafe-examples-business/src/test/java/de/adorsys/datasafe/examples/business/filesystem/BaseUserOperationsTestWithDefaultDatasafeTest.java +++ b/datasafe-examples/datasafe-examples-business/src/test/java/de/adorsys/datasafe/examples/business/filesystem/BaseUserOperationsTestWithDefaultDatasafeTest.java @@ -10,6 +10,7 @@ import de.adorsys.datasafe.storage.impl.fs.FileSystemStorageService; import de.adorsys.datasafe.types.api.actions.ListRequest; import de.adorsys.datasafe.types.api.actions.ReadRequest; +import de.adorsys.datasafe.types.api.actions.WriteInboxRequest; import de.adorsys.datasafe.types.api.actions.WriteRequest; import de.adorsys.datasafe.types.api.resource.AbsoluteLocation; import de.adorsys.datasafe.types.api.resource.ResolvedResource; @@ -121,13 +122,16 @@ void readFileFromPrivateSpace() { @SneakyThrows void shareWithJane() { // BEGIN_SNIPPET:Send file to INBOX + // create sender user + UserIDAuth sender = registerUser("sender"); + // create Jane, so her INBOX does exist UserIDAuth jane = registerUser("jane"); UserID janeUsername = new UserID("jane"); // We send message "Hello John" to John just by his username try (OutputStream os = defaultDatasafeServices.inboxService() - .write(WriteRequest.forDefaultPublic(Collections.singleton(janeUsername), "hello.txt"))) { + .write(WriteInboxRequest.forDefaultPublic(sender, Collections.singleton(janeUsername), "hello.txt"))) { os.write("Hello Jane".getBytes(StandardCharsets.UTF_8)); } // END_SNIPPET @@ -143,6 +147,8 @@ void shareWithJane() { @SneakyThrows void shareWithJaneAndJamie() { // BEGIN_SNIPPET:Send file to INBOX - multiple users + // create Jack, who wants to send a file to Jane and Jamie + UserIDAuth jack = registerUser("jack"); // create Jane, so her INBOX does exist UserIDAuth jane = registerUser("jane"); // create Jamie, so his INBOX does exist @@ -150,7 +156,7 @@ void shareWithJaneAndJamie() { // We send message to both users by using their username: try (OutputStream os = defaultDatasafeServices.inboxService().write( - WriteRequest.forDefaultPublic(ImmutableSet.of(jane.getUserID(), jamie.getUserID()), "hello.txt")) + WriteInboxRequest.forDefaultPublic(jack, ImmutableSet.of(jane.getUserID(), jamie.getUserID()), "hello.txt")) ) { os.write("Hello Jane and Jamie".getBytes(StandardCharsets.UTF_8)); } @@ -206,13 +212,16 @@ void listPrivate() { @Test void listInbox() { // BEGIN_SNIPPET:List INBOX + // creating sender user + UserIDAuth sender = registerUser("sender"); + // creating new user UserIDAuth user = registerUser("john"); UserID johnUsername = new UserID("john"); // let's share 2 messages: - shareMessage(johnUsername, "home/my/secret.txt", "Hi there"); - shareMessage(johnUsername, "home/watch/films.txt", "Films you will like"); + shareMessage(sender, johnUsername, "home/my/secret.txt", "Hi there"); + shareMessage(sender, johnUsername, "home/watch/films.txt", "Films you will like"); // Here's how to list inbox folder root List> johnsInboxFilesInRoot = defaultDatasafeServices.inboxService() @@ -265,12 +274,15 @@ void readFromPrivate() { @Test void readFromInbox() { // BEGIN_SNIPPET:Read file from INBOX + // creating sender user + UserIDAuth sender = registerUser("sender"); + // creating new user UserIDAuth user = registerUser("john"); UserID johnUsername = new UserID("john"); // let's create 1 file: - shareMessage(johnUsername, "home/my/shared.txt", "shared message"); + shareMessage(sender, johnUsername, "home/my/shared.txt", "shared message"); // Lets list our INBOX List> johnsInboxFilesInMy = defaultDatasafeServices.inboxService() @@ -298,9 +310,9 @@ private UserIDAuth registerUser(String username) { } @SneakyThrows - private void shareMessage(UserID forUser, String messageName, String message) { + private void shareMessage(UserIDAuth fromUser, UserID forUser, String messageName, String message) { try (OutputStream os = defaultDatasafeServices.inboxService() - .write(WriteRequest.forDefaultPublic(Collections.singleton(forUser), messageName))) { + .write(WriteInboxRequest.forDefaultPublic(fromUser, Collections.singleton(forUser), messageName))) { os.write(message.getBytes(StandardCharsets.UTF_8)); } } diff --git a/datasafe-inbox/datasafe-inbox-api/src/main/java/de/adorsys/datasafe/inbox/api/actions/WriteToInbox.java b/datasafe-inbox/datasafe-inbox-api/src/main/java/de/adorsys/datasafe/inbox/api/actions/WriteToInbox.java index 5ed574c69..953a88557 100644 --- a/datasafe-inbox/datasafe-inbox-api/src/main/java/de/adorsys/datasafe/inbox/api/actions/WriteToInbox.java +++ b/datasafe-inbox/datasafe-inbox-api/src/main/java/de/adorsys/datasafe/inbox/api/actions/WriteToInbox.java @@ -1,7 +1,8 @@ package de.adorsys.datasafe.inbox.api.actions; import de.adorsys.datasafe.encrypiton.api.types.UserID; -import de.adorsys.datasafe.types.api.actions.WriteRequest; +import de.adorsys.datasafe.encrypiton.api.types.UserIDAuth; +import de.adorsys.datasafe.types.api.actions.WriteInboxRequest; import de.adorsys.datasafe.types.api.resource.PublicResource; import java.io.OutputStream; @@ -19,5 +20,5 @@ public interface WriteToInbox { * @return Stream that will get encrypted and stored within user INBOX. * @apiNote Returned stream should be closed properly */ - OutputStream write(WriteRequest, PublicResource> request); + OutputStream write(WriteInboxRequest, PublicResource> request); } diff --git a/datasafe-inbox/datasafe-inbox-impl/src/main/java/de/adorsys/datasafe/inbox/impl/actions/WriteToInboxImpl.java b/datasafe-inbox/datasafe-inbox-impl/src/main/java/de/adorsys/datasafe/inbox/impl/actions/WriteToInboxImpl.java index 3052de39f..87ad41a2b 100644 --- a/datasafe-inbox/datasafe-inbox-impl/src/main/java/de/adorsys/datasafe/inbox/impl/actions/WriteToInboxImpl.java +++ b/datasafe-inbox/datasafe-inbox-impl/src/main/java/de/adorsys/datasafe/inbox/impl/actions/WriteToInboxImpl.java @@ -1,12 +1,14 @@ package de.adorsys.datasafe.inbox.impl.actions; +import de.adorsys.datasafe.directory.api.profile.keys.PrivateKeyService; import de.adorsys.datasafe.directory.api.profile.keys.PublicKeyService; import de.adorsys.datasafe.directory.api.resource.ResourceResolver; import de.adorsys.datasafe.encrypiton.api.document.EncryptedDocumentReadService; import de.adorsys.datasafe.encrypiton.api.document.EncryptedDocumentWriteService; import de.adorsys.datasafe.encrypiton.api.types.UserID; +import de.adorsys.datasafe.encrypiton.api.types.UserIDAuth; import de.adorsys.datasafe.inbox.api.actions.WriteToInbox; -import de.adorsys.datasafe.types.api.actions.WriteRequest; +import de.adorsys.datasafe.types.api.actions.WriteInboxRequest; import de.adorsys.datasafe.types.api.context.annotations.RuntimeDelegate; import de.adorsys.datasafe.types.api.resource.PublicResource; @@ -24,13 +26,16 @@ public class WriteToInboxImpl implements WriteToInbox { private final PublicKeyService publicKeyService; + + private final PrivateKeyService privateKeyService; private final ResourceResolver resolver; private final EncryptedDocumentWriteService writer; @Inject - public WriteToInboxImpl(PublicKeyService publicKeyService, ResourceResolver resolver, + public WriteToInboxImpl(PublicKeyService publicKeyService, PrivateKeyService privateKeyService, ResourceResolver resolver, EncryptedDocumentWriteService writer) { this.publicKeyService = publicKeyService; + this.privateKeyService = privateKeyService; this.resolver = resolver; this.writer = writer; } @@ -41,13 +46,13 @@ public WriteToInboxImpl(PublicKeyService publicKeyService, ResourceResolver reso * @return Stream sink for data to be shared and encrypted */ @Override - public OutputStream write(WriteRequest, PublicResource> request) { - // No access check - anyone who knows user id can send a message to that user + public OutputStream write(WriteInboxRequest, PublicResource> request) { return writer.write( - request.getOwner().stream().collect(Collectors.toMap( + request.getRecipients().stream().collect(Collectors.toMap( publicKeyService::publicKey, it -> resolver.resolveRelativeToPublicInbox(it, request.getLocation()) - )) + )), + privateKeyService.getKeyPair(request.getOwner()) ); } } diff --git a/datasafe-inbox/datasafe-inbox-impl/src/test/java/de/adorsys/datasafe/inbox/api/actions/WriteToInboxImplTest.java b/datasafe-inbox/datasafe-inbox-impl/src/test/java/de/adorsys/datasafe/inbox/api/actions/WriteToInboxImplTest.java index d78f864a3..52d2f7395 100644 --- a/datasafe-inbox/datasafe-inbox-impl/src/test/java/de/adorsys/datasafe/inbox/api/actions/WriteToInboxImplTest.java +++ b/datasafe-inbox/datasafe-inbox-impl/src/test/java/de/adorsys/datasafe/inbox/api/actions/WriteToInboxImplTest.java @@ -1,13 +1,15 @@ package de.adorsys.datasafe.inbox.api.actions; +import de.adorsys.datasafe.directory.api.profile.keys.PrivateKeyService; import de.adorsys.datasafe.directory.api.profile.keys.PublicKeyService; import de.adorsys.datasafe.directory.api.resource.ResourceResolver; import de.adorsys.datasafe.encrypiton.api.document.EncryptedDocumentWriteService; import de.adorsys.datasafe.encrypiton.api.types.UserID; +import de.adorsys.datasafe.encrypiton.api.types.UserIDAuth; import de.adorsys.datasafe.encrypiton.api.types.keystore.KeyID; import de.adorsys.datasafe.encrypiton.api.types.keystore.PublicKeyIDWithPublicKey; import de.adorsys.datasafe.inbox.impl.actions.WriteToInboxImpl; -import de.adorsys.datasafe.types.api.actions.WriteRequest; +import de.adorsys.datasafe.types.api.actions.WriteInboxRequest; import de.adorsys.datasafe.types.api.resource.AbsoluteLocation; import de.adorsys.datasafe.types.api.resource.BasePublicResource; import de.adorsys.datasafe.types.api.resource.PublicResource; @@ -20,6 +22,7 @@ import java.io.ByteArrayOutputStream; import java.net.URI; +import java.security.KeyPair; import java.security.PublicKey; import java.util.Collections; import java.util.Set; @@ -33,15 +36,21 @@ class WriteToInboxImplTest extends BaseMockitoTest { private static final URI ABSOLUTE_PATH = URI.create("s3://absolute"); private UserID auth = new UserID(""); + private UserIDAuth ownerAuth = new UserIDAuth("owner", () -> "pass".toCharArray()); private PublicKeyIDWithPublicKey publicKeyWithId; + private KeyPair senderKeyPair; + @Mock private PublicKey publicKey; @Mock private PublicKeyService publicKeyService; + @Mock + private PrivateKeyService privateKeyService; + @Mock private ResourceResolver resolver; @@ -60,12 +69,13 @@ void init() { @SneakyThrows void write() { AbsoluteLocation resource = BasePublicResource.forAbsolutePublic(ABSOLUTE_PATH); - WriteRequest, PublicResource> request = WriteRequest - .forDefaultPublic(Collections.singleton(auth), ABSOLUTE_PATH); + WriteInboxRequest, PublicResource> request = WriteInboxRequest + .forDefaultPublic(ownerAuth, Collections.singleton(auth), ABSOLUTE_PATH); when(publicKeyService.publicKey(auth)).thenReturn(publicKeyWithId); + when(privateKeyService.getKeyPair(ownerAuth)).thenReturn(senderKeyPair); when(resolver.resolveRelativeToPublicInbox(auth, request.getLocation())).thenReturn(resource); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - when(writeService.write(Collections.singletonMap(publicKeyWithId, resource))).thenReturn(outputStream); + when(writeService.write(Collections.singletonMap(publicKeyWithId, resource), senderKeyPair)).thenReturn(outputStream); inbox.write(request).write(BYTES.getBytes()); diff --git a/datasafe-long-run-tests/datasafe-business-tests-random-actions/src/test/java/de/adorsys/datasafe/business/impl/e2e/randomactions/RandomActionsOnSimpleDatasafeAdapterTest.java b/datasafe-long-run-tests/datasafe-business-tests-random-actions/src/test/java/de/adorsys/datasafe/business/impl/e2e/randomactions/RandomActionsOnSimpleDatasafeAdapterTest.java index 3cc92af44..b1866bdc4 100644 --- a/datasafe-long-run-tests/datasafe-business-tests-random-actions/src/test/java/de/adorsys/datasafe/business/impl/e2e/randomactions/RandomActionsOnSimpleDatasafeAdapterTest.java +++ b/datasafe-long-run-tests/datasafe-business-tests-random-actions/src/test/java/de/adorsys/datasafe/business/impl/e2e/randomactions/RandomActionsOnSimpleDatasafeAdapterTest.java @@ -33,6 +33,7 @@ import de.adorsys.datasafe.types.api.actions.ListRequest; import de.adorsys.datasafe.types.api.actions.ReadRequest; import de.adorsys.datasafe.types.api.actions.RemoveRequest; +import de.adorsys.datasafe.types.api.actions.WriteInboxRequest; import de.adorsys.datasafe.types.api.actions.WriteRequest; import de.adorsys.datasafe.types.api.resource.AbsoluteLocation; import de.adorsys.datasafe.types.api.resource.BasePrivateResource; @@ -169,7 +170,7 @@ public void remove(RemoveRequest request) { } @Override - public OutputStream write(WriteRequest, PublicResource> request) { + public OutputStream write(WriteInboxRequest, PublicResource> request) { throw new IllegalStateException("Not implemented"); } }; diff --git a/datasafe-long-run-tests/datasafe-business-tests-random-actions/src/test/java/de/adorsys/datasafe/business/impl/e2e/randomactions/framework/services/OperationExecutor.java b/datasafe-long-run-tests/datasafe-business-tests-random-actions/src/test/java/de/adorsys/datasafe/business/impl/e2e/randomactions/framework/services/OperationExecutor.java index 2453d6b52..b3348670d 100644 --- a/datasafe-long-run-tests/datasafe-business-tests-random-actions/src/test/java/de/adorsys/datasafe/business/impl/e2e/randomactions/framework/services/OperationExecutor.java +++ b/datasafe-long-run-tests/datasafe-business-tests-random-actions/src/test/java/de/adorsys/datasafe/business/impl/e2e/randomactions/framework/services/OperationExecutor.java @@ -16,6 +16,7 @@ import de.adorsys.datasafe.types.api.actions.ListRequest; import de.adorsys.datasafe.types.api.actions.ReadRequest; import de.adorsys.datasafe.types.api.actions.RemoveRequest; +import de.adorsys.datasafe.types.api.actions.WriteInboxRequest; import de.adorsys.datasafe.types.api.actions.WriteRequest; import de.adorsys.datasafe.types.api.resource.AbsoluteLocation; import de.adorsys.datasafe.types.api.resource.PrivateResource; @@ -52,7 +53,7 @@ public class OperationExecutor { private final Map> handlers = ImmutableMap.>builder() .put(OperationType.CREATE_USER, this::doCreate) .put(OperationType.WRITE, this::doWrite) - .put(OperationType.SHARE, this::doWrite) + .put(OperationType.SHARE, this::doShare) .put(OperationType.READ, this::doRead) .put(OperationType.LIST, this::doList) .put(OperationType.DELETE, this::doDelete) @@ -153,6 +154,18 @@ private void doWrite(Operation oper) { } } + @SneakyThrows + private void doShare(Operation oper) { + UserSpec user = requireUser(oper); + + UserIDAuth auth = new UserIDAuth(oper.getUserId(), ReadKeyPasswordTestFactory.getForString(oper.getUserId())); + UserSpec sender = new UserSpec(auth, new ContentGenerator(fileContentSize)); + + try (OutputStream os = openShareStream(sender, user, oper)) { + ByteStreams.copy(user.getGenerator().generate(oper.getContentId().getId()), os); + } + } + @SneakyThrows private void doRead(Operation oper) { UserSpec user = requireUser(oper); @@ -204,16 +217,17 @@ private void doDelete(Operation oper) { privateSpace.remove(request); } - private OutputStream openWriteStream(UserSpec user, Operation oper) { - if (StorageType.INBOX.equals(oper.getStorageType())) { - return inboxService.write(WriteRequest.forDefaultPublic( - oper.getRecipients().stream() - .map(it -> requireUser(it).getAuth().getUserID()) - .collect(Collectors.toSet()), - oper.getLocation()) - ); - } + private OutputStream openShareStream(UserSpec sender, UserSpec user, Operation oper) { + return inboxService.write(WriteInboxRequest.forDefaultPublic( + user.getAuth(), + oper.getRecipients().stream() + .map(it -> requireUser(it).getAuth().getUserID()) + .collect(Collectors.toSet()), + oper.getLocation()) + ); + } + private OutputStream openWriteStream(UserSpec user, Operation oper) { return privateSpace.write(WriteRequest.forDefaultPrivate(user.getAuth(), oper.getLocation())); } diff --git a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/InboxController.java b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/InboxController.java index 2967e61d8..2809d383b 100644 --- a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/InboxController.java +++ b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/InboxController.java @@ -8,7 +8,7 @@ import de.adorsys.datasafe.types.api.actions.ListRequest; import de.adorsys.datasafe.types.api.actions.ReadRequest; import de.adorsys.datasafe.types.api.actions.RemoveRequest; -import de.adorsys.datasafe.types.api.actions.WriteRequest; +import de.adorsys.datasafe.types.api.actions.WriteInboxRequest; import de.adorsys.datasafe.types.api.resource.BasePrivateResource; import de.adorsys.datasafe.types.api.resource.PrivateResource; import jakarta.servlet.http.HttpServletResponse; @@ -52,11 +52,15 @@ public class InboxController { */ @SneakyThrows @PutMapping(value = "/inbox/document/{*path}", consumes = MULTIPART_FORM_DATA_VALUE) - public void writeToInbox(@RequestHeader Set users, + public void writeToInbox(@RequestHeader String user, + @RequestHeader String password, + @RequestHeader Set recipients, @PathVariable String path, @RequestParam("file") MultipartFile file) { - Set toUsers = users.stream().map(UserID::new).collect(Collectors.toSet()); - try (OutputStream os = dataSafeService.inboxService().write(WriteRequest.forDefaultPublic(toUsers, path)); + UserIDAuth fromUser = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); + Set toUsers = recipients.stream().map(UserID::new).collect(Collectors.toSet()); + path = path.substring(1); + try (OutputStream os = dataSafeService.inboxService().write(WriteInboxRequest.forDefaultPublic(fromUser, toUsers, path)); InputStream is = file.getInputStream()) { StreamUtils.copy(is, os); } @@ -73,6 +77,7 @@ public void readFromInbox(@RequestHeader String user, @PathVariable String path, HttpServletResponse response) { UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); + path = path.substring(1); PrivateResource resource = BasePrivateResource.forPrivate(path); // this is needed for swagger, produces is just a directive: response.addHeader(CONTENT_TYPE, APPLICATION_OCTET_STREAM_VALUE); diff --git a/datasafe-rest-impl/src/main/resources/application.properties b/datasafe-rest-impl/src/main/resources/application.properties index 549395490..5b4490b30 100644 --- a/datasafe-rest-impl/src/main/resources/application.properties +++ b/datasafe-rest-impl/src/main/resources/application.properties @@ -40,7 +40,9 @@ datasafe.encryption.cms.algo=AES256_GCM #datasafe.encryption.keys.encrypting.algo=RSA #datasafe.encryption.keys.encrypting.size=4096 #datasafe.encryption.keys.encrypting.sigAlgo=SHA256withRSA - +#datasafe.encryption.keys.encrypting.curve= +# #datasafe.encryption.keys.signing.algo=RSA #datasafe.encryption.keys.signing.size=4096 #datasafe.encryption.keys.signing.sigAlgo=SHA256withRSA +#datasafe.encryption.keys.signing.curve= diff --git a/datasafe-simple-adapter/datasafe-simple-adapter-impl/src/main/java/de/adorsys/datasafe/simple/adapter/impl/cmsencryption/SwitchableCmsEncryptionImpl.java b/datasafe-simple-adapter/datasafe-simple-adapter-impl/src/main/java/de/adorsys/datasafe/simple/adapter/impl/cmsencryption/SwitchableCmsEncryptionImpl.java index ecb0d0cf5..b2873bae3 100644 --- a/datasafe-simple-adapter/datasafe-simple-adapter-impl/src/main/java/de/adorsys/datasafe/simple/adapter/impl/cmsencryption/SwitchableCmsEncryptionImpl.java +++ b/datasafe-simple-adapter/datasafe-simple-adapter-impl/src/main/java/de/adorsys/datasafe/simple/adapter/impl/cmsencryption/SwitchableCmsEncryptionImpl.java @@ -11,6 +11,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.security.Key; +import java.security.KeyPair; import java.util.Map; import java.util.Set; import java.util.function.Function; @@ -27,9 +28,9 @@ public SwitchableCmsEncryptionImpl(ASNCmsEncryptionConfig encryptionConfig) { } @Override - public OutputStream buildEncryptionOutputStream(OutputStream dataContentStream, Set publicKeys) { + public OutputStream buildEncryptionOutputStream(OutputStream dataContentStream, Set publicKeys, KeyPair senderKeyPair) { if (withCmsEncryption) { - return super.buildEncryptionOutputStream(dataContentStream, publicKeys); + return super.buildEncryptionOutputStream(dataContentStream, publicKeys, senderKeyPair); } return dataContentStream; } diff --git a/datasafe-types-api/src/main/java/de/adorsys/datasafe/types/api/actions/WriteInboxRequest.java b/datasafe-types-api/src/main/java/de/adorsys/datasafe/types/api/actions/WriteInboxRequest.java new file mode 100644 index 000000000..2d9797086 --- /dev/null +++ b/datasafe-types-api/src/main/java/de/adorsys/datasafe/types/api/actions/WriteInboxRequest.java @@ -0,0 +1,59 @@ +package de.adorsys.datasafe.types.api.actions; + +import de.adorsys.datasafe.types.api.callback.ResourceWriteCallback; +import de.adorsys.datasafe.types.api.resource.BasePublicResource; +import de.adorsys.datasafe.types.api.resource.PublicResource; +import de.adorsys.datasafe.types.api.resource.ResourceLocation; +import de.adorsys.datasafe.types.api.resource.StorageIdentifier; +import de.adorsys.datasafe.types.api.resource.Uri; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.NonNull; +import lombok.Singular; +import lombok.Value; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +/** + * Request to write data at some location. + * @param Resource owner. + * @param Resource path (either relative or absolute). + */ +@Value +@AllArgsConstructor +@Builder(toBuilder = true) +public class WriteInboxRequest { + + @NonNull T owner; + + @NonNull R recipients; + + @NonNull L location; + + @NonNull StorageIdentifier storageIdentifier; + + @Singular + List callbacks; + + private WriteInboxRequest(@NonNull T owner, @NonNull R recipients, @NonNull L location, List callbacks) { + this.owner = owner; + this.recipients = recipients; + this.location = location; + this.callbacks = callbacks; + this.storageIdentifier = StorageIdentifier.DEFAULT; + } + + public static WriteInboxRequest forDefaultPublic(T owner, R recipients, String path) { + return new WriteInboxRequest<>(owner, recipients, new BasePublicResource(new Uri(path)), new ArrayList<>()); + } + + public static WriteInboxRequest forDefaultPublic(T owner, R recipients, Uri path) { + return new WriteInboxRequest<>(owner, recipients, new BasePublicResource(path), new ArrayList<>()); + } + + public static WriteInboxRequest forDefaultPublic(T owner, R recipients, URI path) { + return forDefaultPublic(owner, recipients, new Uri(path)); + } +} diff --git a/datasafe-types-api/src/main/java/de/adorsys/datasafe/types/api/actions/WriteRequest.java b/datasafe-types-api/src/main/java/de/adorsys/datasafe/types/api/actions/WriteRequest.java index 6c4008578..06d4d5576 100644 --- a/datasafe-types-api/src/main/java/de/adorsys/datasafe/types/api/actions/WriteRequest.java +++ b/datasafe-types-api/src/main/java/de/adorsys/datasafe/types/api/actions/WriteRequest.java @@ -2,9 +2,7 @@ import de.adorsys.datasafe.types.api.callback.ResourceWriteCallback; import de.adorsys.datasafe.types.api.resource.BasePrivateResource; -import de.adorsys.datasafe.types.api.resource.BasePublicResource; import de.adorsys.datasafe.types.api.resource.PrivateResource; -import de.adorsys.datasafe.types.api.resource.PublicResource; import de.adorsys.datasafe.types.api.resource.ResourceLocation; import de.adorsys.datasafe.types.api.resource.StorageIdentifier; import de.adorsys.datasafe.types.api.resource.Uri; @@ -28,17 +26,14 @@ @Builder(toBuilder = true) public class WriteRequest { - @NonNull - private final T owner; + @NonNull T owner; - @NonNull - private final L location; + @NonNull L location; - @NonNull - private final StorageIdentifier storageIdentifier; + @NonNull StorageIdentifier storageIdentifier; @Singular - private final List callbacks; + List callbacks; private WriteRequest(@NonNull T owner, @NonNull L location, List callbacks) { this.owner = owner; @@ -51,35 +46,23 @@ public static WriteRequest forDefaultPrivate(T owner, St return new WriteRequest<>(owner, BasePrivateResource.forPrivate(new Uri(path)), new ArrayList<>()); } - public static WriteRequest forDefaultPublic(T owner, String path) { - return new WriteRequest<>(owner, new BasePublicResource(new Uri(path)), new ArrayList<>()); - } - - public static WriteRequest forDefaultPrivate(T owner, URI path) { - return forDefaultPrivate(owner, new Uri(path)); - } - public static WriteRequest forDefaultPrivate(T owner, Uri path) { return new WriteRequest<>(owner, BasePrivateResource.forPrivate(path), new ArrayList<>()); } - public static WriteRequest forDefaultPublic(T owner, URI path) { - return forDefaultPublic(owner, new Uri(path)); - } - - public static WriteRequest forDefaultPublic(T owner, Uri path) { - return new WriteRequest<>(owner, new BasePublicResource(path), new ArrayList<>()); + public static WriteRequest forDefaultPrivate(T owner, URI path) { + return forDefaultPrivate(owner, new Uri(path)); } public static WriteRequest forPrivate(T owner, StorageIdentifier storage, String path) { return new WriteRequest<>(owner, BasePrivateResource.forPrivate(new Uri(path)), storage, new ArrayList<>()); } - public static WriteRequest forPrivate(T owner, StorageIdentifier storage, URI path) { - return forPrivate(owner, storage, new Uri(path)); - } - public static WriteRequest forPrivate(T owner, StorageIdentifier storage, Uri path) { return new WriteRequest<>(owner, BasePrivateResource.forPrivate(path), storage, new ArrayList<>()); } + + public static WriteRequest forPrivate(T owner, StorageIdentifier storage, URI path) { + return forPrivate(owner, storage, new Uri(path)); + } } diff --git a/pom.xml b/pom.xml index d6142200b..ae3b5dfb6 100644 --- a/pom.xml +++ b/pom.xml @@ -73,19 +73,16 @@ none src/main/java src/test/java - - - ${basedir}/../../target/jacoco.exec - ${basedir}/../target/jacoco-e2e.exec - + ${maven.multiModuleProjectDirectory}/target/jacoco.exec + ${maven.multiModuleProjectDirectory}/target/jacoco-e2e.exec 19.2.0 - 3.11.0 + 3.12.1 17 17 - 3.6.0 + 3.6.1 1.18.30 2.8.9 - 2.46.1 + 2.50 32.1.1-jre 4.0.3 5.10.0 @@ -608,11 +605,6 @@ maven-gpg-plugin ${maven-gpg-plugin.version} - opensource@adorsys.de - - --pinentry-mode - loopback - @@ -621,12 +613,6 @@ sign - - - --pinentry-mode - loopback - -