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
deleted file mode 100644
index 437ed0fe64b94bae59dff01b585897a7195138b6..0000000000000000000000000000000000000000
--- a/cli/src/test/java/dev/fitko/fitconnect/cli/util/JWKGeneratorTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-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
diff --git a/pom.xml b/pom.xml
index f73911f10c6d09aa20ccee9aa6239f51c06b7ef4..a6d343b607ef5835525e5ca38f6b06854d1dbeb0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -60,6 +60,7 @@
 
         <!-- 3rd party dependencies -->
         <nimbus.version>9.31</nimbus.version>
+        <bouncy-castle.version>1.74</bouncy-castle.version>
         <okhttp.version>4.11.0</okhttp.version>
 
         <jackson-databind.version>2.15.2</jackson-databind.version>
@@ -110,6 +111,7 @@
         <module>api</module>
         <module>core</module>
         <module>client</module>
+        <module>tools</module>
     </modules>
 
     <dependencyManagement>
@@ -134,6 +136,12 @@
                 <version>${project.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>dev.fitko.fitconnect.sdk</groupId>
+                <artifactId>tools</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
             <dependency>
                 <groupId>dev.fitko.fitconnect.sdk</groupId>
                 <artifactId>integration-tests</artifactId>
@@ -163,6 +171,16 @@
                 <artifactId>nimbus-jose-jwt</artifactId>
                 <version>${nimbus.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.bouncycastle</groupId>
+                <artifactId>bcprov-jdk18on</artifactId>
+                <version>${bouncy-castle.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.bouncycastle</groupId>
+                <artifactId>bcpkix-jdk18on</artifactId>
+                <version>${bouncy-castle.version}</version>
+            </dependency>
             <dependency>
                 <groupId>ch.qos.logback</groupId>
                 <artifactId>logback-classic</artifactId>
diff --git a/tools/pom.xml b/tools/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..529ce0ed8f75616593275aae59cc5dd090dc1954
--- /dev/null
+++ b/tools/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>sdk-java</artifactId>
+        <groupId>dev.fitko.fitconnect.sdk</groupId>
+        <version>1.1.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>tools</artifactId>
+    <packaging>jar</packaging>
+    <name>FIT-Connect Java SDK - Tools Lib</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.nimbusds</groupId>
+            <artifactId>nimbus-jose-jwt</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk18on</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpkix-jdk18on</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-all</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <descriptorRefs>
+                        <descriptorRef>jar-with-dependencies</descriptorRef>
+                    </descriptorRefs>
+                    <archive>
+                        <manifest>
+                            <mainClass>dev.fitko.fitconnect.sdk.tools.CertTool</mainClass>
+                        </manifest>
+                    </archive>
+                    <finalName>sdk-tools</finalName>
+                    <appendAssemblyId>false</appendAssemblyId>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>make-assembly</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/cli/src/main/java/dev/fitko/fitconnect/cli/util/CertTool.java b/tools/src/main/java/dev/fitko/fitconnect/sdk/tools/CertTool.java
similarity index 98%
rename from cli/src/main/java/dev/fitko/fitconnect/cli/util/CertTool.java
rename to tools/src/main/java/dev/fitko/fitconnect/sdk/tools/CertTool.java
index 0f8dfbf918cb482fd94348012cfddc361ef9b25c..c9b1e981b461fe7226690a7a21973eb46483f43f 100644
--- a/cli/src/main/java/dev/fitko/fitconnect/cli/util/CertTool.java
+++ b/tools/src/main/java/dev/fitko/fitconnect/sdk/tools/CertTool.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.cli.util;
+package dev.fitko.fitconnect.sdk.tools;
 
 import com.nimbusds.jose.jwk.JWK;
 
diff --git a/cli/src/main/java/dev/fitko/fitconnect/cli/util/JWKGenerator.java b/tools/src/main/java/dev/fitko/fitconnect/sdk/tools/JWKGenerator.java
similarity index 99%
rename from cli/src/main/java/dev/fitko/fitconnect/cli/util/JWKGenerator.java
rename to tools/src/main/java/dev/fitko/fitconnect/sdk/tools/JWKGenerator.java
index df09d5c5e86de580bf2b01ab1bae8de10ba67ccc..f907e029af66d683ea3f1320ab8ae8999946db28 100644
--- a/cli/src/main/java/dev/fitko/fitconnect/cli/util/JWKGenerator.java
+++ b/tools/src/main/java/dev/fitko/fitconnect/sdk/tools/JWKGenerator.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.cli.util;
+package dev.fitko.fitconnect.sdk.tools;
 
 import com.nimbusds.jose.Algorithm;
 import com.nimbusds.jose.JOSEException;
diff --git a/cli/src/main/java/dev/fitko/fitconnect/cli/util/JWKPair.java b/tools/src/main/java/dev/fitko/fitconnect/sdk/tools/JWKPair.java
similarity index 76%
rename from cli/src/main/java/dev/fitko/fitconnect/cli/util/JWKPair.java
rename to tools/src/main/java/dev/fitko/fitconnect/sdk/tools/JWKPair.java
index 6c2f54ee3d4be872138c7df7200e251faeee954d..d23721c2c1b92fdb192d4c4d39a76a3341f83534 100644
--- a/cli/src/main/java/dev/fitko/fitconnect/cli/util/JWKPair.java
+++ b/tools/src/main/java/dev/fitko/fitconnect/sdk/tools/JWKPair.java
@@ -1,4 +1,4 @@
-package dev.fitko.fitconnect.cli.util;
+package dev.fitko.fitconnect.sdk.tools;
 
 import com.nimbusds.jose.jwk.JWK;
 import lombok.Value;
diff --git a/tools/src/test/java/dev/fitko/fitconnect/sdk/tools/JWKGeneratorTest.java b/tools/src/test/java/dev/fitko/fitconnect/sdk/tools/JWKGeneratorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d27e7c386d28a4bfdfb4e41ab0fad455abf8a786
--- /dev/null
+++ b/tools/src/test/java/dev/fitko/fitconnect/sdk/tools/JWKGeneratorTest.java
@@ -0,0 +1,142 @@
+package dev.fitko.fitconnect.sdk.tools;
+
+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.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+
+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, CoreMatchers.is(notNullValue()));
+
+
+        assertThat(publicKey.getKeyID(), CoreMatchers.is(notNullValue()));
+
+        assertThat(publicKey.getKeyOperations(), hasSize(1));
+        assertThat(publicKey.getKeyOperations(), contains(KeyOperation.WRAP_KEY));
+
+        assertThat(publicKey.getX509CertChain(), hasSize(1));
+
+        assertThat(publicKey.getKeyType(), CoreMatchers.is(KeyType.RSA));
+        assertThat( publicKey.getAlgorithm(), CoreMatchers.is(JWEAlgorithm.RSA_OAEP_256));
+    }
+
+    @Test
+    void testPrivateDecryptionKey() {
+
+        // When
+        final JWKPair encryptionKeyPair = underTest.generateEncryptionKeyPair(2048);
+
+        final JWK privateKey = encryptionKeyPair.getPrivateKey();
+
+        // Then
+        assertThat(privateKey, CoreMatchers.is(CoreMatchers.notNullValue()));
+
+        assertThat(privateKey.getKeyID(), CoreMatchers.is(notNullValue()));
+
+        assertThat(privateKey.getKeyOperations(), hasSize(1));
+        assertThat(privateKey.getKeyOperations(), contains(KeyOperation.UNWRAP_KEY));
+
+        assertThat(privateKey.getX509CertChain(), CoreMatchers.is(nullValue()));
+
+        assertThat(privateKey.getKeyType(), CoreMatchers.is(KeyType.RSA));
+        assertThat(privateKey.getAlgorithm(), CoreMatchers.is(JWEAlgorithm.RSA_OAEP_256));
+
+        final Map<String, Object> keyParams = privateKey.toRSAKey().toJSONObject();
+
+        assertThat(keyParams.get("d"), CoreMatchers.is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("dp"), CoreMatchers.is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("dq"), CoreMatchers.is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("e"), CoreMatchers.is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("n"), CoreMatchers.is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("p"), CoreMatchers.is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("q"), CoreMatchers.is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("qi"), CoreMatchers.is(CoreMatchers.notNullValue()));
+    }
+
+    @Test
+    void testPublicSignatureVerificationKey() {
+
+        // When
+        final JWKPair signatureKeyPair = underTest.generateSignatureKeyPair(2048);
+
+        final JWK publicKey = signatureKeyPair.getPublicKey();
+
+        // Then
+        assertThat(publicKey, CoreMatchers.is(CoreMatchers.notNullValue()));
+
+        assertThat(publicKey.getKeyID(), CoreMatchers.is(notNullValue()));
+
+        assertThat(publicKey.getKeyOperations(), hasSize(1));
+        assertThat(publicKey.getKeyOperations(), contains(KeyOperation.VERIFY));
+
+        assertThat(publicKey.getX509CertChain(), hasSize(1));
+
+        assertThat(publicKey.getKeyType(), CoreMatchers.is(KeyType.RSA));
+        assertThat( publicKey.getAlgorithm(), CoreMatchers.is(JWSAlgorithm.PS512));
+
+    }
+
+    @Test
+    void testPrivateSigningKey() {
+
+        // When
+        final JWKPair signatureKeyPair = underTest.generateSignatureKeyPair(2048);
+
+        final JWK privateKey =  signatureKeyPair.getPrivateKey();
+
+        // Then
+        assertThat(privateKey, CoreMatchers.is(notNullValue()));
+
+
+        assertThat(privateKey.getKeyID(), CoreMatchers.is(notNullValue()));
+
+        assertThat(privateKey.getKeyOperations(), hasSize(1));
+        assertThat(privateKey.getKeyOperations(), contains(KeyOperation.SIGN));
+
+        assertThat(privateKey.getX509CertChain(), CoreMatchers.is(nullValue()));
+
+        assertThat(privateKey.getKeyType(), CoreMatchers.is(KeyType.RSA));
+        assertThat(privateKey.getAlgorithm(), CoreMatchers.is(JWSAlgorithm.PS512));
+
+        final Map<String, Object> keyParams = privateKey.toRSAKey().toJSONObject();
+
+        assertThat(keyParams.get("d"), CoreMatchers.is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("dp"), CoreMatchers.is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("dq"), CoreMatchers.is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("e"), CoreMatchers.is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("n"), CoreMatchers.is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("p"), CoreMatchers.is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("q"), CoreMatchers.is(CoreMatchers.notNullValue()));
+        assertThat(keyParams.get("qi"), CoreMatchers.is(CoreMatchers.notNullValue()));
+
+    }
+}
\ No newline at end of file