diff --git a/dsf-bpe/dsf-bpe-process-api-v2-impl/pom.xml b/dsf-bpe/dsf-bpe-process-api-v2-impl/pom.xml
index 9b1c4cf9e..a2f40486f 100644
--- a/dsf-bpe/dsf-bpe-process-api-v2-impl/pom.xml
+++ b/dsf-bpe/dsf-bpe-process-api-v2-impl/pom.xml
@@ -57,6 +57,12 @@
com.auth0
java-jwt
+
+
+ de.hs-heilbronn.mi
+ crypto-utils
+ ${crypto-utils.version.v2}
+
org.apache.logging.log4j
@@ -93,6 +99,11 @@
dev.dsf
dsf-bpe-process-api-v2-impl
+
+ de.hs-heilbronn.mi
+ crypto-utils
+ ${crypto-utils.version.v2}
+
ca.uhn.hapi.fhir
hapi-fhir-structures-r4
diff --git a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/ProcessPluginApiImpl.java b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/ProcessPluginApiImpl.java
index ac12106dc..78ab08bde 100644
--- a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/ProcessPluginApiImpl.java
+++ b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/ProcessPluginApiImpl.java
@@ -8,6 +8,7 @@
import ca.uhn.fhir.context.FhirContext;
import dev.dsf.bpe.v2.config.ProxyConfig;
+import dev.dsf.bpe.v2.service.CryptoService;
import dev.dsf.bpe.v2.service.DsfClientProvider;
import dev.dsf.bpe.v2.service.EndpointProvider;
import dev.dsf.bpe.v2.service.FhirClientProvider;
@@ -34,13 +35,14 @@ public class ProcessPluginApiImpl implements ProcessPluginApi, InitializingBean
private final QuestionnaireResponseHelper questionnaireResponseHelper;
private final ReadAccessHelper readAccessHelper;
private final TaskHelper taskHelper;
+ private final CryptoService cryptoService;
public ProcessPluginApiImpl(ProxyConfig proxyConfig, EndpointProvider endpointProvider, FhirContext fhirContext,
DsfClientProvider dsfClientProvider, FhirClientProvider fhirClientProvider,
OidcClientProvider oidcClientProvider, MailService mailService, ObjectMapper objectMapper,
OrganizationProvider organizationProvider, ProcessAuthorizationHelper processAuthorizationHelper,
QuestionnaireResponseHelper questionnaireResponseHelper, ReadAccessHelper readAccessHelper,
- TaskHelper taskHelper)
+ TaskHelper taskHelper, CryptoService cryptoService)
{
this.proxyConfig = proxyConfig;
this.endpointProvider = endpointProvider;
@@ -55,6 +57,7 @@ public ProcessPluginApiImpl(ProxyConfig proxyConfig, EndpointProvider endpointPr
this.questionnaireResponseHelper = questionnaireResponseHelper;
this.readAccessHelper = readAccessHelper;
this.taskHelper = taskHelper;
+ this.cryptoService = cryptoService;
}
@Override
@@ -73,6 +76,7 @@ public void afterPropertiesSet() throws Exception
Objects.requireNonNull(questionnaireResponseHelper, "questionnaireResponseHelper");
Objects.requireNonNull(readAccessHelper, "readAccessHelper");
Objects.requireNonNull(taskHelper, "taskHelper");
+ Objects.requireNonNull(cryptoService, "cryptoService");
}
@Override
@@ -152,4 +156,10 @@ public TaskHelper getTaskHelper()
{
return taskHelper;
}
+
+ @Override
+ public CryptoService getCryptoService()
+ {
+ return cryptoService;
+ }
}
diff --git a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/CryptoServiceImpl.java b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/CryptoServiceImpl.java
new file mode 100644
index 000000000..6539d716a
--- /dev/null
+++ b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/CryptoServiceImpl.java
@@ -0,0 +1,217 @@
+package dev.dsf.bpe.v2.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyManagementException;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+
+import javax.crypto.DecapsulateException;
+import javax.crypto.NoSuchPaddingException;
+import javax.net.ssl.SSLContext;
+
+import de.hsheilbronn.mi.utils.crypto.cert.CertificateValidator;
+import de.hsheilbronn.mi.utils.crypto.context.SSLContextFactory;
+import de.hsheilbronn.mi.utils.crypto.io.KeyStoreReader;
+import de.hsheilbronn.mi.utils.crypto.io.PemReader;
+import de.hsheilbronn.mi.utils.crypto.kem.AbstractKemAesGcm;
+import de.hsheilbronn.mi.utils.crypto.kem.EcDhKemAesGcm;
+import de.hsheilbronn.mi.utils.crypto.kem.RsaKemAesGcm;
+import de.hsheilbronn.mi.utils.crypto.keypair.KeyPairGeneratorFactory;
+import de.hsheilbronn.mi.utils.crypto.keypair.KeyPairValidator;
+import de.hsheilbronn.mi.utils.crypto.keystore.KeyStoreCreator;
+
+public class CryptoServiceImpl implements CryptoService
+{
+ public static final class KemDelegate implements Kem
+ {
+ private final AbstractKemAesGcm delegate;
+
+ public KemDelegate(AbstractKemAesGcm delegate)
+ {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public InputStream encrypt(InputStream data, PublicKey publicKey) throws NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException
+ {
+ return delegate.encrypt(data, publicKey);
+ }
+
+ @Override
+ public InputStream decrypt(InputStream encrypted, PrivateKey privateKey)
+ throws IOException, NoSuchAlgorithmException, InvalidKeyException, DecapsulateException,
+ NoSuchPaddingException, InvalidAlgorithmParameterException
+ {
+ return delegate.decrypt(encrypted, privateKey);
+ }
+ }
+
+ @Override
+ public Kem createRsaKem()
+ {
+ return new KemDelegate(new RsaKemAesGcm());
+ }
+
+ @Override
+ public Kem createEcDhKem()
+ {
+ return new KemDelegate(new EcDhKemAesGcm());
+ }
+
+ @Override
+ public KeyPairGenerator createKeyPairGeneratorRsa4096AndInitialize()
+ {
+ return KeyPairGeneratorFactory.rsa4096().initialize();
+ }
+
+ @Override
+ public KeyPairGenerator createKeyPairGeneratorSecp256r1AndInitialize()
+ {
+ return KeyPairGeneratorFactory.secp256r1().initialize();
+ }
+
+ @Override
+ public KeyPairGenerator createKeyPairGeneratorSecp384r1AndInitialize()
+ {
+ return KeyPairGeneratorFactory.secp384r1().initialize();
+ }
+
+ @Override
+ public KeyPairGenerator createKeyPairGeneratorSecp521r1AndInitialize()
+ {
+ return KeyPairGeneratorFactory.secp521r1().initialize();
+ }
+
+ @Override
+ public KeyPairGenerator createKeyPairGeneratorX25519AndInitialize()
+ {
+ return KeyPairGeneratorFactory.x25519().initialize();
+ }
+
+ @Override
+ public KeyPairGenerator createKeyPairGeneratorX448AndInitialize()
+ {
+ return KeyPairGeneratorFactory.x448().initialize();
+ }
+
+ @Override
+ public X509Certificate readCertificate(InputStream pem) throws IOException
+ {
+ return PemReader.readCertificate(pem);
+ }
+
+ @Override
+ public List readCertificates(InputStream pem) throws IOException
+ {
+ return PemReader.readCertificates(pem);
+ }
+
+ @Override
+ public PrivateKey readPrivateKey(InputStream pem, char[] password) throws IOException
+ {
+ return PemReader.readPrivateKey(pem, password);
+ }
+
+ @Override
+ public boolean isKeyPair(PrivateKey privateKey, PublicKey publicKey)
+ {
+ return KeyPairValidator.matches(privateKey, publicKey);
+ }
+
+ @Override
+ public boolean isCertificateExpired(X509Certificate certificate)
+ {
+ return CertificateValidator.isCertificateExpired(certificate);
+ }
+
+ @Override
+ public boolean isClientCertificate(X509Certificate certificate)
+ {
+ return CertificateValidator.isClientCertificate(certificate);
+ }
+
+ @Override
+ public boolean isServerCertificate(X509Certificate certificate)
+ {
+ return CertificateValidator.isServerCertificate(certificate);
+ }
+
+ @Override
+ public void validateClientCertificate(KeyStore trustStore, Collection extends X509Certificate> certificateChain)
+ throws CertificateException
+ {
+ Objects.requireNonNull(trustStore, "trustStore");
+ Objects.requireNonNull(certificateChain, "certificateChain");
+
+ CertificateValidator.vaildateClientCertificate(trustStore, certificateChain);
+ }
+
+ @Override
+ public void validateServerCertificate(KeyStore trustStore, Collection extends X509Certificate> certificateChain)
+ throws CertificateException
+ {
+ Objects.requireNonNull(trustStore, "trustStore");
+ Objects.requireNonNull(certificateChain, "certificateChain");
+
+ CertificateValidator.vaildateServerCertificate(trustStore, certificateChain);
+ }
+
+ @Override
+ public KeyStore createKeyStoreForPrivateKeyAndCertificateChain(PrivateKey key, char[] password,
+ Collection extends X509Certificate> chain)
+ {
+ return KeyStoreCreator.jksForPrivateKeyAndCertificateChain(key, password, chain);
+ }
+
+ @Override
+ public KeyStore createKeyStoreForTrustedCertificates(Collection extends X509Certificate> certificates)
+ {
+ return KeyStoreCreator.jksForTrustedCertificates(certificates);
+ }
+
+ @Override
+ public KeyStore readKeyStoreJks(InputStream stream, char[] password) throws IOException
+ {
+ return KeyStoreReader.readJks(stream, password);
+ }
+
+ @Override
+ public KeyStore readKeyStorePkcs12(InputStream stream, char[] password) throws IOException
+ {
+ return KeyStoreReader.readPkcs12(stream, password);
+ }
+
+ @Override
+ public SSLContext createSSLContext(KeyStore trustStore)
+ throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException
+ {
+ Objects.requireNonNull(trustStore, "trustStore");
+
+ return SSLContextFactory.createSSLContext(trustStore);
+ }
+
+ @Override
+ public SSLContext createSSLContext(KeyStore trustStore, KeyStore keyStore, char[] keyStorePassword)
+ throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException
+ {
+ Objects.requireNonNull(trustStore, "trustStore");
+ Objects.requireNonNull(keyStore, "keyStore");
+ Objects.requireNonNull(keyStorePassword, "keyStorePassword");
+
+ return SSLContextFactory.createSSLContext(trustStore, keyStore, keyStorePassword);
+ }
+}
diff --git a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/spring/ApiServiceConfig.java b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/spring/ApiServiceConfig.java
index 012e26dd7..7921ee466 100644
--- a/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/spring/ApiServiceConfig.java
+++ b/dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/spring/ApiServiceConfig.java
@@ -33,6 +33,8 @@
import dev.dsf.bpe.v2.listener.EndListener;
import dev.dsf.bpe.v2.listener.StartListener;
import dev.dsf.bpe.v2.plugin.ProcessPluginFactoryImpl;
+import dev.dsf.bpe.v2.service.CryptoService;
+import dev.dsf.bpe.v2.service.CryptoServiceImpl;
import dev.dsf.bpe.v2.service.DsfClientProvider;
import dev.dsf.bpe.v2.service.DsfClientProviderImpl;
import dev.dsf.bpe.v2.service.EndpointProvider;
@@ -88,7 +90,8 @@ public ProcessPluginApi processPluginApiV2()
{
return new ProcessPluginApiImpl(proxyConfigDelegate(), endpointProvider(), fhirContext(), dsfClientProvider(),
fhirClientProvider(), oidcClientProvider(), mailService(), objectMapper(), organizationProvider(),
- processAuthorizationHelper(), questionnaireResponseHelper(), readAccessHelper(), taskHelper());
+ processAuthorizationHelper(), questionnaireResponseHelper(), readAccessHelper(), taskHelper(),
+ cryptoService());
}
@Bean
@@ -255,4 +258,10 @@ public ListenerFactory listenerFactory()
return new ListenerFactoryImpl(ProcessPluginFactoryImpl.API_VERSION, startListener(), endListener(),
continueListener());
}
+
+ @Bean
+ public CryptoService cryptoService()
+ {
+ return new CryptoServiceImpl();
+ }
}
diff --git a/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/ProcessPluginApi.java b/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/ProcessPluginApi.java
index 76ec1d614..f8689bdc6 100644
--- a/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/ProcessPluginApi.java
+++ b/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/ProcessPluginApi.java
@@ -7,6 +7,7 @@
import ca.uhn.fhir.context.FhirContext;
import dev.dsf.bpe.v2.config.ProxyConfig;
+import dev.dsf.bpe.v2.service.CryptoService;
import dev.dsf.bpe.v2.service.DsfClientProvider;
import dev.dsf.bpe.v2.service.EndpointProvider;
import dev.dsf.bpe.v2.service.FhirClientProvider;
@@ -52,4 +53,6 @@ public interface ProcessPluginApi
ReadAccessHelper getReadAccessHelper();
TaskHelper getTaskHelper();
+
+ CryptoService getCryptoService();
}
diff --git a/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/service/CryptoService.java b/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/service/CryptoService.java
new file mode 100644
index 000000000..c9e618c81
--- /dev/null
+++ b/dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/service/CryptoService.java
@@ -0,0 +1,518 @@
+package dev.dsf.bpe.v2.service;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyManagementException;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPublicKey;
+import java.time.ZonedDateTime;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+
+import javax.crypto.DecapsulateException;
+import javax.crypto.NoSuchPaddingException;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+
+/**
+ * Provides methods for:
+ *
+ * - Creating and using RSA and ECDH key encapsulation mechanism
+ * - Reading X509 certificates and private-keys (encrypted or not encrypted)
+ * - Reading JKS and PKCS12 key-stores
+ * - Creating JKS and PKCS12 key-stores based on trusted certificates or private-key and certificate chain
+ * - Generating RSA (4096 bit), EC (secp256r1, secp384r1, secp521r1, X25519, X448) key-pairs
+ * - Validating key-pairs to check if a private-key belongs to a public-key
+ * - Validating certificates
+ * - Creating {@link SSLContext}s based on a key-store with trusted certificates and/or a key-store with private-key
+ * and certificate chain
+ *
+ */
+public interface CryptoService
+{
+ /**
+ * Key encapsulation mechanism with encrypt and decrypt methods.
+ */
+ public interface Kem
+ {
+ /**
+ * Encrypts the given {@link InputStream} with an AES session key calculated by KEM for the given
+ * {@link PublicKey}. The returned {@link InputStream} has the form [encapsulation length (big-endian, 2 bytes),
+ * encapsulation, AES initialization vector (12 bytes), AES encrypted data].
+ *
+ * @param data
+ * not null
+ * @param publicKey
+ * not null
+ * @return byte array of [encapsulation length (big-endian, 2 bytes), encapsulation, iv (12 bytes), encrypted
+ * data]
+ * @throws IOException
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeyException
+ * @throws NoSuchPaddingException
+ * @throws InvalidAlgorithmParameterException
+ */
+ default byte[] encrypt(byte[] data, PublicKey publicKey) throws IOException, NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException
+ {
+ return encrypt(new ByteArrayInputStream(data), publicKey).readAllBytes();
+ }
+
+ /**
+ * Encrypts the given {@link InputStream} with an AES session key calculated by KEM for the given
+ * {@link PublicKey}. The returned {@link InputStream} has the form [encapsulation length (big-endian, 2 bytes),
+ * encapsulation, AES initialization vector (12 bytes), AES encrypted data].
+ *
+ * @param data
+ * not null
+ * @param publicKey
+ * not null
+ * @return {@link InputStream} of [encapsulation length (big-endian, 2 bytes), encapsulation, iv (12 bytes),
+ * encrypted data]
+ * @throws IOException
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeyException
+ * @throws NoSuchPaddingException
+ * @throws InvalidAlgorithmParameterException
+ */
+ InputStream encrypt(InputStream data, PublicKey publicKey) throws IOException, NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException;
+
+ /**
+ * @param encrypted
+ * not null, {@link InputStream} of [encapsulation length (big-endian, 2 bytes),
+ * encapsulation, iv (12 bytes), encrypted data]
+ * @param privateKey
+ * not null
+ * @return decrypted data
+ * @throws IOException
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeyException
+ * @throws DecapsulateException
+ * @throws NoSuchPaddingException
+ * @throws InvalidAlgorithmParameterException
+ */
+ default byte[] decrypt(byte[] encrypted, PrivateKey privateKey) throws IOException, NoSuchAlgorithmException,
+ InvalidKeyException, DecapsulateException, NoSuchPaddingException, InvalidAlgorithmParameterException
+ {
+ return decrypt(new ByteArrayInputStream(encrypted), privateKey).readAllBytes();
+ }
+
+ /**
+ * @param encrypted
+ * not null, {@link InputStream} of [encapsulation length (big-endian, 2 bytes),
+ * encapsulation, iv (12 bytes), encrypted data]
+ * @param privateKey
+ * not null
+ * @return decrypted data
+ * @throws IOException
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeyException
+ * @throws DecapsulateException
+ * @throws NoSuchPaddingException
+ * @throws InvalidAlgorithmParameterException
+ */
+ InputStream decrypt(InputStream encrypted, PrivateKey privateKey) throws IOException, NoSuchAlgorithmException,
+ InvalidKeyException, DecapsulateException, NoSuchPaddingException, InvalidAlgorithmParameterException;
+ }
+
+ /**
+ * @return key encapsulation mechanism with RSA key exchange using KDF2 SHA-512 for AES-256, use with RSA key pairs
+ */
+ Kem createRsaKem();
+
+ /**
+ * @return key encapsulation mechanism with Diffie–Hellman key exchange for AES-256, use with elliptic curve key
+ * pairs like X25519, X448, secp256r1, secp384r1 and secp521r1
+ */
+ Kem createEcDhKem();
+
+ /**
+ * @return created and initialized RSA (4096 bit) key pair generator
+ * @see KeyPairGenerator#generateKeyPair()
+ */
+ KeyPairGenerator createKeyPairGeneratorRsa4096AndInitialize();
+
+ /**
+ * @return created and initialized secp256r1 key pair generator
+ * @see KeyPairGenerator#generateKeyPair()
+ */
+ KeyPairGenerator createKeyPairGeneratorSecp256r1AndInitialize();
+
+ /**
+ * @return created and initialized secp384r1 key pair generator
+ * @see KeyPairGenerator#generateKeyPair()
+ */
+ KeyPairGenerator createKeyPairGeneratorSecp384r1AndInitialize();
+
+ /**
+ * @return created and initialized secp521r1 key pair generator
+ * @see KeyPairGenerator#generateKeyPair()
+ */
+ KeyPairGenerator createKeyPairGeneratorSecp521r1AndInitialize();
+
+ /**
+ * @return created and initialized x25519 key pair generator
+ * @see KeyPairGenerator#generateKeyPair()
+ */
+ KeyPairGenerator createKeyPairGeneratorX25519AndInitialize();
+
+ /**
+ * @return created and initialized x448 key pair generator
+ * @see KeyPairGenerator#generateKeyPair()
+ */
+ KeyPairGenerator createKeyPairGeneratorX448AndInitialize();
+
+ /**
+ * @param pem
+ * not null
+ * @return certificate
+ * @throws IOException
+ * if the given file does not contain a pem encoded certificate, more than one or is not readable or
+ * parsable
+ */
+ default X509Certificate readCertificate(Path pem) throws IOException
+ {
+ Objects.requireNonNull(pem, "pem");
+
+ try (InputStream in = Files.newInputStream(pem))
+ {
+ return readCertificate(in);
+ }
+ }
+
+ /**
+ * @param pem
+ * not null
+ * @return certificate
+ * @throws IOException
+ * if the given {@link InputStream} does not contain a pem encoded certificate, more than one or is not
+ * readable or parsable
+ */
+ X509Certificate readCertificate(InputStream pem) throws IOException;
+
+ /**
+ * @param pem
+ * not null
+ * @return list of certificates
+ * @throws IOException
+ * if the given file does not contain pem encoded certificates or is not readable or one is not parsable
+ */
+ default List readCertificates(Path pem) throws IOException
+ {
+ Objects.requireNonNull(pem, "pem");
+
+ try (InputStream in = Files.newInputStream(pem))
+ {
+ return readCertificates(in);
+ }
+ }
+
+ /**
+ * @param pem
+ * @return list of certificates
+ * @throws IOException
+ * if the given {@link InputStream} does not contain pem encoded certificates or is not readable or one
+ * is not parsable
+ */
+ List readCertificates(InputStream pem) throws IOException;
+
+ /**
+ * @param pem
+ * not null
+ * @return private key
+ * @throws IOException
+ * if the given file does not contain a pem encoded, unencrypted private key, more than one or is not
+ * readable or parsable
+ */
+ default PrivateKey readPrivateKey(Path pem) throws IOException
+ {
+ return readPrivateKey(pem, null);
+ }
+
+ /**
+ * @param pem
+ * not null
+ * @return private key
+ * @throws IOException
+ * if the given {@link InputStream} does not contain a pem encoded, unencrypted private key, more than
+ * one or is not readable or parsable
+ */
+ default PrivateKey readPrivateKey(InputStream pem) throws IOException
+ {
+ return readPrivateKey(pem, null);
+ }
+
+ /**
+ * @param pem
+ * not null
+ * @param password
+ * if key encrypted not null
+ * @return private key
+ * @throws IOException
+ * if the given file does not contain a pem encoded private key, more than one or is not readable or
+ * parsable
+ */
+ default PrivateKey readPrivateKey(Path pem, char[] password) throws IOException
+ {
+ Objects.requireNonNull(pem, "pem");
+
+ try (InputStream in = Files.newInputStream(pem))
+ {
+ return readPrivateKey(in, password);
+ }
+ }
+
+ /**
+ * @param pem
+ * not null
+ * @param password
+ * if key encrypted not null
+ * @return private key
+ * @throws IOException
+ * if the given {@link InputStream} does not contain a pem encoded private key, more than one or is not
+ * readable or parsable
+ */
+ PrivateKey readPrivateKey(InputStream pem, char[] password) throws IOException;
+
+ /**
+ * Checks if the given privateKey and publicKey match by checking if a generated signature can be
+ * verified for RSA, EC and EdDSA key pairs or a Diffie-Hellman key agreement produces the same secret key for a XDH
+ * key pair. If the privateKey is a {@link RSAPrivateCrtKey} and the publicKey is a
+ * {@link RSAPublicKey} modulus and public-exponent will be compared.
+ *
+ * @param privateKey
+ * may be null
+ * @param publicKey
+ * may be null
+ * @return true if the given keys are not null and match
+ */
+ boolean isKeyPair(PrivateKey privateKey, PublicKey publicKey);
+
+ /**
+ * @param certificate
+ * not null
+ * @return true if the given certificate not-after field is after {@link ZonedDateTime#now()}
+ */
+ boolean isCertificateExpired(X509Certificate certificate);
+
+ /**
+ * @param certificate
+ * not null
+ * @return true if given certificate has extended key usage extension "TLS Web Client
+ * Authentication"
+ */
+ boolean isClientCertificate(X509Certificate certificate);
+
+ /**
+ * @param certificate
+ * not null
+ * @return true if given certificate has extended key usage extension "TLS Web Server
+ * Authentication"
+ */
+ boolean isServerCertificate(X509Certificate certificate);
+
+ /**
+ * @param trustStore
+ * not null
+ * @param certificateChain
+ * not null
+ * @throws CertificateException
+ * if the the given certificate or certificate chain is not trusted as a client certificate by a PKIX
+ * trust manager created for the given trust store
+ */
+ default void validateClientCertificate(KeyStore trustStore, X509Certificate... certificateChain)
+ throws CertificateException
+ {
+ validateClientCertificate(trustStore, List.of(certificateChain));
+ }
+
+ /**
+ * @param trustStore
+ * not null
+ * @param certificateChain
+ * not null
+ * @throws CertificateException
+ * if the the given certificate or certificate chain is not trusted as a client certificate by a PKIX
+ * trust manager created for the given trust store
+ */
+ void validateClientCertificate(KeyStore trustStore, Collection extends X509Certificate> certificateChain)
+ throws CertificateException;
+
+ /**
+ * @param trustStore
+ * not null
+ * @param certificateChain
+ * not null
+ * @throws CertificateException
+ * if the the given certificate or certificate chain is not trusted as a server certificate by a PKIX
+ * trust manager created for the given trust store
+ */
+ default void validateServerCertificate(KeyStore trustStore, X509Certificate... certificateChain)
+ throws CertificateException
+ {
+ validateServerCertificate(trustStore, List.of(certificateChain));
+ }
+
+ /**
+ * @param trustStore
+ * not null
+ * @param certificateChain
+ * not null
+ * @throws CertificateException
+ * if the the given certificate or certificate chain is not trusted as a server certificate by a PKIX
+ * trust manager created for the given trust store
+ */
+ void validateServerCertificate(KeyStore trustStore, Collection extends X509Certificate> certificateChain)
+ throws CertificateException;
+
+ /**
+ * @param key
+ * not null
+ * @param password
+ * not null
+ * @param chain
+ * not null, at least one
+ * @return jks {@link KeyStore} for the given key and chain
+ */
+ default KeyStore createKeyStoreForPrivateKeyAndCertificateChain(PrivateKey key, char[] password,
+ X509Certificate... chain)
+ {
+ return createKeyStoreForPrivateKeyAndCertificateChain(key, password, Arrays.asList(chain));
+ }
+
+ /**
+ * @param key
+ * not null
+ * @param password
+ * not null
+ * @param chain
+ * not null, at least one
+ * @return jks {@link KeyStore} for the given key and chain
+ */
+ KeyStore createKeyStoreForPrivateKeyAndCertificateChain(PrivateKey key, char[] password,
+ Collection extends X509Certificate> chain);
+
+ /**
+ * @param certificates
+ * not null, at least one
+ * @return jks {@link KeyStore} for the given certificates
+ */
+ default KeyStore createKeyStoreForTrustedCertificates(X509Certificate... certificates)
+ {
+ return createKeyStoreForTrustedCertificates(List.of(certificates));
+ }
+
+ /**
+ * @param certificates
+ * not null, at least one
+ * @return jks {@link KeyStore} for the given certificates
+ */
+ KeyStore createKeyStoreForTrustedCertificates(Collection extends X509Certificate> certificates);
+
+ /**
+ * @param file
+ * not null
+ * @param password
+ * if not null used to check the integrity of the keystore
+ * @return jks {@link KeyStore}
+ * @throws IOException
+ * @see KeyStore#load(InputStream, char[])
+ */
+ default KeyStore readKeyStoreJks(Path file, char[] password) throws IOException
+ {
+ Objects.requireNonNull(file, "file");
+
+ try (InputStream in = Files.newInputStream(file))
+ {
+ return readKeyStoreJks(in, password);
+ }
+ }
+
+ /**
+ * @param stream
+ * not null
+ * @param password
+ * if not null used to check the integrity of the keystore
+ * @return jks {@link KeyStore}
+ * @throws IOException
+ * @see KeyStore#load(InputStream, char[])
+ */
+ KeyStore readKeyStoreJks(InputStream stream, char[] password) throws IOException;
+
+ /**
+ * @param file
+ * not null
+ * @param password
+ * if not null used to check the integrity of the keystore
+ * @return pkcs12 {@link KeyStore}
+ * @throws IOException
+ * @see KeyStore#load(InputStream, char[])
+ */
+ default KeyStore readKeyStorePkcs12(Path file, char[] password) throws IOException
+ {
+ Objects.requireNonNull(file, "file");
+
+ try (InputStream in = Files.newInputStream(file))
+ {
+ return readKeyStorePkcs12(in, password);
+ }
+ }
+
+ /**
+ * @param stream
+ * not null
+ * @param password
+ * if not null used to check the integrity of the keystore
+ * @return pkcs12 {@link KeyStore}
+ * @throws IOException
+ * @see KeyStore#load(InputStream, char[])
+ */
+ KeyStore readKeyStorePkcs12(InputStream stream, char[] password) throws IOException;
+
+ /**
+ * @param trustStore
+ * not null
+ * @return {@link SSLContext} with {@link TrustManager} for the given trustStore
+ * @throws KeyStoreException
+ * @throws NoSuchAlgorithmException
+ * @throws UnrecoverableKeyException
+ * @throws KeyManagementException
+ */
+ SSLContext createSSLContext(KeyStore trustStore)
+ throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException;
+
+ /**
+ * @param trustStore
+ * not null
+ * @param keyStore
+ * not null
+ * @param keyStorePassword
+ * not null
+ * @return {@link SSLContext} with {@link TrustManager} for the given trustStore and {@link KeyManager} for
+ * the given keyStore / keyStorePassword
+ * @throws KeyStoreException
+ * @throws NoSuchAlgorithmException
+ * @throws UnrecoverableKeyException
+ * @throws KeyManagementException
+ */
+ SSLContext createSSLContext(KeyStore trustStore, KeyStore keyStore, char[] keyStorePassword)
+ throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException;
+}
diff --git a/dsf-bpe/dsf-bpe-server/src/main/resources/bpe/api/v2/allowed-bpe-classes.list b/dsf-bpe/dsf-bpe-server/src/main/resources/bpe/api/v2/allowed-bpe-classes.list
index 03b73b653..904d411e3 100644
--- a/dsf-bpe/dsf-bpe-server/src/main/resources/bpe/api/v2/allowed-bpe-classes.list
+++ b/dsf-bpe/dsf-bpe-server/src/main/resources/bpe/api/v2/allowed-bpe-classes.list
@@ -12,6 +12,7 @@ org.apache.commons.io
org.apache.commons.lang3
org.apache.commons.text
org.apache.http
+org.bouncycastle
org.camunda.bpm.engine.delegate
org.camunda.bpm.engine.impl.bpmn.parser.FieldDeclaration
org.camunda.bpm.engine.impl.el.FixedValue
diff --git a/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/AbstractIntegrationTest.java b/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/AbstractIntegrationTest.java
index e1b39334b..c6779a7da 100644
--- a/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/AbstractIntegrationTest.java
+++ b/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/AbstractIntegrationTest.java
@@ -160,6 +160,7 @@ public static void beforeClass() throws Exception
Paths.get("src/main/resources/bpe/api/v1/allowed-bpe-classes.list"));
allowedBpeClassesV1.add("dev.dsf.bpe.test.PluginTest");
allowedBpeClassesV1.add("dev.dsf.bpe.test.PluginTestExecutor");
+ allowedBpeClassesV1.add("dev.dsf.bpe.test.PluginTestExecutor$RunnableWithException");
writeListFile(ALLOWED_BPE_CLASSES_LIST_FILE_V1, allowedBpeClassesV1);
// allowed bpe classes override to enable access to classes from dsf-bpe-test-plugin module for v2 test plugins
@@ -167,6 +168,7 @@ public static void beforeClass() throws Exception
Paths.get("src/main/resources/bpe/api/v2/allowed-bpe-classes.list"));
allowedBpeClassesV2.add("dev.dsf.bpe.test.PluginTest");
allowedBpeClassesV2.add("dev.dsf.bpe.test.PluginTestExecutor");
+ allowedBpeClassesV2.add("dev.dsf.bpe.test.PluginTestExecutor$RunnableWithException");
writeListFile(ALLOWED_BPE_CLASSES_LIST_FILE_V2, allowedBpeClassesV2);
bpeDefaultDataSource = createBpeDefaultDataSource(bpeLiquibaseRule.getHost(),
diff --git a/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/PluginV2IntegrationTest.java b/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/PluginV2IntegrationTest.java
index d88707a32..83d2adafc 100644
--- a/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/PluginV2IntegrationTest.java
+++ b/dsf-bpe/dsf-bpe-server/src/test/java/dev/dsf/bpe/integration/PluginV2IntegrationTest.java
@@ -1,8 +1,22 @@
package dev.dsf.bpe.integration;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.cert.X509Certificate;
+import java.util.List;
+
import org.junit.BeforeClass;
import org.junit.Test;
+import de.hsheilbronn.mi.utils.crypto.ca.CertificateAuthority;
+import de.hsheilbronn.mi.utils.crypto.ca.CertificationRequest;
+import de.hsheilbronn.mi.utils.crypto.ca.CertificationRequest.CertificationRequestAndPrivateKey;
+import de.hsheilbronn.mi.utils.crypto.io.KeyStoreWriter;
+import de.hsheilbronn.mi.utils.crypto.io.PemWriter;
+import de.hsheilbronn.mi.utils.crypto.keystore.KeyStoreCreator;
+
public class PluginV2IntegrationTest extends AbstractPluginIntegrationTest
{
private static final String PROCESS_VERSION = "2.0";
@@ -83,4 +97,93 @@ public void startJsonVariableTest() throws Exception
{
executePluginTest(createTestTask("JsonVariableTest"));
}
+
+ @Test
+ public void startCryptoServiceTest() throws Exception
+ {
+ List filesToDelete = null;
+ try
+ {
+ filesToDelete = createCaCerKeyFiles();
+ executePluginTest(createTestTask("CryptoServiceTest"));
+ }
+ finally
+ {
+ if (filesToDelete != null)
+ filesToDelete.forEach(this::deleteFile);
+ }
+ }
+
+ private List createCaCerKeyFiles() throws Exception
+ {
+ CertificateAuthority ca = CertificateAuthority
+ .builderSha384EcdsaSecp384r1("DE", null, null, "DSF", "Test", "Plugin V2 Integration Test CA").build();
+ CertificationRequestAndPrivateKey clientReq = CertificationRequest
+ .builder(ca, "DE", null, null, "DSF", "Test", "client").generateKeyPair().build();
+ CertificationRequestAndPrivateKey serverReq = CertificationRequest
+ .builder(ca, "DE", null, null, "DSF", "Test", "server").generateKeyPair().build();
+ X509Certificate clientCert = ca.signClientCertificate(clientReq);
+ X509Certificate serverCert = ca.signServerCertificate(serverReq);
+
+ char[] password = "password".toCharArray();
+
+ Path caCertFile = Paths.get("target/plugin_v2_ca.crt");
+ PemWriter.writeCertificate(ca.getCertificate(), caCertFile);
+
+ Path caTrustStoreJksFile = Paths.get("target/plugin_v2_ca.jks");
+ KeyStoreWriter.write(KeyStoreCreator.jksForTrustedCertificates(ca.getCertificate()), password,
+ caTrustStoreJksFile);
+
+ Path caTrustStoreP12File = Paths.get("target/plugin_v2_ca.p12");
+ KeyStoreWriter.write(KeyStoreCreator.pkcs12ForTrustedCertificates(ca.getCertificate()), password,
+ caTrustStoreP12File);
+
+ Path clientCertFile = Paths.get("target/plugin_v2_client.crt");
+ PemWriter.writeCertificate(clientCert, clientCertFile);
+
+ Path clientKeyFile = Paths.get("target/plugin_v2_client.key");
+ PemWriter.writePrivateKey(clientReq.getPrivateKey()).asPkcs8().encryptedAes128(password).toFile(clientKeyFile);
+
+ Path clientKeyStoreJksFile = Paths.get("target/plugin_v2_client.jks");
+ KeyStoreWriter.write(
+ KeyStoreCreator.jksForPrivateKeyAndCertificateChain(clientReq.getPrivateKey(), password, clientCert),
+ password, clientKeyStoreJksFile);
+
+ Path clientKeyStoreP12File = Paths.get("target/plugin_v2_client.p12");
+ KeyStoreWriter.write(
+ KeyStoreCreator.pkcs12ForPrivateKeyAndCertificateChain(clientReq.getPrivateKey(), password, clientCert),
+ password, clientKeyStoreP12File);
+
+ Path serverCertFile = Paths.get("target/plugin_v2_server.crt");
+ PemWriter.writeCertificate(serverCert, serverCertFile);
+
+ Path serverKeyFile = Paths.get("target/plugin_v2_server.key");
+ PemWriter.writePrivateKey(serverReq.getPrivateKey()).asPkcs8().encryptedAes128(password).toFile(serverKeyFile);
+
+ Path serverKeyStoreJksFile = Paths.get("target/plugin_v2_server.jks");
+ KeyStoreWriter.write(
+ KeyStoreCreator.jksForPrivateKeyAndCertificateChain(serverReq.getPrivateKey(), password, serverCert),
+ password, serverKeyStoreJksFile);
+
+ Path serverKeyStoreP12File = Paths.get("target/plugin_v2_server.p12");
+ KeyStoreWriter.write(
+ KeyStoreCreator.pkcs12ForPrivateKeyAndCertificateChain(serverReq.getPrivateKey(), password, serverCert),
+ password, serverKeyStoreP12File);
+
+ return List.of(caCertFile, caTrustStoreJksFile, caTrustStoreP12File, clientCertFile, clientKeyFile,
+ clientKeyStoreJksFile, clientKeyStoreP12File, serverCertFile, serverKeyFile, serverKeyStoreJksFile,
+ serverKeyStoreP12File);
+ }
+
+ private void deleteFile(Path path)
+ {
+ try
+ {
+ Files.deleteIfExists(path);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
}
\ No newline at end of file
diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/message/ContinueSendTestSend.java b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/message/ContinueSendTestSend.java
index 8a4000dca..c3dca08ef 100644
--- a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/message/ContinueSendTestSend.java
+++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/message/ContinueSendTestSend.java
@@ -4,5 +4,5 @@
public class ContinueSendTestSend implements MessageIntermediateThrowEvent
{
- // default implementation;
+ // default implementation
}
diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/ApiTest.java b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/ApiTest.java
index de2e193b9..a80b43184 100644
--- a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/ApiTest.java
+++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/ApiTest.java
@@ -94,4 +94,10 @@ public void apiGetTaskHelperNotNull(ProcessPluginApi api) throws Exception
{
expectNotNull(api.getTaskHelper());
}
+
+ @PluginTest
+ public void apiGetCryptoService(ProcessPluginApi api) throws Exception
+ {
+ expectNotNull(api.getCryptoService());
+ }
}
diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/CryptoServiceTest.java b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/CryptoServiceTest.java
new file mode 100644
index 000000000..5c25748a3
--- /dev/null
+++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/service/CryptoServiceTest.java
@@ -0,0 +1,438 @@
+package dev.dsf.bpe.test.service;
+
+import static dev.dsf.bpe.test.PluginTestExecutor.expectException;
+import static dev.dsf.bpe.test.PluginTestExecutor.expectFalse;
+import static dev.dsf.bpe.test.PluginTestExecutor.expectNotNull;
+import static dev.dsf.bpe.test.PluginTestExecutor.expectSame;
+import static dev.dsf.bpe.test.PluginTestExecutor.expectTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+import javax.net.ssl.SSLContext;
+
+import dev.dsf.bpe.test.AbstractTest;
+import dev.dsf.bpe.test.PluginTest;
+import dev.dsf.bpe.v2.ProcessPluginApi;
+import dev.dsf.bpe.v2.activity.ServiceTask;
+import dev.dsf.bpe.v2.error.ErrorBoundaryEvent;
+import dev.dsf.bpe.v2.service.CryptoService;
+import dev.dsf.bpe.v2.service.CryptoService.Kem;
+import dev.dsf.bpe.v2.variables.Variables;
+
+public class CryptoServiceTest extends AbstractTest implements ServiceTask
+{
+ // files created by dev.dsf.bpe.integration.PluginV2IntegrationTest
+ private static final Path CA_CERT_FILE = Paths.get("target/plugin_v2_ca.crt");
+ private static final Path CA_TRUST_STORE_JKS_FILE = Paths.get("target/plugin_v2_ca.jks");
+ private static final Path CA_TRUST_STORE_P12_FILE = Paths.get("target/plugin_v2_ca.p12");
+
+ private static final Path CLIENT_CERT_FILE = Paths.get("target/plugin_v2_client.crt");
+ private static final Path CLIENT_KEY_FILE = Paths.get("target/plugin_v2_client.key");
+ private static final Path CLIENT_KEY_STORE_JKS_FILE = Paths.get("target/plugin_v2_client.jks");
+ private static final Path CLIENT_KEY_STORE_P12_FILE = Paths.get("target/plugin_v2_client.p12");
+
+ private static final Path SERVER_CERT_FILE = Paths.get("target/plugin_v2_server.crt");
+ private static final Path SERVER_KEY_FILE = Paths.get("target/plugin_v2_server.key");
+ private static final Path SERVER_KEY_STORE_JKS_FILE = Paths.get("target/plugin_v2_server.jks");
+ private static final Path SERVER_KEY_STORE_P12_FILE = Paths.get("target/plugin_v2_server.p12");
+
+ private static final char[] PASSWORD = "password".toCharArray();
+
+ @Override
+ public void execute(ProcessPluginApi api, Variables variables) throws ErrorBoundaryEvent, Exception
+ {
+ executeTests(api, variables, api.getCryptoService());
+ }
+
+ @PluginTest
+ public void createEcDhKem(CryptoService cryptoService) throws Exception
+ {
+ expectNotNull(cryptoService.createEcDhKem());
+ }
+
+ @PluginTest
+ public void createEcDhKemCheckEncryptionDecryption(CryptoService cryptoService) throws Exception
+ {
+ Kem kem = cryptoService.createEcDhKem();
+
+ KeyPair keyPair = cryptoService.createKeyPairGeneratorX25519AndInitialize().generateKeyPair();
+ byte[] plainData = "Hello World".getBytes(StandardCharsets.UTF_8);
+
+ InputStream encrypted = kem.encrypt(new ByteArrayInputStream(plainData), keyPair.getPublic());
+ expectNotNull(encrypted);
+
+ byte[] encryptedData = encrypted.readAllBytes();
+ expectNotNull(encryptedData);
+ expectTrue(encryptedData.length > 12 + 2 + 1 + 1);
+
+ InputStream decryptedDataStream = kem.decrypt(new ByteArrayInputStream(encryptedData), keyPair.getPrivate());
+ byte[] decryptedData = decryptedDataStream.readAllBytes();
+ expectNotNull(decryptedData);
+ expectSame(plainData, decryptedData);
+ }
+
+ @PluginTest
+ public void createEcDhKemCheckEncryptionDecryptionByteArray(CryptoService cryptoService) throws Exception
+ {
+ Kem kem = cryptoService.createEcDhKem();
+
+ KeyPair keyPair = cryptoService.createKeyPairGeneratorX25519AndInitialize().generateKeyPair();
+ byte[] plainData = "Hello World".getBytes(StandardCharsets.UTF_8);
+
+ byte[] encryptedData = kem.encrypt(plainData, keyPair.getPublic());
+ expectNotNull(encryptedData);
+ expectTrue(encryptedData.length > 12 + 2 + 1 + 1);
+
+ byte[] decryptedData = kem.decrypt(encryptedData, keyPair.getPrivate());
+ expectNotNull(decryptedData);
+ expectSame(plainData, decryptedData);
+ }
+
+ @PluginTest
+ public void createKeyPairGeneratorRsa4096AndInitialize(CryptoService cryptoService) throws Exception
+ {
+ expectNotNull(cryptoService.createKeyPairGeneratorRsa4096AndInitialize());
+ }
+
+ @PluginTest
+ public void createKeyPairGeneratorSecp256r1AndInitialize(CryptoService cryptoService) throws Exception
+ {
+ expectNotNull(cryptoService.createKeyPairGeneratorSecp256r1AndInitialize());
+ }
+
+ @PluginTest
+ public void createKeyPairGeneratorSecp384r1AndInitialize(CryptoService cryptoService) throws Exception
+ {
+ expectNotNull(cryptoService.createKeyPairGeneratorSecp384r1AndInitialize());
+ }
+
+ @PluginTest
+ public void createKeyPairGeneratorSecp521r1AndInitialize(CryptoService cryptoService) throws Exception
+ {
+ expectNotNull(cryptoService.createKeyPairGeneratorSecp521r1AndInitialize());
+ }
+
+ @PluginTest
+ public void createKeyPairGeneratorX25519AndInitialize(CryptoService cryptoService) throws Exception
+ {
+ expectNotNull(cryptoService.createKeyPairGeneratorX25519AndInitialize());
+ }
+
+ @PluginTest
+ public void createKeyPairGeneratorX448AndInitialize(CryptoService cryptoService) throws Exception
+ {
+ expectNotNull(cryptoService.createKeyPairGeneratorX448AndInitialize());
+ }
+
+ @PluginTest
+ public void createKeyStoreForPrivateKeyAndCertificateChainCollection(CryptoService cryptoService) throws Exception
+ {
+ PrivateKey key = cryptoService.readPrivateKey(CLIENT_KEY_FILE, PASSWORD);
+ X509Certificate cert = cryptoService.readCertificate(CLIENT_CERT_FILE);
+ KeyStore store = cryptoService.createKeyStoreForPrivateKeyAndCertificateChain(key,
+ UUID.randomUUID().toString().toCharArray(), List.of(cert));
+ expectNotNull(store);
+ expectSame(1, Collections.list(store.aliases()).size());
+ }
+
+ @PluginTest
+ public void createKeyStoreForPrivateKeyAndCertificateChainVarargs(CryptoService cryptoService) throws Exception
+ {
+ PrivateKey key = cryptoService.readPrivateKey(SERVER_KEY_FILE, PASSWORD);
+ X509Certificate cert = cryptoService.readCertificate(SERVER_CERT_FILE);
+ KeyStore store = cryptoService.createKeyStoreForPrivateKeyAndCertificateChain(key,
+ UUID.randomUUID().toString().toCharArray(), cert);
+ expectNotNull(store);
+ expectSame(1, Collections.list(store.aliases()).size());
+ }
+
+ @PluginTest
+ public void createKeyStoreForTrustedCertificatesCollection(CryptoService cryptoService) throws Exception
+ {
+ X509Certificate cert = cryptoService.readCertificate(CA_CERT_FILE);
+ KeyStore store = cryptoService.createKeyStoreForTrustedCertificates(List.of(cert));
+ expectNotNull(store);
+ expectSame(1, Collections.list(store.aliases()).size());
+ }
+
+ @PluginTest
+ public void createKeyStoreForTrustedCertificatesVarargs(CryptoService cryptoService) throws Exception
+ {
+ X509Certificate cert = cryptoService.readCertificate(CA_CERT_FILE);
+ KeyStore store = cryptoService.createKeyStoreForTrustedCertificates(cert);
+ expectNotNull(store);
+ expectSame(1, Collections.list(store.aliases()).size());
+ }
+
+ @PluginTest
+ public void createRsaKem(CryptoService cryptoService) throws Exception
+ {
+ expectNotNull(cryptoService.createRsaKem());
+ }
+
+ @PluginTest
+ public void createRsaKemCheckEncryptionDecryption(CryptoService cryptoService) throws Exception
+ {
+ Kem kem = cryptoService.createRsaKem();
+
+ KeyPair keyPair = cryptoService.createKeyPairGeneratorRsa4096AndInitialize().generateKeyPair();
+ byte[] plainData = "Hello World".getBytes(StandardCharsets.UTF_8);
+
+ InputStream encrypted = kem.encrypt(new ByteArrayInputStream(plainData), keyPair.getPublic());
+ expectNotNull(encrypted);
+
+ byte[] encryptedData = encrypted.readAllBytes();
+ expectNotNull(encryptedData);
+ expectTrue(encryptedData.length > 12 + 2 + 1 + 1);
+
+ InputStream decryptedDataStream = kem.decrypt(new ByteArrayInputStream(encryptedData), keyPair.getPrivate());
+ byte[] decryptedData = decryptedDataStream.readAllBytes();
+
+ expectSame(plainData, decryptedData);
+ }
+
+ @PluginTest
+ public void createSSLContextTrustStore(CryptoService cryptoService) throws Exception
+ {
+ X509Certificate cert = cryptoService.readCertificate(CA_CERT_FILE);
+ KeyStore store = cryptoService.createKeyStoreForTrustedCertificates(cert);
+
+ SSLContext context = cryptoService.createSSLContext(store);
+ expectNotNull(context);
+ }
+
+ @PluginTest
+ public void createSSLContextTrustStoreKeyStore(CryptoService cryptoService) throws Exception
+ {
+ X509Certificate caCert = cryptoService.readCertificate(CA_CERT_FILE);
+ KeyStore trustStore = cryptoService.createKeyStoreForTrustedCertificates(caCert);
+
+ PrivateKey clientKey = cryptoService.readPrivateKey(CLIENT_KEY_FILE, PASSWORD);
+ X509Certificate clientCert = cryptoService.readCertificate(CLIENT_CERT_FILE);
+ char[] keyStorePassword = UUID.randomUUID().toString().toCharArray();
+ KeyStore keyStore = cryptoService.createKeyStoreForPrivateKeyAndCertificateChain(clientKey, keyStorePassword,
+ List.of(clientCert));
+
+ SSLContext context = cryptoService.createSSLContext(trustStore, keyStore, keyStorePassword);
+ expectNotNull(context);
+ }
+
+ @PluginTest
+ public void isCertificateExpired(CryptoService cryptoService) throws Exception
+ {
+ X509Certificate caCert = cryptoService.readCertificate(CA_CERT_FILE);
+ expectFalse(cryptoService.isCertificateExpired(caCert));
+ }
+
+ @PluginTest
+ public void isClientCertificate(CryptoService cryptoService) throws Exception
+ {
+ X509Certificate clientCert = cryptoService.readCertificate(CLIENT_CERT_FILE);
+ X509Certificate serverCert = cryptoService.readCertificate(SERVER_CERT_FILE);
+
+ expectTrue(cryptoService.isClientCertificate(clientCert));
+ expectFalse(cryptoService.isClientCertificate(serverCert));
+ }
+
+ @PluginTest
+ public void isKeyPair(CryptoService cryptoService) throws Exception
+ {
+ PrivateKey clientKey = cryptoService.readPrivateKey(CLIENT_KEY_FILE, PASSWORD);
+ X509Certificate clientCert = cryptoService.readCertificate(CLIENT_CERT_FILE);
+ PrivateKey serverKey = cryptoService.readPrivateKey(SERVER_KEY_FILE, PASSWORD);
+ X509Certificate serverCert = cryptoService.readCertificate(SERVER_CERT_FILE);
+
+ expectTrue(cryptoService.isKeyPair(clientKey, clientCert.getPublicKey()));
+ expectTrue(cryptoService.isKeyPair(serverKey, serverCert.getPublicKey()));
+
+ expectFalse(cryptoService.isKeyPair(clientKey, serverCert.getPublicKey()));
+ expectFalse(cryptoService.isKeyPair(serverKey, clientCert.getPublicKey()));
+ }
+
+ @PluginTest
+ public void isServerCertificate(CryptoService cryptoService) throws Exception
+ {
+ X509Certificate clientCert = cryptoService.readCertificate(CLIENT_CERT_FILE);
+ X509Certificate serverCert = cryptoService.readCertificate(SERVER_CERT_FILE);
+
+ expectFalse(cryptoService.isServerCertificate(clientCert));
+ expectTrue(cryptoService.isServerCertificate(serverCert));
+ }
+
+ @PluginTest
+ public void readCertificateInputStream(CryptoService cryptoService) throws Exception
+ {
+ try (InputStream in = Files.newInputStream(CA_CERT_FILE))
+ {
+ expectNotNull(cryptoService.readCertificate(in));
+ }
+ }
+
+ @PluginTest
+ public void readCertificatePath(CryptoService cryptoService) throws Exception
+ {
+ expectNotNull(cryptoService.readCertificate(CA_CERT_FILE));
+ }
+
+ @PluginTest
+ public void readCertificatesInputStream(CryptoService cryptoService) throws Exception
+ {
+ try (InputStream in = Files.newInputStream(CA_CERT_FILE))
+ {
+ List certs = cryptoService.readCertificates(in);
+ expectNotNull(certs);
+ expectSame(1, certs.size());
+ }
+ }
+
+ @PluginTest
+ public void readCertificatesPath(CryptoService cryptoService) throws Exception
+ {
+ List certs = cryptoService.readCertificates(CA_CERT_FILE);
+ expectNotNull(certs);
+ expectSame(1, certs.size());
+ }
+
+ @PluginTest
+ public void readKeyStoreJksInputStream(CryptoService cryptoService) throws Exception
+ {
+ try (InputStream in = Files.newInputStream(CLIENT_KEY_STORE_JKS_FILE))
+ {
+ expectNotNull(cryptoService.readKeyStoreJks(in, PASSWORD));
+ }
+ }
+
+ @PluginTest
+ public void readKeyStoreJksPath(CryptoService cryptoService) throws Exception
+ {
+ expectNotNull(cryptoService.readKeyStoreJks(SERVER_KEY_STORE_JKS_FILE, PASSWORD));
+ }
+
+ @PluginTest
+ public void readKeyStorePkcs12InputStream(CryptoService cryptoService) throws Exception
+ {
+ try (InputStream in = Files.newInputStream(CLIENT_KEY_STORE_P12_FILE))
+ {
+ cryptoService.readKeyStorePkcs12(in, PASSWORD);
+ }
+ }
+
+ @PluginTest
+ public void readKeyStorePkcs12Path(CryptoService cryptoService) throws Exception
+ {
+ expectNotNull(cryptoService.readKeyStorePkcs12(SERVER_KEY_STORE_P12_FILE, PASSWORD));
+ }
+
+ @PluginTest
+ public void readPrivateKeyInputStream(CryptoService cryptoService) throws Exception
+ {
+ expectException(NullPointerException.class, () ->
+ {
+ try (InputStream in = Files.newInputStream(CLIENT_KEY_FILE))
+ {
+ cryptoService.readPrivateKey(in);
+ }
+ });
+ }
+
+ @PluginTest
+ public void readPrivateKeyPath(CryptoService cryptoService) throws Exception
+ {
+ expectException(NullPointerException.class, () ->
+ {
+ cryptoService.readPrivateKey(SERVER_KEY_FILE);
+ });
+ }
+
+ @PluginTest
+ public void readPrivateKeyInputStreamCharArray(CryptoService cryptoService) throws Exception
+ {
+ try (InputStream in = Files.newInputStream(CLIENT_KEY_FILE))
+ {
+ expectNotNull(cryptoService.readPrivateKey(in, PASSWORD));
+ }
+ }
+
+ @PluginTest
+ public void readPrivateKeyPathCharArray(CryptoService cryptoService) throws Exception
+ {
+ expectNotNull(cryptoService.readPrivateKey(SERVER_KEY_FILE, PASSWORD));
+ }
+
+ @PluginTest
+ public void validateClientCertificateCollection(CryptoService cryptoService) throws Exception
+ {
+ KeyStore caTrustStore = cryptoService.readKeyStorePkcs12(CA_TRUST_STORE_P12_FILE, PASSWORD);
+ X509Certificate serverCert = cryptoService.readCertificate(SERVER_CERT_FILE);
+ KeyStore serverTrustStore = cryptoService.createKeyStoreForTrustedCertificates(serverCert);
+ X509Certificate clientCert = cryptoService.readCertificate(CLIENT_CERT_FILE);
+
+ cryptoService.validateClientCertificate(caTrustStore, List.of(clientCert));
+
+ expectException(CertificateException.class,
+ () -> cryptoService.validateClientCertificate(caTrustStore, List.of(serverCert)));
+ expectException(CertificateException.class,
+ () -> cryptoService.validateClientCertificate(serverTrustStore, List.of(clientCert)));
+ }
+
+ @PluginTest
+ public void vaildateClientCertificateVarArgs(CryptoService cryptoService) throws Exception
+ {
+ KeyStore caTrustStore = cryptoService.readKeyStorePkcs12(CA_TRUST_STORE_JKS_FILE, PASSWORD);
+ X509Certificate serverCert = cryptoService.readCertificate(SERVER_CERT_FILE);
+ KeyStore serverTrustStore = cryptoService.createKeyStoreForTrustedCertificates(serverCert);
+ X509Certificate clientCert = cryptoService.readCertificate(CLIENT_CERT_FILE);
+
+ cryptoService.validateClientCertificate(caTrustStore, clientCert);
+
+ expectException(CertificateException.class,
+ () -> cryptoService.validateClientCertificate(caTrustStore, serverCert));
+ expectException(CertificateException.class,
+ () -> cryptoService.validateClientCertificate(serverTrustStore, clientCert));
+ }
+
+ @PluginTest
+ public void vaildateServerCertificateCollection(CryptoService cryptoService) throws Exception
+ {
+ KeyStore caTrustStore = cryptoService.readKeyStorePkcs12(CA_TRUST_STORE_P12_FILE, PASSWORD);
+ X509Certificate serverCert = cryptoService.readCertificate(SERVER_CERT_FILE);
+ X509Certificate clientCert = cryptoService.readCertificate(CLIENT_CERT_FILE);
+ KeyStore clientTrustStore = cryptoService.createKeyStoreForTrustedCertificates(clientCert);
+
+ cryptoService.validateServerCertificate(caTrustStore, List.of(serverCert));
+
+ expectException(CertificateException.class,
+ () -> cryptoService.validateServerCertificate(caTrustStore, List.of(clientCert)));
+ expectException(CertificateException.class,
+ () -> cryptoService.validateServerCertificate(clientTrustStore, List.of(serverCert)));
+ }
+
+ @PluginTest
+ public void vaildateServerCertificateVarArgs(CryptoService cryptoService) throws Exception
+ {
+ KeyStore caTrustStore = cryptoService.readKeyStorePkcs12(CA_TRUST_STORE_JKS_FILE, PASSWORD);
+ X509Certificate serverCert = cryptoService.readCertificate(SERVER_CERT_FILE);
+ X509Certificate clientCert = cryptoService.readCertificate(CLIENT_CERT_FILE);
+ KeyStore clientTrustStore = cryptoService.createKeyStoreForTrustedCertificates(serverCert);
+
+ cryptoService.validateServerCertificate(caTrustStore, serverCert);
+
+ expectException(CertificateException.class,
+ () -> cryptoService.validateServerCertificate(caTrustStore, clientCert));
+ expectException(CertificateException.class,
+ () -> cryptoService.validateServerCertificate(clientTrustStore, serverCert));
+ }
+}
diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/spring/config/Config.java b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/spring/config/Config.java
index 9e9b002a8..2f18e87a9 100644
--- a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/spring/config/Config.java
+++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/java/dev/dsf/bpe/test/spring/config/Config.java
@@ -10,6 +10,7 @@
import dev.dsf.bpe.test.service.ApiTest;
import dev.dsf.bpe.test.service.ContinueSendTest;
import dev.dsf.bpe.test.service.ContinueSendTestEvaluate;
+import dev.dsf.bpe.test.service.CryptoServiceTest;
import dev.dsf.bpe.test.service.EndpointProviderTest;
import dev.dsf.bpe.test.service.ErrorBoundaryEventTestThrow;
import dev.dsf.bpe.test.service.ErrorBoundaryEventTestVerify;
@@ -34,6 +35,6 @@ public ActivityPrototypeBeanCreator activityPrototypeBeanCreator()
StartSendTaskTestListener.class, SendTaskTest.class, StartFieldInjectionTestListener.class,
FieldInjectionTest.class, ErrorBoundaryEventTestThrow.class, ErrorBoundaryEventTestVerify.class,
ExceptionTest.class, ContinueSendTest.class, ContinueSendTestSend.class, ContinueSendTestEvaluate.class,
- JsonVariableTestSet.class, JsonVariableTestGet.class);
+ JsonVariableTestSet.class, JsonVariableTestGet.class, CryptoServiceTest.class);
}
}
diff --git a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/resources/bpe/test.bpmn b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/resources/bpe/test.bpmn
index 44307bf9f..2e1aa6c61 100644
--- a/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/resources/bpe/test.bpmn
+++ b/dsf-bpe/dsf-bpe-test-plugin-v2/src/main/resources/bpe/test.bpmn
@@ -33,6 +33,7 @@
Flow_0jv5jil
Flow_0xzipbl
Flow_0kt0v58
+ Flow_1g6h0ul
@@ -54,6 +55,7 @@
Flow_1p9dw3m
Flow_1n773yf
Flow_0x6aoim
+ Flow_0b2eolg
Flow_0a1kwg9
@@ -235,6 +237,14 @@
${testActivity == 'JsonVariableTest'}
+
+ Flow_1g6h0ul
+ Flow_0b2eolg
+
+
+ ${testActivity == 'CryptoServiceTest'}
+
+
@@ -335,6 +345,10 @@
+
+
+
+
@@ -502,6 +516,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/dsf-bpe/dsf-bpe-test-plugin/src/main/java/dev/dsf/bpe/test/PluginTestExecutor.java b/dsf-bpe/dsf-bpe-test-plugin/src/main/java/dev/dsf/bpe/test/PluginTestExecutor.java
index 5684f8dd0..e4ae17d3c 100644
--- a/dsf-bpe/dsf-bpe-test-plugin/src/main/java/dev/dsf/bpe/test/PluginTestExecutor.java
+++ b/dsf-bpe/dsf-bpe-test-plugin/src/main/java/dev/dsf/bpe/test/PluginTestExecutor.java
@@ -23,6 +23,12 @@ public TestAssertException(String message)
}
}
+ @FunctionalInterface
+ public interface RunnableWithException
+ {
+ void run() throws Exception;
+ }
+
public static final void execute(Object testClass, Consumer addTestSucceededToStartTask,
Consumer addTestFailedToStartTask, Runnable updateStartTask, Object testMethodArg0,
Object testMethodArg1, Object... testMethodArgs)
@@ -188,7 +194,7 @@ private static TestAssertException createTestAssertExceptionNotSame(String type,
"Tested " + type + " is not same as expected [expected: " + expected + ", actual: " + actual + "]");
}
- public static void expectException(Class> expectedException, Runnable run)
+ public static void expectException(Class> expectedException, RunnableWithException run)
{
Objects.requireNonNull(expectedException, "expectedException");
Objects.requireNonNull(run, "run");