diff --git a/cli/src/main/java/dev/fitko/fitconnect/cli/util/CertTool.java b/cli/src/main/java/dev/fitko/fitconnect/cli/util/CertTool.java
new file mode 100644
index 0000000000000000000000000000000000000000..0f8dfbf918cb482fd94348012cfddc361ef9b25c
--- /dev/null
+++ b/cli/src/main/java/dev/fitko/fitconnect/cli/util/CertTool.java
@@ -0,0 +1,113 @@
+package dev.fitko.fitconnect.cli.util;
+
+import com.nimbusds.jose.jwk.JWK;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+public class CertTool {
+
+    public static final String OUTPUT_DIR = "outputDir";
+    private final Map<String, String> cmdArgs = new HashMap<>();
+
+    final JWKGenerator jwkGenerator;
+
+    public static void main(final String[] args) {
+
+        new CertTool(new JWKGenerator()).run(args);
+
+        /*final Map<String, Object> data = new LinkedHashMap<>();
+        data.put("senderConfig", Map.of("clientSecret", "", "clientId", ""));
+        data.put("subscriberConfig", Map.of("clientSecret", "", "clientId", "", "privateDecryptionKeyPaths", List.of(), "privateSigningKeyPath", ""));
+        data.put("activeEnvironment", DefaultEnvironments.TEST.getEnvironmentName().getName());
+
+        final DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        options.setPrettyFlow(true);
+
+        final Yaml yaml = new Yaml(options);
+        final StringWriter writer = new StringWriter();
+
+        yaml.dump(data, writer);
+        writer.flush();
+
+        System.out.println(writer);*/
+    }
+
+    public CertTool(final JWKGenerator jwkGenerator){
+        this.jwkGenerator = jwkGenerator;
+    }
+
+    private void run(final String[] args) {
+
+        mapArgs(args);
+
+        final String dir = getDir();
+
+        final JWKPair encryptionKeyPair = jwkGenerator.generateEncryptionKeyPair(4096);
+        final JWKPair signatureKeyPair = jwkGenerator.generateSignatureKeyPair(4096);
+
+        writeFile(Path.of(dir, "publicKey_encryption.json"), encryptionKeyPair.getPublicKey());
+        writeFile(Path.of(dir, "privateKey_decryption.json"), encryptionKeyPair.getPrivateKey());
+
+        writeFile(Path.of(dir, "publicKey_signature_verification.json"), signatureKeyPair.getPublicKey());
+        writeFile(Path.of(dir, "privateKey_signing.json"), signatureKeyPair.getPrivateKey());
+
+    }
+
+    private String getDir() {
+        if (cmdArgs.containsKey(OUTPUT_DIR)) {
+            return Path.of(cmdArgs.get(OUTPUT_DIR)).toAbsolutePath().toString();
+        } else {
+            return createTempDir();
+        }
+    }
+
+    private static String createTempDir() {
+        try {
+            return Files.createTempDirectory("testJWKs").toFile().getAbsolutePath();
+        } catch (final IOException e) {
+            System.out.println(e);
+            System.exit(0);
+        }
+        return null;
+    }
+
+    private static void writeFile(final Path path, final JWK jwk) {
+        try {
+            Files.write(path, toBytes(jwk));
+            System.out.println("Wrote " + path.getFileName() + " to " + path);
+        } catch (final IOException e) {
+            System.out.println(e);
+            System.exit(0);
+        }
+    }
+
+    private void mapArgs(final String[] args) {
+        if (args.length > 1) {
+            System.out.println("Invalid number of arguments \n Use: -o=OutputDirectory");
+            System.exit(0);
+        } else if (args.length == 1) {
+            final String[] s = args[0].split("=");
+            if(s.length != 2){
+                System.out.println("Invalid option " + args[0] + "\n Use: -o=OutputDirectory");
+                System.exit(0);
+            }
+            if (Objects.equals(s[0], "-o")) {
+                cmdArgs.put(OUTPUT_DIR, s[1]);
+            } else {
+                System.out.println("Invalid option " + s[0] + "\n Use: -o=OutputDirectory");
+                System.exit(0);
+            }
+        }
+    }
+
+    private static byte[] toBytes(final JWK jwk) {
+        return jwk.toJSONString().getBytes(StandardCharsets.UTF_8);
+    }
+}
diff --git a/cli/src/main/java/dev/fitko/fitconnect/cli/util/JWKGenerator.java b/cli/src/main/java/dev/fitko/fitconnect/cli/util/JWKGenerator.java
index a2ebe76e8d25816d205e3f465aa863af4f324574..df09d5c5e86de580bf2b01ab1bae8de10ba67ccc 100644
--- a/cli/src/main/java/dev/fitko/fitconnect/cli/util/JWKGenerator.java
+++ b/cli/src/main/java/dev/fitko/fitconnect/cli/util/JWKGenerator.java
@@ -1,11 +1,12 @@
 package dev.fitko.fitconnect.cli.util;
 
+import com.nimbusds.jose.Algorithm;
 import com.nimbusds.jose.JOSEException;
 import com.nimbusds.jose.JWEAlgorithm;
-import com.nimbusds.jose.jwk.JWK;
-import com.nimbusds.jose.jwk.JWKSet;
+import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.jose.jwk.KeyOperation;
 import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.jose.util.Base64;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.cert.X509v3CertificateBuilder;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
@@ -15,6 +16,7 @@ import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 
 import java.math.BigInteger;
+import java.security.InvalidParameterException;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.NoSuchAlgorithmException;
@@ -23,6 +25,7 @@ import java.security.cert.X509Certificate;
 import java.security.interfaces.RSAPublicKey;
 import java.time.Duration;
 import java.time.Instant;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.Set;
@@ -30,66 +33,96 @@ import java.util.UUID;
 
 /**
  * JWK Test Key Generator.
- *
- * Generates public and private keys for encryption and signing
+ * <p>
+ * Generates public and private keys for encryption and signing with.
  */
 public class JWKGenerator {
 
-    public static void main(final String[] args) throws NoSuchAlgorithmException, JOSEException, CertificateException, OperatorCreationException {
+    /**
+     * Generate a set of public encryption key and private decryption key.
+     *
+     * @param keySize size of the RSA key in bits
+     * @return JWKPair of public and private key
+     */
+    public JWKPair generateEncryptionKeyPair(final int keySize) {
+        final KeyPair keyPair = getKeyPair(keySize);
+        final List<Base64> x509CertChain = getX509CertChain(keyPair);
 
-        final JWKSet encryptionKeySet = getEncryptionKeySet();
+        final String keyId = UUID.randomUUID().toString();
+        final JWEAlgorithm encryptionAlgorithm = JWEAlgorithm.RSA_OAEP_256;
 
-    }
+        final RSAKey publicEncryptionKey = buildRSAKey(keyId, keyPair, KeyOperation.WRAP_KEY, encryptionAlgorithm, x509CertChain);
+        final RSAKey privateDecryptionKey = buildRSAKey(keyId, keyPair, KeyOperation.UNWRAP_KEY, encryptionAlgorithm);
 
-    private static JWKSet getEncryptionKeySet() throws NoSuchAlgorithmException, OperatorCreationException, CertificateException, JOSEException {
+        return new JWKPair(publicEncryptionKey.toPublicJWK(), privateDecryptionKey);
+    }
 
-        final KeyPair keyPair = getKeyPair();
-        final X509Certificate cert = getX509Certificate(keyPair);
+    /**
+     * Generate a set of public signature verification key and private signature key.
+     *
+     * @param keySize size of the RSA key in bits
+     * @return JWKPair of signature and verification key
+     */
+    public JWKPair generateSignatureKeyPair(final int keySize) {
+        final KeyPair keyPair = getKeyPair(keySize);
+        final List<Base64> x509CertChain = getX509CertChain(keyPair);
 
         final String keyId = UUID.randomUUID().toString();
+        final JWSAlgorithm signingAlgorithm = JWSAlgorithm.PS512;
 
-        final JWK publicKey = new RSAKey.Builder((RSAPublicKey)keyPair.getPublic())
-                .privateKey(keyPair.getPrivate())
-                .keyID(keyId)
-                .keyOperations(Set.of(KeyOperation.WRAP_KEY))
-                .algorithm(JWEAlgorithm.RSA_OAEP_256)
-                .x509CertChain(RSAKey.parse(cert).getX509CertChain())
-                .build();
+        final RSAKey privateSignatureKey = buildRSAKey(keyId, keyPair, KeyOperation.SIGN, signingAlgorithm);
+        final RSAKey publicSignatureVerificationKey = buildRSAKey(keyId, keyPair, KeyOperation.VERIFY, signingAlgorithm, x509CertChain);
+
+        return new JWKPair(publicSignatureVerificationKey.toPublicJWK(), privateSignatureKey);
+    }
+
+    private static List<Base64> getX509CertChain(final KeyPair keyPair) {
+        final X509Certificate cert = getX509Certificate(keyPair);
+        try {
+            return RSAKey.parse(cert).getX509CertChain();
+        } catch (final JOSEException e) {
+            throw new RuntimeException(e);
+        }
+    }
 
-        final JWK privateKey = new RSAKey.Builder((RSAPublicKey)keyPair.getPublic())
+    private static RSAKey buildRSAKey(final String keyId, final KeyPair keyPair, final KeyOperation keyOperation, final Algorithm algorithm, final List<Base64> x509CertChain) {
+        final RSAKey.Builder builder = new RSAKey.Builder((RSAPublicKey) keyPair.getPublic())
                 .privateKey(keyPair.getPrivate())
                 .keyID(keyId)
-                .keyOperations(Set.of(KeyOperation.UNWRAP_KEY))
-                .algorithm(JWEAlgorithm.RSA_OAEP_256)
-                .build();
+                .keyOperations(Set.of(keyOperation))
+                .algorithm(algorithm);
+        return x509CertChain.isEmpty() ? builder.build() : builder.x509CertChain(x509CertChain).build();
+    }
 
-        return new JWKSet(List.of(publicKey, privateKey));
+    private static RSAKey buildRSAKey(final String keyId, final KeyPair keyPair, final KeyOperation keyOperation, final Algorithm algorithm) {
+        return buildRSAKey(keyId, keyPair, keyOperation, algorithm, Collections.emptyList());
     }
 
-    private static X509Certificate getX509Certificate(final KeyPair keyPair) throws OperatorCreationException, CertificateException {
+    private static X509Certificate getX509Certificate(final KeyPair keyPair) {
         final Instant now = Instant.now();
         final Date notBefore = Date.from(now);
         final Date notAfter = Date.from(now.plus(Duration.ofDays(365 * 10)));
 
-        final ContentSigner contentSigner = new JcaContentSignerBuilder("SHA512withRSA").build(keyPair.getPrivate());
-        final X500Name x500Name = new X500Name("CN=localhost");
-        final X500Name x500Subject = new X500Name("C=Test");
-
-        final X509v3CertificateBuilder certificateBuilder =
-                new JcaX509v3CertificateBuilder(x500Name,
-                        BigInteger.valueOf(now.toEpochMilli()),
-                        notBefore,
-                        notAfter,
-                        x500Subject,
-                        keyPair.getPublic());
-
-        return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(contentSigner));
+        try {
+            final ContentSigner contentSigner = new JcaContentSignerBuilder("SHA512withRSA").build(keyPair.getPrivate());
+            final X500Name x500Name = new X500Name("CN=localhost");
+            final X500Name x500Subject = new X500Name("C=Test");
+            final BigInteger serialNr = BigInteger.valueOf(now.toEpochMilli());
+            final X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(x500Name, serialNr, notBefore, notAfter, x500Subject, keyPair.getPublic());
+            return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(contentSigner));
+        } catch (final CertificateException | OperatorCreationException e) {
+            throw new RuntimeException(e);
+        }
     }
 
-    private static KeyPair getKeyPair() throws NoSuchAlgorithmException {
-        final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
-        keyPairGenerator.initialize(4096);
-        return keyPairGenerator.generateKeyPair();
+    private static KeyPair getKeyPair(final int keySize) {
+        try {
+            final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+            keyPairGenerator.initialize(keySize);
+            return keyPairGenerator.generateKeyPair();
+        } catch (final NoSuchAlgorithmException | InvalidParameterException e) {
+            throw new RuntimeException(e);
+        }
     }
 
 }
diff --git a/cli/src/main/java/dev/fitko/fitconnect/cli/util/JWKPair.java b/cli/src/main/java/dev/fitko/fitconnect/cli/util/JWKPair.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c2f54ee3d4be872138c7df7200e251faeee954d
--- /dev/null
+++ b/cli/src/main/java/dev/fitko/fitconnect/cli/util/JWKPair.java
@@ -0,0 +1,11 @@
+package dev.fitko.fitconnect.cli.util;
+
+import com.nimbusds.jose.jwk.JWK;
+import lombok.Value;
+
+@Value
+public class JWKPair {
+
+    JWK publicKey;
+    JWK privateKey;
+}
diff --git a/cli/src/test/java/dev/fitko/fitconnect/cli/util/JWKGeneratorTest.java b/cli/src/test/java/dev/fitko/fitconnect/cli/util/JWKGeneratorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..437ed0fe64b94bae59dff01b585897a7195138b6
--- /dev/null
+++ b/cli/src/test/java/dev/fitko/fitconnect/cli/util/JWKGeneratorTest.java
@@ -0,0 +1,143 @@
+package dev.fitko.fitconnect.cli.util;
+
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.KeyOperation;
+import com.nimbusds.jose.jwk.KeyType;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.notNullValue;
+
+class JWKGeneratorTest {
+
+    private JWKGenerator underTest;
+
+    @BeforeEach
+    void setup() {
+        underTest = new JWKGenerator();
+    }
+
+    @Test
+    void testPublicEncryptionKey() {
+
+        // When
+        final JWKPair encryptionKeyPair = underTest.generateEncryptionKeyPair(2048);
+
+        final JWK publicKey = encryptionKeyPair.getPublicKey();
+
+        // Then
+        assertThat(publicKey, is(notNullValue()));
+
+
+        assertThat(publicKey.getKeyID(), is(notNullValue()));
+
+        assertThat(publicKey.getKeyOperations(), hasSize(1));
+        assertThat(publicKey.getKeyOperations(), contains(KeyOperation.WRAP_KEY));
+
+        assertThat(publicKey.getX509CertChain(), hasSize(1));
+
+        assertThat(publicKey.getKeyType(), is(KeyType.RSA));
+        assertThat( publicKey.getAlgorithm(), is(JWEAlgorithm.RSA_OAEP_256));
+    }
+
+    @Test
+    void testPrivateDecryptionKey() {
+
+        // When
+        final JWKPair encryptionKeyPair = underTest.generateEncryptionKeyPair(2048);
+
+        final JWK privateKey = encryptionKeyPair.getPrivateKey();
+
+        // Then
+        assertThat(privateKey, is(CoreMatchers.notNullValue()));
+
+        assertThat(privateKey.getKeyID(), is(notNullValue()));
+
+        assertThat(privateKey.getKeyOperations(), hasSize(1));
+        assertThat(privateKey.getKeyOperations(), contains(KeyOperation.UNWRAP_KEY));
+
+        assertThat(privateKey.getX509CertChain(), is(Matchers.nullValue()));
+
+        assertThat(privateKey.getKeyType(), is(KeyType.RSA));
+        assertThat(privateKey.getAlgorithm(), is(JWEAlgorithm.RSA_OAEP_256));
+
+        final Map<String, Object> keyParams = privateKey.toRSAKey().toJSONObject();
+
+        assertThat(keyParams.get("d"), is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("dp"), is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("dq"), is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("e"), is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("n"), is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("p"), is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("q"), is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("qi"), is(CoreMatchers.notNullValue()));
+    }
+
+    @Test
+    void testPublicSignatureVerificationKey() {
+
+        // When
+        final JWKPair signatureKeyPair = underTest.generateSignatureKeyPair(2048);
+
+        final JWK publicKey = signatureKeyPair.getPublicKey();
+
+        // Then
+        assertThat(publicKey, is(CoreMatchers.notNullValue()));
+
+        assertThat(publicKey.getKeyID(), is(notNullValue()));
+
+        assertThat(publicKey.getKeyOperations(), hasSize(1));
+        assertThat(publicKey.getKeyOperations(), contains(KeyOperation.VERIFY));
+
+        assertThat(publicKey.getX509CertChain(), hasSize(1));
+
+        assertThat(publicKey.getKeyType(), is(KeyType.RSA));
+        assertThat( publicKey.getAlgorithm(), is(JWSAlgorithm.PS512));
+
+    }
+
+    @Test
+    void testPrivateSigningKey() {
+
+        // When
+        final JWKPair signatureKeyPair = underTest.generateSignatureKeyPair(2048);
+
+        final JWK privateKey =  signatureKeyPair.getPrivateKey();
+
+        // Then
+        assertThat(privateKey, is(notNullValue()));
+
+
+        assertThat(privateKey.getKeyID(), is(notNullValue()));
+
+        assertThat(privateKey.getKeyOperations(), hasSize(1));
+        assertThat(privateKey.getKeyOperations(), contains(KeyOperation.SIGN));
+
+        assertThat(privateKey.getX509CertChain(), is(Matchers.nullValue()));
+
+        assertThat(privateKey.getKeyType(), is(KeyType.RSA));
+        assertThat(privateKey.getAlgorithm(), is(JWSAlgorithm.PS512));
+
+        final Map<String, Object> keyParams = privateKey.toRSAKey().toJSONObject();
+
+        assertThat(keyParams.get("d"), is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("dp"), is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("dq"), is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("e"), is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("n"), is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("p"), is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("q"), is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("qi"), is(CoreMatchers.notNullValue()));
+
+    }
+}
\ No newline at end of file