diff --git a/.gitignore b/.gitignore
index 6b0bf13b3c857d41b467d1bacc6634f5cbd386c8..4025da0208de7d028d455a6666c09d094687e1ae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -88,6 +88,5 @@ fabric.properties
 # Maven
 log/
 target/
-jams/
 testdb/
 tomcat*/
diff --git a/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java b/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java
new file mode 100644
index 0000000000000000000000000000000000000000..610e4e952e14a37ed899bbaeda2c538208d9f2e9
--- /dev/null
+++ b/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java
@@ -0,0 +1,99 @@
+package net.jami.jams.ad.connector;
+
+import com.imperva.ddc.core.query.Endpoint;
+import com.jsoniter.JsonIterator;
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.ad.connector.service.AuthenticationService;
+import net.jami.jams.common.authentication.AuthenticationSource;
+import net.jami.jams.common.authentication.AuthenticationSourceInfo;
+import net.jami.jams.common.authentication.AuthenticationSourceType;
+import net.jami.jams.common.authentication.activedirectory.ActiveDirectorySettings;
+import net.jami.jams.common.objects.user.User;
+import net.jami.jams.common.objects.user.UserProfile;
+import net.jami.jams.common.serialization.JsoniterRegistry;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+@Slf4j
+public class ADConnector implements AuthenticationSource {
+
+    private static final ConcurrentLinkedQueue<Endpoint> endpoints = new ConcurrentLinkedQueue<>();
+    private static ActiveDirectorySettings settings;
+    private AuthenticationService authenticationService = new AuthenticationService();
+
+    public ADConnector(String settings) {
+        JsoniterRegistry.initCodecs();
+        ADConnector.settings = JsonIterator.deserialize(settings,ActiveDirectorySettings.class);
+        for(int i=0; i<10;i++){
+            Endpoint endpoint = new Endpoint();
+            endpoint.setSecuredConnection(ADConnector.settings.getSsl());
+            endpoint.setPort(ADConnector.settings.getPort());
+            endpoint.setHost(ADConnector.settings.getHost());
+            endpoints.add(endpoint);
+        }
+        log.info("Started Active Directory Connector!");
+    }
+
+    public static Endpoint getConnection(){
+        Endpoint endpoint = null;
+        while(endpoint == null){
+            endpoint = endpoints.poll();
+        }
+        endpoint.setUserAccountName(settings.getUsername()); //* You can use the user's Distinguished Name as well
+        endpoint.setPassword(settings.getPassword());
+        return endpoint;
+    }
+
+    public static Endpoint getConnection(String username, String password){
+        Endpoint endpoint = null;
+        while(endpoint == null){
+            endpoint = endpoints.poll();
+        }
+        endpoint.setUserAccountName(ADConnector.settings.getRealm() + "\\" + username); //* You can use the user's Distinguished Name as well
+        endpoint.setPassword(password);
+        return endpoint;
+    }
+
+    public static void returnConnection(Endpoint connection){
+        endpoints.add(connection);
+    }
+
+
+    @Override
+    public boolean createUser(User user) {
+        return false;
+    }
+
+    @Override
+    public UserProfile getUserProfile(String username) {
+        return null;
+    }
+
+
+    @Override
+    public boolean authenticate(String username, String password) {
+        try {
+            return authenticationService.authenticateUser(username, password);
+        }
+        catch (Exception e){
+            return false;
+        }
+    }
+
+    @Override
+    public AuthenticationSourceInfo getInfo() {
+        return new AuthenticationSourceInfo(settings.getRealm(), AuthenticationSourceType.AD);
+    }
+
+    @Override
+    public boolean testConfiguration(String configuration) {
+        return false;
+    }
+
+    @Override
+    public boolean updatePassword(User user, String password) {
+        return false;
+    }
+
+
+}
diff --git a/ad-connector/src/main/java/net/jami/jams/ad/connector/service/AuthenticationService.java b/ad-connector/src/main/java/net/jami/jams/ad/connector/service/AuthenticationService.java
new file mode 100644
index 0000000000000000000000000000000000000000..deb25d253ab208af1b9592151e9edad976fc1b7c
--- /dev/null
+++ b/ad-connector/src/main/java/net/jami/jams/ad/connector/service/AuthenticationService.java
@@ -0,0 +1,25 @@
+package net.jami.jams.ad.connector.service;
+
+import com.imperva.ddc.core.query.ConnectionResponse;
+import com.imperva.ddc.core.query.Endpoint;
+import com.imperva.ddc.service.DirectoryConnectorService;
+import net.jami.jams.ad.connector.ADConnector;
+
+public class AuthenticationService {
+
+    public boolean authenticateUser(String username, String password) {
+        Endpoint endpoint = ADConnector.getConnection(username, password);
+        try {
+            ConnectionResponse connectionResponse = DirectoryConnectorService.authenticate(endpoint);
+            return !connectionResponse.isError();
+        }
+        catch (Exception e){
+            return false;
+        }
+        finally {
+            ADConnector.returnConnection(endpoint);
+        }
+    }
+
+
+}
diff --git a/ad-connector/src/main/java/net/jami/jams/ad/connector/service/UserProfileService.java b/ad-connector/src/main/java/net/jami/jams/ad/connector/service/UserProfileService.java
new file mode 100644
index 0000000000000000000000000000000000000000..be611eec61c396467ae7fed577b312e96a8b0074
--- /dev/null
+++ b/ad-connector/src/main/java/net/jami/jams/ad/connector/service/UserProfileService.java
@@ -0,0 +1,7 @@
+package net.jami.jams.ad.connector.service;
+
+public class UserProfileService {
+
+
+
+}
diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/CryptoEngine.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/CryptoEngine.java
new file mode 100644
index 0000000000000000000000000000000000000000..13500473cd9bacac2cbdb8b92d9fde5f008258d1
--- /dev/null
+++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/CryptoEngine.java
@@ -0,0 +1,85 @@
+package net.jami.jams.cryptoengine;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.common.cryptoengineapi.CertificateAuthority;
+import net.jami.jams.common.objects.devices.Device;
+import net.jami.jams.common.objects.requests.RevocationRequest;
+import net.jami.jams.common.objects.system.SystemAccount;
+import net.jami.jams.common.objects.user.User;
+import net.jami.jams.cryptoengine.workers.crl.CRLWorker;
+import net.jami.jams.cryptoengine.workers.csr.CertificateWorker;
+import net.jami.jams.cryptoengine.workers.ocsp.OCSPWorker;
+import org.bouncycastle.cert.X509CRLHolder;
+import org.bouncycastle.cert.ocsp.OCSPReq;
+import org.bouncycastle.cert.ocsp.OCSPResp;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.security.Security;
+import java.util.concurrent.atomic.AtomicReference;
+
+@Slf4j
+public class CryptoEngine implements CertificateAuthority {
+
+
+    //These are the workers which are responsible for CRL/OCSP, they have an odd relationship.
+    private static CRLWorker crlWorker;
+    private static OCSPWorker ocspWorker;
+    public static volatile String serverDomain;
+    public static volatile String signingAlgorithm;
+
+    //Various times
+    public static long caLifetime = 360_000_000;
+    public static long crlLifetime = 360_000_000;
+    public static long userLifetime = 360_000_000;
+    public static long deviceLifetime = 360_000_000;
+
+    //CA certificate & OCSP Certificates, because they are often used.
+    public static SystemAccount CA;
+    public static SystemAccount OCSP;
+
+    static {
+        Security.addProvider(new BouncyCastleProvider());
+    }
+
+    @Override
+    public void init(String domain, String signingAlgorithm, SystemAccount ca, SystemAccount ocsp) {
+        serverDomain = domain;
+        CA = ca;
+        OCSP = ocsp;
+        CryptoEngine.signingAlgorithm = signingAlgorithm;
+        crlWorker = new CRLWorker(CA.getPrivateKey(), CA.getCertificate());
+    }
+
+    @Override
+    public User getSignedCertificate(User user) {
+        return CertificateWorker.getSignedCertificate(user);
+    }
+
+    @Override
+    public Device getSignedCertificate(User user, Device device) {
+        return CertificateWorker.getSignedCertificate(user, device);
+    }
+
+    @Override
+    public SystemAccount getSignedCertificate(SystemAccount systemAccount) {
+        return CertificateWorker.getSignedCertificate(systemAccount);
+    }
+
+    @Override
+    public void revokeCertificate(RevocationRequest revocationRequest) {
+        crlWorker.getInput().add(revocationRequest);
+        synchronized (crlWorker.getInput()) {
+            crlWorker.getInput().notify();
+        }
+    }
+
+    @Override
+    public AtomicReference<X509CRLHolder> getLatestCRL() {
+        return crlWorker.getExistingCRL();
+    }
+
+    @Override
+    public OCSPResp getOCSPResponse(OCSPReq ocspRequest) {
+        return null;
+    }
+}
diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/X509Worker.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/X509Worker.java
new file mode 100644
index 0000000000000000000000000000000000000000..3746eba42336bcd0c9983670ad67645061185f45
--- /dev/null
+++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/X509Worker.java
@@ -0,0 +1,24 @@
+package net.jami.jams.cryptoengine.workers;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+@Getter
+@Setter
+@Slf4j
+public abstract class X509Worker<T> extends Thread{
+
+    private ConcurrentLinkedQueue<T> input = new ConcurrentLinkedQueue<>();
+    private PrivateKey signingKey;
+    private X509Certificate certificate;
+
+    public X509Worker(PrivateKey privateKey, X509Certificate certificate) {
+        this.signingKey = privateKey;
+        this.certificate = certificate;
+    }
+}
diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/crl/CRLWorker.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/crl/CRLWorker.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b9c05f7adff67e6cb79115bc4aa113f7319ddf7
--- /dev/null
+++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/crl/CRLWorker.java
@@ -0,0 +1,78 @@
+package net.jami.jams.cryptoengine.workers.crl;
+
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.common.objects.requests.RevocationRequest;
+import net.jami.jams.cryptoengine.CryptoEngine;
+import net.jami.jams.cryptoengine.workers.X509Worker;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.CRLReason;
+import org.bouncycastle.cert.X509CRLHolder;
+import org.bouncycastle.cert.X509v2CRLBuilder;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.concurrent.atomic.AtomicReference;
+
+@Slf4j
+public class CRLWorker extends X509Worker<RevocationRequest> {
+
+    @Getter
+    private AtomicReference<X509CRLHolder> existingCRL = null;
+
+    public CRLWorker(PrivateKey privateKey, X509Certificate certificate) {
+        super(privateKey, certificate);
+        this.setDaemon(true);
+        this.start();
+        log.info("Instantiated & started a CRL Worker...");
+    }
+
+    //Basically we just publish it in the CRL.
+    private void revokeCertificate(RevocationRequest revocationRequest){
+        try {
+            X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(new X500Name((getCertificate()).getSubjectDN().getName()), new Date());
+            if(revocationRequest != null) {
+                crlBuilder.addCRLEntry(revocationRequest.getIdentifier(), new Date(), CRLReason.privilegeWithdrawn);
+            }
+            if (existingCRL != null){
+                crlBuilder.addCRL(existingCRL.get());
+            }
+            else existingCRL = new AtomicReference<>();
+            existingCRL.set(crlBuilder.build(new JcaContentSignerBuilder("SHA512WITHRSA").setProvider("BC").build(getSigningKey())));
+            log.info("Successfully processed request for ID " + revocationRequest.getIdentifier());
+        }
+        catch (Exception e){
+            log.error("Failed to sign a CRL with error " + e.toString());
+        }
+    }
+
+    @Override
+    public void run() {
+        boolean needsRefresh = false;
+        while(true){
+            try{
+                while(getInput().isEmpty()){
+                    if(needsRefresh){
+                        revokeCertificate(null);
+                        needsRefresh = false;
+                    }
+                    synchronized (getInput()){
+                        getInput().wait(CryptoEngine.crlLifetime - 10_000);
+                        needsRefresh = true;
+                    }
+                }
+                while(!getInput().isEmpty()) {
+                    RevocationRequest revocationRequest = getInput().poll();
+                    revokeCertificate(revocationRequest);
+                    needsRefresh = false;
+                    log.info("Successfully revoked the certificated with ID " + revocationRequest.getIdentifier());
+                }
+            }
+            catch (Exception e){
+                log.error("An error has occured in the CRL signing thread: " + e.toString());
+            }
+        }
+    }
+}
diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/CertificateWorker.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/CertificateWorker.java
new file mode 100644
index 0000000000000000000000000000000000000000..150cb0dbb37b51905f018914301db7df00106cf5
--- /dev/null
+++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/CertificateWorker.java
@@ -0,0 +1,30 @@
+package net.jami.jams.cryptoengine.workers.csr;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.common.objects.devices.Device;
+import net.jami.jams.common.objects.system.SystemAccount;
+import net.jami.jams.common.objects.user.User;
+import net.jami.jams.cryptoengine.workers.csr.builders.DeviceBuilder;
+import net.jami.jams.cryptoengine.workers.csr.builders.SystemAccountBuilder;
+import net.jami.jams.cryptoengine.workers.csr.builders.UserBuilder;
+
+@Slf4j
+public class CertificateWorker {
+
+    //The CSR here is null because we generate a certificate and keypair.
+    public static SystemAccount getSignedCertificate(SystemAccount systemAccount) {
+        switch (systemAccount.getSystemAccountType()){
+            case CA: return SystemAccountBuilder.generateCA(systemAccount);
+            case OCSP: return SystemAccountBuilder.generateOCSP(systemAccount);
+            default: return null;
+        }
+    }
+
+    public static User getSignedCertificate(User user){
+        return UserBuilder.generateUser(user);
+    }
+
+    public static Device getSignedCertificate(User user, Device device){
+        return DeviceBuilder.generateDevice(user,device);
+    }
+}
diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/DeviceBuilder.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/DeviceBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c21eda83deaea4eff6ed9597679683026eeb308
--- /dev/null
+++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/DeviceBuilder.java
@@ -0,0 +1,39 @@
+package net.jami.jams.cryptoengine.workers.csr.builders;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.common.objects.devices.Device;
+import net.jami.jams.common.objects.user.User;
+import net.jami.jams.cryptoengine.CryptoEngine;
+import net.jami.jams.cryptoengine.workers.csr.utils.CertificateSigner;
+import net.jami.jams.cryptoengine.workers.csr.utils.ExtensionLibrary;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.Date;
+
+@Slf4j
+public class DeviceBuilder {
+
+    public static Device generateDevice(User user, Device device){
+        try {
+            X509v3CertificateBuilder builder = new X509v3CertificateBuilder(
+                    new JcaX509CertificateHolder(user.getCertificate()).getSubject(),
+                    new BigInteger(256, new SecureRandom()),
+                    new Date(System.currentTimeMillis()),
+                    new Date(System.currentTimeMillis() + CryptoEngine.deviceLifetime),
+                    device.getCertificationRequest().getSubject(),
+                    device.getCertificationRequest().getSubjectPublicKeyInfo()
+            );
+            device.setCertificate(CertificateSigner.signCertificate(user.getPrivateKey(),builder, ExtensionLibrary.deviceExtensions));
+            return device;
+        }
+        catch (Exception e){
+            log.error("Could not generate a user certificate: " + e.toString());
+            return null;
+        }
+    }
+
+
+}
diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/SystemAccountBuilder.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/SystemAccountBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..353ed47c7ea7ad620bfd2cd75396088e6cda3f2a
--- /dev/null
+++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/SystemAccountBuilder.java
@@ -0,0 +1,71 @@
+package net.jami.jams.cryptoengine.workers.csr.builders;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.common.objects.system.SystemAccount;
+import net.jami.jams.cryptoengine.CryptoEngine;
+import net.jami.jams.cryptoengine.workers.csr.utils.CertificateSigner;
+import net.jami.jams.cryptoengine.workers.csr.utils.ExtensionLibrary;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+import java.util.Date;
+
+@Slf4j
+public class SystemAccountBuilder {
+
+    //Self-signed because it is a CA
+    public static SystemAccount generateCA(SystemAccount systemAccount){
+        try {
+            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+            keyPairGenerator.initialize(4096);
+            KeyPair keyPair = keyPairGenerator.generateKeyPair();
+            X509v3CertificateBuilder builder = new X509v3CertificateBuilder(
+                    new X500Name("CN=" + systemAccount.getX509Fields().getDN()),
+                    new BigInteger(256, new SecureRandom()),
+                    new Date(System.currentTimeMillis()),
+                    new Date(System.currentTimeMillis() + CryptoEngine.caLifetime),
+                    new X500Name("CN="+ systemAccount.getX509Fields().getDN()),
+                    SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded())
+            );
+            systemAccount.setPrivateKey(keyPair.getPrivate());
+            systemAccount.setCertificate(CertificateSigner.signCertificate(keyPair.getPrivate(),builder, ExtensionLibrary.caExtensions));
+            return systemAccount;
+        }
+        catch (Exception e){
+            log.error("Could not generate the system's CA: " + e.toString());
+            return null;
+        }
+    }
+
+    //This is signed by the CA, the OCSP certificate has the same lifetime as the CA by default.
+    //someday this could change.
+    public static SystemAccount generateOCSP(SystemAccount systemAccount){
+        try {
+            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+            keyPairGenerator.initialize(4096);
+            KeyPair keyPair = keyPairGenerator.generateKeyPair();
+            X509v3CertificateBuilder builder = new X509v3CertificateBuilder(
+                    new JcaX509CertificateHolder(CryptoEngine.CA.getCertificate()).getSubject(),
+                    new BigInteger(256, new SecureRandom()),
+                    new Date(System.currentTimeMillis()),
+                    new Date(System.currentTimeMillis() + CryptoEngine.caLifetime),
+                    new X500Name("CN=" + systemAccount.getX509Fields().getDN()),
+                    SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded())
+            );
+            systemAccount.setPrivateKey(keyPair.getPrivate());
+            systemAccount.setCertificate(CertificateSigner.signCertificate(CryptoEngine.CA.getPrivateKey(), builder, ExtensionLibrary.caExtensions));
+            return systemAccount;
+        }
+        catch (Exception e){
+            log.error("Could not generate the system's OCSP: " + e.toString());
+            return null;
+        }
+    }
+
+}
diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/UserBuilder.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/UserBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ef3929fb4ba95219570fc94444333f16b614922
--- /dev/null
+++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/UserBuilder.java
@@ -0,0 +1,44 @@
+package net.jami.jams.cryptoengine.workers.csr.builders;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.common.objects.user.User;
+import net.jami.jams.cryptoengine.CryptoEngine;
+import net.jami.jams.cryptoengine.workers.csr.utils.CertificateSigner;
+import net.jami.jams.cryptoengine.workers.csr.utils.ExtensionLibrary;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+import java.util.Date;
+
+@Slf4j
+public class UserBuilder {
+
+    public static User generateUser(User user){
+        try {
+            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+            keyPairGenerator.initialize(4096);
+            KeyPair keyPair = keyPairGenerator.generateKeyPair();
+            X509v3CertificateBuilder builder = new X509v3CertificateBuilder(
+                    new JcaX509CertificateHolder(CryptoEngine.CA.getCertificate()).getSubject(),
+                    new BigInteger(256, new SecureRandom()),
+                    new Date(System.currentTimeMillis()),
+                    new Date(System.currentTimeMillis() + CryptoEngine.userLifetime),
+                    new X500Name(user.getX509Fields().getDN()),
+                    SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded())
+            );
+            user.setPrivateKey(keyPair.getPrivate());
+            user.setCertificate(CertificateSigner.signCertificate(CryptoEngine.CA.getPrivateKey(),builder, ExtensionLibrary.userExtensions));
+            return user;
+        }
+        catch (Exception e){
+            log.error("Could not generate a user certificate: " + e.toString());
+            return null;
+        }
+    }
+}
diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/CertificateExtendedData.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/CertificateExtendedData.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef1fb6b04d220aaaa2b7625ede1cfeb76e598880
--- /dev/null
+++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/CertificateExtendedData.java
@@ -0,0 +1,15 @@
+package net.jami.jams.cryptoengine.workers.csr.utils;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Getter
+@Setter
+public class CertificateExtendedData {
+    private List<Object[]> extensions = new ArrayList<>();
+}
+
+
diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/CertificateSigner.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/CertificateSigner.java
new file mode 100644
index 0000000000000000000000000000000000000000..a718c8b1a2040d12e83523781f95236d15b26e7e
--- /dev/null
+++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/CertificateSigner.java
@@ -0,0 +1,48 @@
+package net.jami.jams.cryptoengine.workers.csr.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.cryptoengine.CryptoEngine;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.util.PrivateKeyFactory;
+import org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
+
+import java.io.ByteArrayInputStream;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+@Slf4j
+public class CertificateSigner {
+
+    public static X509Certificate signCertificate(PrivateKey privateKey, X509v3CertificateBuilder certificateBuilder,
+                                                  CertificateExtendedData certificateExtendedData){
+        try {
+            //Appose the extended data from the template.
+            for(Object[] extensions : certificateExtendedData.getExtensions()){
+                certificateBuilder.addExtension((ASN1ObjectIdentifier) extensions[0],(boolean) extensions[1],(ASN1Encodable) extensions[2]);
+            }
+            //Initialize the signing.
+            AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(CryptoEngine.signingAlgorithm);
+            AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
+            AsymmetricKeyParameter asymmetricKeyParameter = PrivateKeyFactory.createKey(privateKey.getEncoded());
+            //Sign the certificate.
+            ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(asymmetricKeyParameter);
+            Certificate eeX509CertificateStructure = certificateBuilder.build(sigGen).toASN1Structure();
+            return (X509Certificate) new CertificateFactory().engineGenerateCertificate(new ByteArrayInputStream(eeX509CertificateStructure.getEncoded()));
+        }
+        catch (Exception e){
+            log.error("Could not sign a certificate with error: " + e.toString());
+            return null;
+        }
+    }
+
+
+}
diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/ExtensionLibrary.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/ExtensionLibrary.java
new file mode 100644
index 0000000000000000000000000000000000000000..f47130bae7f33d09a4f650ea6f7bd72150c2a5ca
--- /dev/null
+++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/ExtensionLibrary.java
@@ -0,0 +1,52 @@
+package net.jami.jams.cryptoengine.workers.csr.utils;
+
+import net.jami.jams.cryptoengine.CryptoEngine;
+import org.bouncycastle.asn1.x509.*;
+
+public class ExtensionLibrary {
+
+    public static CertificateExtendedData caExtensions = new CertificateExtendedData();
+    public static CertificateExtendedData userExtensions = new CertificateExtendedData();
+    public static CertificateExtendedData ocspExtensions = new CertificateExtendedData();
+    public static CertificateExtendedData deviceExtensions = new CertificateExtendedData();
+
+    private static final int SCHEMA = GeneralName.uniformResourceIdentifier;
+
+    static {
+        //Pre-Define the CRL Distribution Point
+        DistributionPoint[] distPoints = new DistributionPoint[1];
+        distPoints[0] = new DistributionPoint(new DistributionPointName(
+                new GeneralNames(new GeneralName(SCHEMA, CryptoEngine.serverDomain + "/api/auth/crl")))
+                , null, null
+        );
+
+        //Pre-Define the AIA Point
+        AccessDescription accessDescription = new AccessDescription(
+                AccessDescription.id_ad_ocsp,
+                new GeneralName(SCHEMA,CryptoEngine.serverDomain + "/api/auth/ocsp")
+        );
+
+        //CA Extensions.
+        caExtensions.getExtensions().add(new Object[]{Extension.basicConstraints, true, new BasicConstraints(10)});
+        caExtensions.getExtensions().add(new Object[]{Extension.keyUsage, false, new KeyUsage(KeyUsage.cRLSign | KeyUsage.keyCertSign)});
+
+        //OCSP Extensions.
+        ocspExtensions.getExtensions().add(new Object[]{Extension.basicConstraints, true, new BasicConstraints(false)});
+        ocspExtensions.getExtensions().add(new Object[]{Extension.extendedKeyUsage, false, new ExtendedKeyUsage(KeyPurposeId.id_kp_OCSPSigning)});
+        ocspExtensions.getExtensions().add(new Object[]{Extension.keyUsage, false, new KeyUsage(KeyUsage.nonRepudiation | KeyUsage.digitalSignature | KeyUsage.keyEncipherment)});
+
+        //User extensions (the user is a sub-CA)
+        userExtensions.getExtensions().add(new Object[]{Extension.basicConstraints, true, new BasicConstraints(10)});
+        userExtensions.getExtensions().add(new Object[]{Extension.keyUsage, false, new KeyUsage(KeyUsage.cRLSign | KeyUsage.keyCertSign)});
+        userExtensions.getExtensions().add(new Object[]{Extension.cRLDistributionPoints, false, new CRLDistPoint(distPoints)});
+        userExtensions.getExtensions().add(new Object[]{Extension.authorityInfoAccess, false, new AuthorityInformationAccess(accessDescription)});
+
+        //Device extensions
+        deviceExtensions.getExtensions().add(new Object[]{Extension.basicConstraints, true, new BasicConstraints(false)});
+        deviceExtensions.getExtensions().add(new Object[]{Extension.keyUsage, false, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.dataEncipherment | KeyUsage.keyAgreement | KeyUsage.nonRepudiation)});
+        deviceExtensions.getExtensions().add(new Object[]{Extension.cRLDistributionPoints, false, new CRLDistPoint(distPoints)});
+        deviceExtensions.getExtensions().add(new Object[]{Extension.authorityInfoAccess, false, new AuthorityInformationAccess(accessDescription)});
+    }
+
+
+}
diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/ocsp/OCSPWorker.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/ocsp/OCSPWorker.java
new file mode 100644
index 0000000000000000000000000000000000000000..87763b88febd2b01703b7c3ce1cb23c8d726fb13
--- /dev/null
+++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/ocsp/OCSPWorker.java
@@ -0,0 +1,16 @@
+package net.jami.jams.cryptoengine.workers.ocsp;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.cryptoengine.workers.X509Worker;
+
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+@Slf4j
+public class OCSPWorker extends X509Worker<String> {
+
+    public OCSPWorker(PrivateKey privateKey, X509Certificate certificate) {
+        super(privateKey, certificate);
+        log.info("Instantiated OCSP Worker...");
+    }
+}
diff --git a/cryptoengine/src/test/java/net/jami/jams/cryptoengine/workers/csr/builders/SystemAccountBuilderTest.java b/cryptoengine/src/test/java/net/jami/jams/cryptoengine/workers/csr/builders/SystemAccountBuilderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..37397a0bfa354c51a3a4cb07bfe1cec58eb80aef
--- /dev/null
+++ b/cryptoengine/src/test/java/net/jami/jams/cryptoengine/workers/csr/builders/SystemAccountBuilderTest.java
@@ -0,0 +1,105 @@
+package net.jami.jams.cryptoengine.workers.csr.builders;
+
+import net.jami.jams.common.authentication.AuthenticationSourceType;
+import net.jami.jams.common.objects.devices.Device;
+import net.jami.jams.common.objects.requests.RevocationRequest;
+import net.jami.jams.common.objects.requests.RevocationType;
+import net.jami.jams.common.objects.roots.X509Fields;
+import net.jami.jams.common.objects.system.SystemAccount;
+import net.jami.jams.common.objects.system.SystemAccountType;
+import net.jami.jams.common.objects.user.User;
+import net.jami.jams.common.utils.X509Utils;
+import net.jami.jams.cryptoengine.CryptoEngine;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.io.InputStream;
+import java.math.BigInteger;
+
+class SystemAccountBuilderTest {
+
+    private static String strPkcs10Request;
+
+    @BeforeAll
+    static void setUp() throws Exception{
+        CryptoEngine.serverDomain = "https://localhost";
+        CryptoEngine.signingAlgorithm = "SHA512WITHRSA";
+        InputStream path;
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        path = classLoader.getResourceAsStream("pkcs10request.txt");
+        strPkcs10Request = new String(path.readAllBytes());
+    }
+
+    @Test
+    void generateFullChain() {
+        SystemAccount caAccount = new SystemAccount();
+        caAccount.setSystemAccountType(SystemAccountType.CA);
+        caAccount.setX509Fields(new X509Fields());
+        caAccount.getX509Fields().setCommonName("Test CA");
+        caAccount.getX509Fields().setCountry("FR");
+        caAccount = SystemAccountBuilder.generateCA(caAccount);
+        Assertions.assertNotNull(caAccount.getCertificate(),"CA Certificate was not generated!");
+
+        CryptoEngine.CA = caAccount;
+
+        //Generate OCSP
+        SystemAccount ocspAccount = new SystemAccount();
+        ocspAccount.setSystemAccountType(SystemAccountType.OCSP);
+        ocspAccount.setX509Fields(new X509Fields());
+        ocspAccount.getX509Fields().setCommonName("OCSP Server");
+        ocspAccount = SystemAccountBuilder.generateOCSP(ocspAccount);
+        Assertions.assertNotNull(ocspAccount.getCertificate(),"OCSP Certificate was not generated!");
+
+        //Generate User.
+        User user = new User();
+        user.setUserType(AuthenticationSourceType.LOCAL);
+        user.setX509Fields(new X509Fields());
+        user.getX509Fields().setCommonName("Felix's Personal Certificate");
+        user = UserBuilder.generateUser(user);
+        Assertions.assertNotNull(user.getCertificate(),"User Certificate was not generated!");
+
+        //Generate a device
+        Device device = new Device();
+        device.setOwner("00000");
+        device.setCertificationRequest(X509Utils.getCSRFromString(strPkcs10Request));
+        device = DeviceBuilder.generateDevice(user,device);
+        Assertions.assertNotNull(device.getCertificate(),"Device certificate was not generated!");
+    }
+
+    @Test
+    void testCRLGeneration() throws Exception{
+        SystemAccount caAccount = new SystemAccount();
+        caAccount.setSystemAccountType(SystemAccountType.CA);
+        caAccount.setX509Fields(new X509Fields());
+        caAccount.getX509Fields().setCommonName("Test CA");
+        caAccount.getX509Fields().setCountry("FR");
+        caAccount = SystemAccountBuilder.generateCA(caAccount);
+        Assertions.assertNotNull(caAccount.getCertificate(),"CA Certificate was not generated!");
+
+        CryptoEngine cryptoEngine = new CryptoEngine();
+        cryptoEngine.init("http://localhost","SHA512WITHRSA",caAccount,null);
+        RevocationRequest revocationRequest = new RevocationRequest();
+        revocationRequest.setIdentifier(new BigInteger("91828882"));
+        revocationRequest.setRevocationType(RevocationType.USER);
+        cryptoEngine.revokeCertificate(revocationRequest);
+        synchronized (this){
+            this.wait(2_000);
+        }
+        Assertions.assertNotNull(cryptoEngine.getLatestCRL());
+        Assertions.assertEquals(cryptoEngine.getLatestCRL().get().getRevokedCertificates().toArray().length,1,"Expected only 1 certificate!");
+
+        revocationRequest = new RevocationRequest();
+        revocationRequest.setIdentifier(new BigInteger("17262653"));
+        revocationRequest.setRevocationType(RevocationType.USER);
+        cryptoEngine.revokeCertificate(revocationRequest);
+        synchronized (this){
+            this.wait(2_000);
+        }
+        Assertions.assertNotNull(cryptoEngine.getLatestCRL());
+        Assertions.assertEquals(cryptoEngine.getLatestCRL().get().getRevokedCertificates().toArray().length,2,"Expected only 2 certificates!");
+
+
+
+    }
+}
\ No newline at end of file
diff --git a/jami-dht/src/main/java/net/jami/jams/dht/DeviceReceiptGenerator.java b/jami-dht/src/main/java/net/jami/jams/dht/DeviceReceiptGenerator.java
new file mode 100644
index 0000000000000000000000000000000000000000..99f3698e77b5162c30460b988b13493724f2def6
--- /dev/null
+++ b/jami-dht/src/main/java/net/jami/jams/dht/DeviceReceiptGenerator.java
@@ -0,0 +1,125 @@
+package net.jami.jams.dht;
+
+/*
+ *  Copyright (c) 2019 Savoir-faire Linux Inc.
+ *
+ *  Author: Felix Sidokhine <felix.sidokhined@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.codec.digest.MessageDigestAlgorithms;
+import org.msgpack.core.MessageBufferPacker;
+import org.msgpack.core.MessagePack;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.stream.Collectors;
+
+@Slf4j
+public class DeviceReceiptGenerator {
+
+
+    private static void packToSign(MessageBufferPacker messagePack, PublicKey publicKey, byte[] deviceId) throws IOException {
+        byte[] owner = publicKey.getEncoded();
+        messagePack.packMapHeader(4);
+        messagePack.packString("seq");
+        messagePack.packInt(0);
+        messagePack.packString("owner");
+        messagePack.packBinaryHeader(owner.length);
+        messagePack.addPayload(owner);
+        messagePack.packString("type");
+        messagePack.packInt(0);
+        messagePack.packString("data");
+        //Inner message building.
+        MessageBufferPacker innerMessagePack = MessagePack.newDefaultBufferPacker();
+        innerMessagePack.packMapHeader(1);
+        innerMessagePack.packString("dev");
+        innerMessagePack.packBinaryHeader(deviceId.length);
+        innerMessagePack.addPayload(deviceId);
+        //Append the inner to the messagePack
+        messagePack.packBinaryHeader(innerMessagePack.toByteArray().length);
+        messagePack.addPayload(innerMessagePack.toByteArray());
+    }
+
+    private static byte[] generateAnnoucement(PrivateKey privateKey, PublicKey publicKey, PublicKey devicePubKey){
+        try {
+            MessageBufferPacker outerMessagePack = MessagePack.newDefaultBufferPacker();
+            MessageBufferPacker messagePack = MessagePack.newDefaultBufferPacker();
+            MessageDigest digest = MessageDigest.getInstance(MessageDigestAlgorithms.SHA_1);
+            byte[] deviceId = digest.digest(devicePubKey.getEncoded());
+            packToSign(messagePack, publicKey, deviceId);
+            byte[] owner = publicKey.getEncoded();
+
+            //Sign the message pack object using SHA-512 digest.
+            Signature signer = Signature.getInstance("SHA512withRSA");
+            signer.initSign(privateKey);
+            signer.update(messagePack.toByteArray());
+            byte[] signature = signer.sign();
+
+            //Build the outer enveloper.
+            outerMessagePack.packMapHeader(2);
+            outerMessagePack.packString("id");
+            outerMessagePack.packInt(0);
+            outerMessagePack.packString("dat");
+            outerMessagePack.packMapHeader(2);
+            outerMessagePack.packString("body");
+            packToSign(outerMessagePack, publicKey, deviceId);
+            outerMessagePack.packString("sig");
+            outerMessagePack.packBinaryHeader(signature.length);
+            outerMessagePack.addPayload(signature);
+
+            return outerMessagePack.toByteArray();
+        }
+        catch (Exception e){
+            log.error("Could not build the device receipt!");
+            return null;
+        }
+    }
+
+    public static String[] generateReceipt(PrivateKey privateKey, PublicKey publicKey, PublicKey devicePubKey, String ethAddress){
+        try {
+            HashMap<String, String> receipt = new LinkedHashMap<String, String>() {
+                @Override
+                public String toString() {
+                    return "{" + entrySet().stream().map(x -> "\"" + x.getKey() + "\":\"" + x.getValue() + "\"").collect(Collectors.joining(",")) + "}";
+                }
+            };
+            MessageDigest digest = MessageDigest.getInstance(MessageDigestAlgorithms.SHA_1);
+            receipt.put("id",Hex.encodeHexString(digest.digest(publicKey.getEncoded())));
+            receipt.put("dev",Hex.encodeHexString(digest.digest(devicePubKey.getEncoded())));
+            receipt.put("eth",ethAddress);
+            receipt.put("announce", Base64.encodeBase64String(generateAnnoucement(privateKey,publicKey,devicePubKey)));
+            Signature signer = Signature.getInstance("SHA512withRSA");
+            signer.initSign(privateKey);
+            signer.update(receipt.toString().getBytes());
+            byte[] signature = signer.sign();
+            return new String[]{receipt.toString(), java.util.Base64.getEncoder().encodeToString(signature)};
+        }
+        catch (Exception e){
+            log.error("Could not build generate device receipt!");
+            return null;
+        }
+
+    }
+
+}
diff --git a/jami-dht/src/main/java/net/jami/jams/dht/ETHAddressGenerator.java b/jami-dht/src/main/java/net/jami/jams/dht/ETHAddressGenerator.java
new file mode 100644
index 0000000000000000000000000000000000000000..4329865ff41504fad37a920679747e083678ebaa
--- /dev/null
+++ b/jami-dht/src/main/java/net/jami/jams/dht/ETHAddressGenerator.java
@@ -0,0 +1,61 @@
+package net.jami.jams.dht;
+
+/*
+ *  Copyright (c) 2019 Savoir-faire Linux Inc.
+ *
+ *  Author: Felix Sidokhine <felix.sidokhined@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+import net.jami.jams.dht.hashutils.HexBin;
+import net.jami.jams.dht.hashutils.Keccak256;
+import org.apache.commons.codec.binary.Hex;
+
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECGenParameterSpec;
+
+public class ETHAddressGenerator {
+
+
+    public static String[] generateAddress(){
+        try {
+            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC","SunEC");
+            keyGen.initialize(new ECGenParameterSpec("secp256k1"));
+            //Generate the Public and Private Keys
+            KeyPair pair = keyGen.generateKeyPair();
+            ECPrivateKey ecpriv = (ECPrivateKey) pair.getPrivate();
+            ECPublicKey ecpub  = (ECPublicKey)  pair.getPublic();
+
+            //Get the hex representations we need.
+            String hexPubKey = ecpub.getW().getAffineX().toString(16) + ecpub.getW().getAffineY().toString(16);
+            String hexPrvKey =  ecpriv.getS().toString(16);
+
+            //In ethereum the private key is just 0x + private key, so nothing to do here.
+            Keccak256 keccak256 = new Keccak256();
+            byte[] addressData = keccak256.digest(HexBin.decode(hexPubKey));
+            String address = Hex.encodeHexString(addressData);
+            address = "0x" + address.substring(24);
+            //Return the address and the private key - we just store them for now.
+            return new String[]{address,hexPrvKey};
+        }
+        catch (Exception e){
+            return null;
+        }
+    }
+
+
+}
diff --git a/jami-dht/src/main/java/net/jami/jams/dht/hashutils/Digest.java b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/Digest.java
new file mode 100755
index 0000000000000000000000000000000000000000..5eb3da19e8a2acfc7fafd4b402eeabc17ce9f7e8
--- /dev/null
+++ b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/Digest.java
@@ -0,0 +1,165 @@
+// $Id: Digest.java 232 2010-06-17 14:19:24Z tp $
+
+package net.jami.jams.dht.hashutils;
+
+/**
+ * <p>This interface documents the API for a hash function. This
+ * interface somewhat mimics the standard {@code
+ * java.security.MessageDigest} class. We do not extend that class in
+ * order to provide compatibility with reduced Java implementations such
+ * as J2ME. Implementing a {@code java.security.Provider} compatible
+ * with Sun's JCA ought to be easy.</p>
+ *
+ * <p>A {@code Digest} object maintains a running state for a hash
+ * function computation. Data is inserted with {@code update()} calls;
+ * the result is obtained from a {@code digest()} method (where some
+ * final data can be inserted as well). When a digest output has been
+ * produced, the objet is automatically resetted, and can be used
+ * immediately for another digest operation. The state of a computation
+ * can be cloned with the {@link #copy} method; this can be used to get
+ * a partial hash result without interrupting the complete
+ * computation.</p>
+ *
+ * <p>{@code Digest} objects are stateful and hence not thread-safe;
+ * however, distinct {@code Digest} objects can be accessed concurrently
+ * without any problem.</p>
+ *
+ * <pre>
+ * ==========================(LICENSE BEGIN)============================
+ *
+ * Copyright (c) 2007-2010  Projet RNRT SAPHIR
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ===========================(LICENSE END)=============================
+ * </pre>
+ *
+ * @version   $Revision: 232 $
+ * @author    Thomas Pornin &lt;thomas.pornin@cryptolog.com&gt;
+ */
+
+public interface Digest {
+
+	/**
+	 * Insert one more input data byte.
+	 *
+	 * @param in   the input byte
+	 */
+	public void update(byte in);
+
+	/**
+	 * Insert some more bytes.
+	 *
+	 * @param inbuf   the data bytes
+	 */
+	public void update(byte[] inbuf);
+
+	/**
+	 * Insert some more bytes.
+	 *
+	 * @param inbuf   the data buffer
+	 * @param off     the data offset in {@code inbuf}
+	 * @param len     the data length (in bytes)
+	 */
+	public void update(byte[] inbuf, int off, int len);
+
+	/**
+	 * Finalize the current hash computation and return the hash value
+	 * in a newly-allocated array. The object is resetted.
+	 *
+	 * @return  the hash output
+	 */
+	public byte[] digest();
+
+	/**
+	 * Input some bytes, then finalize the current hash computation
+	 * and return the hash value in a newly-allocated array. The object
+	 * is resetted.
+	 *
+	 * @param inbuf   the input data
+	 * @return  the hash output
+	 */
+	public byte[] digest(byte[] inbuf);
+
+	/**
+	 * Finalize the current hash computation and store the hash value
+	 * in the provided output buffer. The {@code len} parameter
+	 * contains the maximum number of bytes that should be written;
+	 * no more bytes than the natural hash function output length will
+	 * be produced. If {@code len} is smaller than the natural
+	 * hash output length, the hash output is truncated to its first
+	 * {@code len} bytes. The object is resetted.
+	 *
+	 * @param outbuf   the output buffer
+	 * @param off      the output offset within {@code outbuf}
+	 * @param len      the requested hash output length (in bytes)
+	 * @return  the number of bytes actually written in {@code outbuf}
+	 */
+	public int digest(byte[] outbuf, int off, int len);
+
+	/**
+	 * Get the natural hash function output length (in bytes).
+	 *
+	 * @return  the digest output length (in bytes)
+	 */
+	public int getDigestLength();
+
+	/**
+	 * Reset the object: this makes it suitable for a new hash
+	 * computation. The current computation, if any, is discarded.
+	 */
+	public void reset();
+
+	/**
+	 * Clone the current state. The returned object evolves independantly
+	 * of this object.
+	 *
+	 * @return  the clone
+	 */
+	public Digest copy();
+
+	/**
+	 * <p>Return the "block length" for the hash function. This
+	 * value is naturally defined for iterated hash functions
+	 * (Merkle-Damgard). It is used in HMAC (that's what the
+	 * <a href="http://tools.ietf.org/html/rfc2104">HMAC specification</a>
+	 * names the "{@code B}" parameter).</p>
+	 *
+	 * <p>If the function is "block-less" then this function may
+	 * return {@code -n} where {@code n} is an integer such that the
+	 * block length for HMAC ("{@code B}") will be inferred from the
+	 * key length, by selecting the smallest multiple of {@code n}
+	 * which is no smaller than the key length. For instance, for
+	 * the Fugue-xxx hash functions, this function returns -4: the
+	 * virtual block length B is the HMAC key length, rounded up to
+	 * the next multiple of 4.</p>
+	 *
+	 * @return  the internal block length (in bytes), or {@code -n}
+	 */
+	public int getBlockLength();
+
+	/**
+	 * <p>Get the display name for this function (e.g. {@code "SHA-1"}
+	 * for SHA-1).</p>
+	 *
+	 * @see Object
+	 */
+	public String toString();
+}
diff --git a/jami-dht/src/main/java/net/jami/jams/dht/hashutils/DigestEngine.java b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/DigestEngine.java
new file mode 100755
index 0000000000000000000000000000000000000000..bc9667ad704b429dc82bcf8fca99952a9f4c3778
--- /dev/null
+++ b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/DigestEngine.java
@@ -0,0 +1,266 @@
+// $Id: DigestEngine.java 229 2010-06-16 20:22:27Z tp $
+
+package net.jami.jams.dht.hashutils;
+
+/**
+ * <p>This class is a template which can be used to implement hash
+ * functions. It takes care of some of the API, and also provides an
+ * internal data buffer whose length is equal to the hash function
+ * internal block length.</p>
+ *
+ * <p>Classes which use this template MUST provide a working {@link
+ * #getBlockLength} method even before initialization (alternatively,
+ * they may define a custom {@link #getInternalBlockLength} which does
+ * not call {@link #getBlockLength}. The {@link #getDigestLength} should
+ * also be operational from the beginning, but it is acceptable that it
+ * returns 0 while the {@link #doInit} method has not been called
+ * yet.</p>
+ *
+ * <pre>
+ * ==========================(LICENSE BEGIN)============================
+ *
+ * Copyright (c) 2007-2010  Projet RNRT SAPHIR
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ===========================(LICENSE END)=============================
+ * </pre>
+ *
+ * @version   $Revision: 229 $
+ * @author    Thomas Pornin &lt;thomas.pornin@cryptolog.com&gt;
+ */
+
+public abstract class DigestEngine implements Digest {
+
+	/**
+	 * Reset the hash algorithm state.
+	 */
+	protected abstract void engineReset();
+
+	/**
+	 * Process one block of data.
+	 *
+	 * @param data   the data block
+	 */
+	protected abstract void processBlock(byte[] data);
+
+	/**
+	 * Perform the final padding and store the result in the
+	 * provided buffer. This method shall call {@link #flush}
+	 * and then {@link #update} with the appropriate padding
+	 * data in order to get the full input data.
+	 *
+	 * @param buf   the output buffer
+	 * @param off   the output offset
+	 */
+	protected abstract void doPadding(byte[] buf, int off);
+
+	/**
+	 * This function is called at object creation time; the
+	 * implementation should use it to perform initialization tasks.
+	 * After this method is called, the implementation should be ready
+	 * to process data or meaningfully honour calls such as
+	 * {@link #getDigestLength}</code>.
+	 */
+	protected abstract void doInit();
+
+	private int digestLen, blockLen, inputLen;
+	private byte[] inputBuf, outputBuf;
+	private long blockCount;
+
+	/**
+	 * Instantiate the engine.
+	 */
+	public DigestEngine()
+	{
+		doInit();
+		digestLen = getDigestLength();
+		blockLen = getInternalBlockLength();
+		inputBuf = new byte[blockLen];
+		outputBuf = new byte[digestLen];
+		inputLen = 0;
+		blockCount = 0;
+	}
+
+	private void adjustDigestLen()
+	{
+		if (digestLen == 0) {
+			digestLen = getDigestLength();
+			outputBuf = new byte[digestLen];
+		}
+	}
+
+	/** @see Digest */
+	public byte[] digest()
+	{
+		adjustDigestLen();
+		byte[] result = new byte[digestLen];
+		digest(result, 0, digestLen);
+		return result;
+	}
+
+	/** @see Digest */
+	public byte[] digest(byte[] input)
+	{
+		update(input, 0, input.length);
+		return digest();
+	}
+
+	/** @see Digest */
+	public int digest(byte[] buf, int offset, int len)
+	{
+		adjustDigestLen();
+		if (len >= digestLen) {
+			doPadding(buf, offset);
+			reset();
+			return digestLen;
+		} else {
+			doPadding(outputBuf, 0);
+			System.arraycopy(outputBuf, 0, buf, offset, len);
+			reset();
+			return len;
+		}
+	}
+
+	/** @see Digest */
+	public void reset()
+	{
+		engineReset();
+		inputLen = 0;
+		blockCount = 0;
+	}
+
+	/** @see Digest */
+	public void update(byte input)
+	{
+		inputBuf[inputLen ++] = (byte)input;
+		if (inputLen == blockLen) {
+			processBlock(inputBuf);
+			blockCount ++;
+			inputLen = 0;
+		}
+	}
+
+	/** @see Digest */
+	public void update(byte[] input)
+	{
+		update(input, 0, input.length);
+	}
+
+	/** @see Digest */
+	public void update(byte[] input, int offset, int len)
+	{
+		while (len > 0) {
+			int copyLen = blockLen - inputLen;
+			if (copyLen > len)
+				copyLen = len;
+			System.arraycopy(input, offset, inputBuf, inputLen,
+				copyLen);
+			offset += copyLen;
+			inputLen += copyLen;
+			len -= copyLen;
+			if (inputLen == blockLen) {
+				processBlock(inputBuf);
+				blockCount ++;
+				inputLen = 0;
+			}
+		}
+	}
+
+	/**
+	 * Get the internal block length. This is the length (in
+	 * bytes) of the array which will be passed as parameter to
+	 * {@link #processBlock}. The default implementation of this
+	 * method calls {@link #getBlockLength} and returns the same
+	 * value. Overriding this method is useful when the advertised
+	 * block length (which is used, for instance, by HMAC) is
+	 * suboptimal with regards to internal buffering needs.
+	 *
+	 * @return  the internal block length (in bytes)
+	 */
+	protected int getInternalBlockLength()
+	{
+		return getBlockLength();
+	}
+
+	/**
+	 * Flush internal buffers, so that less than a block of data
+	 * may at most be upheld.
+	 *
+	 * @return  the number of bytes still unprocessed after the flush
+	 */
+	protected final int flush()
+	{
+		return inputLen;
+	}
+
+	/**
+	 * Get a reference to an internal buffer with the same size
+	 * than a block. The contents of that buffer are defined only
+	 * immediately after a call to {@link #flush()}: if
+	 * {@link #flush()} return the value {@code n}, then the
+	 * first {@code n} bytes of the array returned by this method
+	 * are the {@code n} bytes of input data which are still
+	 * unprocessed. The values of the remaining bytes are
+	 * undefined and may be altered at will.
+	 *
+	 * @return  a block-sized internal buffer
+	 */
+	protected final byte[] getBlockBuffer()
+	{
+		return inputBuf;
+	}
+
+	/**
+	 * Get the "block count": this is the number of times the
+	 * {@link #processBlock} method has been invoked for the
+	 * current hash operation. That counter is incremented
+	 * <em>after</em> the call to {@link #processBlock}.
+	 *
+	 * @return  the block count
+	 */
+	protected long getBlockCount()
+	{
+		return blockCount;
+	}
+
+	/**
+	 * This function copies the internal buffering state to some
+	 * other instance of a class extending {@code DigestEngine}.
+	 * It returns a reference to the copy. This method is intended
+	 * to be called by the implementation of the {@link #copy}
+	 * method.
+	 *
+	 * @param dest   the copy
+	 * @return  the value {@code dest}
+	 */
+	protected Digest copyState(DigestEngine dest)
+	{
+		dest.inputLen = inputLen;
+		dest.blockCount = blockCount;
+		System.arraycopy(inputBuf, 0, dest.inputBuf, 0,
+			inputBuf.length);
+		adjustDigestLen();
+		dest.adjustDigestLen();
+		System.arraycopy(outputBuf, 0, dest.outputBuf, 0,
+			outputBuf.length);
+		return dest;
+	}
+}
diff --git a/jami-dht/src/main/java/net/jami/jams/dht/hashutils/HexBin.java b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/HexBin.java
new file mode 100755
index 0000000000000000000000000000000000000000..5dbd9610abb783d23b7a82a3cd2c167914fd7267
--- /dev/null
+++ b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/HexBin.java
@@ -0,0 +1,90 @@
+package net.jami.jams.dht.hashutils;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * format validation
+ *
+ * This class encodes/decodes hexadecimal data
+ *
+ * @xerces.internal
+ *
+ * @author Jeffrey Rodriguez
+ * @version $Id: HexBin.java 446747 2006-09-15 21:46:20Z mrglavas $
+ */
+public final class  HexBin {
+    static private final int  BASELENGTH   = 128;
+    static private final int  LOOKUPLENGTH = 16;
+    static final private byte [] hexNumberTable    = new byte[BASELENGTH];
+    static final private char [] lookUpHexAlphabet = new char[LOOKUPLENGTH];
+
+
+    static {
+        for (int i = 0; i < BASELENGTH; i++ ) {
+            hexNumberTable[i] = -1;
+        }
+        for ( int i = '9'; i >= '0'; i--) {
+            hexNumberTable[i] = (byte) (i-'0');
+        }
+        for ( int i = 'F'; i>= 'A'; i--) {
+            hexNumberTable[i] = (byte) ( i-'A' + 10 );
+        }
+        for ( int i = 'f'; i>= 'a'; i--) {
+            hexNumberTable[i] = (byte) ( i-'a' + 10 );
+        }
+
+        for(int i = 0; i<10; i++ ) {
+            lookUpHexAlphabet[i] = (char)('0'+i);
+        }
+        for(int i = 10; i<=15; i++ ) {
+            lookUpHexAlphabet[i] = (char)('A'+i -10);
+        }
+    }
+
+    /**
+     * Decode hex string to a byte array
+     *
+     * @param encoded encoded string
+     * @return return array of byte to encode
+     */
+    static public byte[] decode(String encoded) {
+        if (encoded == null)
+            return null;
+        int lengthData = encoded.length();
+        if (lengthData % 2 != 0)
+            return null;
+
+        char[] binaryData = encoded.toCharArray();
+        int lengthDecode = lengthData / 2;
+        byte[] decodedData = new byte[lengthDecode];
+        byte temp1, temp2;
+        char tempChar;
+        for( int i = 0; i<lengthDecode; i++ ){
+            tempChar = binaryData[i*2];
+            temp1 = (tempChar < BASELENGTH) ? hexNumberTable[tempChar] : -1;
+            if (temp1 == -1)
+                return null;
+            tempChar = binaryData[i*2+1];
+            temp2 = (tempChar < BASELENGTH) ? hexNumberTable[tempChar] : -1;
+            if (temp2 == -1)
+                return null;
+            decodedData[i] = (byte)((temp1 << 4) | temp2);
+        }
+        return decodedData;
+    }
+}
\ No newline at end of file
diff --git a/jami-dht/src/main/java/net/jami/jams/dht/hashutils/Keccak256.java b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/Keccak256.java
new file mode 100755
index 0000000000000000000000000000000000000000..0cc57c2a1e4884cb1e14e5bc8bf383da0f055c03
--- /dev/null
+++ b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/Keccak256.java
@@ -0,0 +1,59 @@
+package net.jami.jams.dht.hashutils;
+
+// $Id: Keccak256.java 189 2010-05-14 21:21:46Z tp $
+/**
+ * <p>This class implements the Keccak-256 digest algorithm under the
+ * {@link Digest} API.</p>
+ *
+ * <pre>
+ * ==========================(LICENSE BEGIN)============================
+ *
+ * Copyright (c) 2007-2010  Projet RNRT SAPHIR
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ===========================(LICENSE END)=============================
+ * </pre>
+ *
+ * @version   $Revision: 189 $
+ * @author    Thomas Pornin &lt;thomas.pornin@cryptolog.com&gt;
+ */
+
+public class Keccak256 extends KeccakCore {
+
+    /**
+     * Create the engine.
+     */
+    public Keccak256()
+    {
+    }
+
+    /** @see Digest */
+    public Digest copy()
+    {
+        return copyState(new Keccak256());
+    }
+
+    /** @see Digest */
+    public int getDigestLength()
+    {
+        return 32;
+    }
+}
\ No newline at end of file
diff --git a/jami-dht/src/main/java/net/jami/jams/dht/hashutils/KeccakCore.java b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/KeccakCore.java
new file mode 100755
index 0000000000000000000000000000000000000000..0c1473cece63d16dce96f36bcfa671bdfde346b2
--- /dev/null
+++ b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/KeccakCore.java
@@ -0,0 +1,585 @@
+// $Id: KeccakCore.java 258 2011-07-15 22:16:50Z tp $
+
+package net.jami.jams.dht.hashutils;
+
+/**
+ * This class implements the core operations for the Keccak digest
+ * algorithm.
+ *
+ * <pre>
+ * ==========================(LICENSE BEGIN)============================
+ *
+ * Copyright (c) 2007-2010  Projet RNRT SAPHIR
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ===========================(LICENSE END)=============================
+ * </pre>
+ *
+ * @version   $Revision: 258 $
+ * @author    Thomas Pornin &lt;thomas.pornin@cryptolog.com&gt;
+ */
+
+abstract class KeccakCore extends DigestEngine {
+
+    KeccakCore()
+    {
+    }
+
+    private long[] A;
+    private byte[] tmpOut;
+
+    private static final long[] RC = {
+            0x0000000000000001L, 0x0000000000008082L,
+            0x800000000000808AL, 0x8000000080008000L,
+            0x000000000000808BL, 0x0000000080000001L,
+            0x8000000080008081L, 0x8000000000008009L,
+            0x000000000000008AL, 0x0000000000000088L,
+            0x0000000080008009L, 0x000000008000000AL,
+            0x000000008000808BL, 0x800000000000008BL,
+            0x8000000000008089L, 0x8000000000008003L,
+            0x8000000000008002L, 0x8000000000000080L,
+            0x000000000000800AL, 0x800000008000000AL,
+            0x8000000080008081L, 0x8000000000008080L,
+            0x0000000080000001L, 0x8000000080008008L
+    };
+
+    /**
+     * Encode the 64-bit word {@code val} into the array
+     * {@code buf} at offset {@code off}, in little-endian
+     * convention (least significant byte first).
+     *
+     * @param val   the value to encode
+     * @param buf   the destination buffer
+     * @param off   the destination offset
+     */
+    private static final void encodeLELong(long val, byte[] buf, int off)
+    {
+        buf[off + 0] = (byte)val;
+        buf[off + 1] = (byte)(val >>> 8);
+        buf[off + 2] = (byte)(val >>> 16);
+        buf[off + 3] = (byte)(val >>> 24);
+        buf[off + 4] = (byte)(val >>> 32);
+        buf[off + 5] = (byte)(val >>> 40);
+        buf[off + 6] = (byte)(val >>> 48);
+        buf[off + 7] = (byte)(val >>> 56);
+    }
+
+    /**
+     * Decode a 64-bit little-endian word from the array {@code buf}
+     * at offset {@code off}.
+     *
+     * @param buf   the source buffer
+     * @param off   the source offset
+     * @return  the decoded value
+     */
+    private static final long decodeLELong(byte[] buf, int off)
+    {
+        return (buf[off + 0] & 0xFFL)
+                | ((buf[off + 1] & 0xFFL) << 8)
+                | ((buf[off + 2] & 0xFFL) << 16)
+                | ((buf[off + 3] & 0xFFL) << 24)
+                | ((buf[off + 4] & 0xFFL) << 32)
+                | ((buf[off + 5] & 0xFFL) << 40)
+                | ((buf[off + 6] & 0xFFL) << 48)
+                | ((buf[off + 7] & 0xFFL) << 56);
+    }
+
+    /** @see DigestEngine */
+    protected void engineReset()
+    {
+        doReset();
+    }
+
+    /** @see DigestEngine */
+    protected void processBlock(byte[] data)
+    {
+		/* Input block */
+        for (int i = 0; i < data.length; i += 8)
+            A[i >>> 3] ^= decodeLELong(data, i);
+
+        long t0, t1, t2, t3, t4;
+        long tt0, tt1, tt2, tt3, tt4;
+        long t, kt;
+        long c0, c1, c2, c3, c4, bnn;
+
+		/*
+		 * Unrolling four rounds kills performance big time
+		 * on Intel x86 Core2, in both 32-bit and 64-bit modes
+		 * (less than 1 MB/s instead of 55 MB/s on x86-64).
+		 * Unrolling two rounds appears to be fine.
+		 */
+        for (int j = 0; j < 24; j += 2) {
+
+            tt0 = A[ 1] ^ A[ 6];
+            tt1 = A[11] ^ A[16];
+            tt0 ^= A[21] ^ tt1;
+            tt0 = (tt0 << 1) | (tt0 >>> 63);
+            tt2 = A[ 4] ^ A[ 9];
+            tt3 = A[14] ^ A[19];
+            tt0 ^= A[24];
+            tt2 ^= tt3;
+            t0 = tt0 ^ tt2;
+
+            tt0 = A[ 2] ^ A[ 7];
+            tt1 = A[12] ^ A[17];
+            tt0 ^= A[22] ^ tt1;
+            tt0 = (tt0 << 1) | (tt0 >>> 63);
+            tt2 = A[ 0] ^ A[ 5];
+            tt3 = A[10] ^ A[15];
+            tt0 ^= A[20];
+            tt2 ^= tt3;
+            t1 = tt0 ^ tt2;
+
+            tt0 = A[ 3] ^ A[ 8];
+            tt1 = A[13] ^ A[18];
+            tt0 ^= A[23] ^ tt1;
+            tt0 = (tt0 << 1) | (tt0 >>> 63);
+            tt2 = A[ 1] ^ A[ 6];
+            tt3 = A[11] ^ A[16];
+            tt0 ^= A[21];
+            tt2 ^= tt3;
+            t2 = tt0 ^ tt2;
+
+            tt0 = A[ 4] ^ A[ 9];
+            tt1 = A[14] ^ A[19];
+            tt0 ^= A[24] ^ tt1;
+            tt0 = (tt0 << 1) | (tt0 >>> 63);
+            tt2 = A[ 2] ^ A[ 7];
+            tt3 = A[12] ^ A[17];
+            tt0 ^= A[22];
+            tt2 ^= tt3;
+            t3 = tt0 ^ tt2;
+
+            tt0 = A[ 0] ^ A[ 5];
+            tt1 = A[10] ^ A[15];
+            tt0 ^= A[20] ^ tt1;
+            tt0 = (tt0 << 1) | (tt0 >>> 63);
+            tt2 = A[ 3] ^ A[ 8];
+            tt3 = A[13] ^ A[18];
+            tt0 ^= A[23];
+            tt2 ^= tt3;
+            t4 = tt0 ^ tt2;
+
+            A[ 0] = A[ 0] ^ t0;
+            A[ 5] = A[ 5] ^ t0;
+            A[10] = A[10] ^ t0;
+            A[15] = A[15] ^ t0;
+            A[20] = A[20] ^ t0;
+            A[ 1] = A[ 1] ^ t1;
+            A[ 6] = A[ 6] ^ t1;
+            A[11] = A[11] ^ t1;
+            A[16] = A[16] ^ t1;
+            A[21] = A[21] ^ t1;
+            A[ 2] = A[ 2] ^ t2;
+            A[ 7] = A[ 7] ^ t2;
+            A[12] = A[12] ^ t2;
+            A[17] = A[17] ^ t2;
+            A[22] = A[22] ^ t2;
+            A[ 3] = A[ 3] ^ t3;
+            A[ 8] = A[ 8] ^ t3;
+            A[13] = A[13] ^ t3;
+            A[18] = A[18] ^ t3;
+            A[23] = A[23] ^ t3;
+            A[ 4] = A[ 4] ^ t4;
+            A[ 9] = A[ 9] ^ t4;
+            A[14] = A[14] ^ t4;
+            A[19] = A[19] ^ t4;
+            A[24] = A[24] ^ t4;
+            A[ 5] = (A[ 5] << 36) | (A[ 5] >>> (64 - 36));
+            A[10] = (A[10] << 3) | (A[10] >>> (64 - 3));
+            A[15] = (A[15] << 41) | (A[15] >>> (64 - 41));
+            A[20] = (A[20] << 18) | (A[20] >>> (64 - 18));
+            A[ 1] = (A[ 1] << 1) | (A[ 1] >>> (64 - 1));
+            A[ 6] = (A[ 6] << 44) | (A[ 6] >>> (64 - 44));
+            A[11] = (A[11] << 10) | (A[11] >>> (64 - 10));
+            A[16] = (A[16] << 45) | (A[16] >>> (64 - 45));
+            A[21] = (A[21] << 2) | (A[21] >>> (64 - 2));
+            A[ 2] = (A[ 2] << 62) | (A[ 2] >>> (64 - 62));
+            A[ 7] = (A[ 7] << 6) | (A[ 7] >>> (64 - 6));
+            A[12] = (A[12] << 43) | (A[12] >>> (64 - 43));
+            A[17] = (A[17] << 15) | (A[17] >>> (64 - 15));
+            A[22] = (A[22] << 61) | (A[22] >>> (64 - 61));
+            A[ 3] = (A[ 3] << 28) | (A[ 3] >>> (64 - 28));
+            A[ 8] = (A[ 8] << 55) | (A[ 8] >>> (64 - 55));
+            A[13] = (A[13] << 25) | (A[13] >>> (64 - 25));
+            A[18] = (A[18] << 21) | (A[18] >>> (64 - 21));
+            A[23] = (A[23] << 56) | (A[23] >>> (64 - 56));
+            A[ 4] = (A[ 4] << 27) | (A[ 4] >>> (64 - 27));
+            A[ 9] = (A[ 9] << 20) | (A[ 9] >>> (64 - 20));
+            A[14] = (A[14] << 39) | (A[14] >>> (64 - 39));
+            A[19] = (A[19] << 8) | (A[19] >>> (64 - 8));
+            A[24] = (A[24] << 14) | (A[24] >>> (64 - 14));
+            bnn = ~A[12];
+            kt = A[ 6] | A[12];
+            c0 = A[ 0] ^ kt;
+            kt = bnn | A[18];
+            c1 = A[ 6] ^ kt;
+            kt = A[18] & A[24];
+            c2 = A[12] ^ kt;
+            kt = A[24] | A[ 0];
+            c3 = A[18] ^ kt;
+            kt = A[ 0] & A[ 6];
+            c4 = A[24] ^ kt;
+            A[ 0] = c0;
+            A[ 6] = c1;
+            A[12] = c2;
+            A[18] = c3;
+            A[24] = c4;
+            bnn = ~A[22];
+            kt = A[ 9] | A[10];
+            c0 = A[ 3] ^ kt;
+            kt = A[10] & A[16];
+            c1 = A[ 9] ^ kt;
+            kt = A[16] | bnn;
+            c2 = A[10] ^ kt;
+            kt = A[22] | A[ 3];
+            c3 = A[16] ^ kt;
+            kt = A[ 3] & A[ 9];
+            c4 = A[22] ^ kt;
+            A[ 3] = c0;
+            A[ 9] = c1;
+            A[10] = c2;
+            A[16] = c3;
+            A[22] = c4;
+            bnn = ~A[19];
+            kt = A[ 7] | A[13];
+            c0 = A[ 1] ^ kt;
+            kt = A[13] & A[19];
+            c1 = A[ 7] ^ kt;
+            kt = bnn & A[20];
+            c2 = A[13] ^ kt;
+            kt = A[20] | A[ 1];
+            c3 = bnn ^ kt;
+            kt = A[ 1] & A[ 7];
+            c4 = A[20] ^ kt;
+            A[ 1] = c0;
+            A[ 7] = c1;
+            A[13] = c2;
+            A[19] = c3;
+            A[20] = c4;
+            bnn = ~A[17];
+            kt = A[ 5] & A[11];
+            c0 = A[ 4] ^ kt;
+            kt = A[11] | A[17];
+            c1 = A[ 5] ^ kt;
+            kt = bnn | A[23];
+            c2 = A[11] ^ kt;
+            kt = A[23] & A[ 4];
+            c3 = bnn ^ kt;
+            kt = A[ 4] | A[ 5];
+            c4 = A[23] ^ kt;
+            A[ 4] = c0;
+            A[ 5] = c1;
+            A[11] = c2;
+            A[17] = c3;
+            A[23] = c4;
+            bnn = ~A[ 8];
+            kt = bnn & A[14];
+            c0 = A[ 2] ^ kt;
+            kt = A[14] | A[15];
+            c1 = bnn ^ kt;
+            kt = A[15] & A[21];
+            c2 = A[14] ^ kt;
+            kt = A[21] | A[ 2];
+            c3 = A[15] ^ kt;
+            kt = A[ 2] & A[ 8];
+            c4 = A[21] ^ kt;
+            A[ 2] = c0;
+            A[ 8] = c1;
+            A[14] = c2;
+            A[15] = c3;
+            A[21] = c4;
+            A[ 0] = A[ 0] ^ RC[j + 0];
+
+            tt0 = A[ 6] ^ A[ 9];
+            tt1 = A[ 7] ^ A[ 5];
+            tt0 ^= A[ 8] ^ tt1;
+            tt0 = (tt0 << 1) | (tt0 >>> 63);
+            tt2 = A[24] ^ A[22];
+            tt3 = A[20] ^ A[23];
+            tt0 ^= A[21];
+            tt2 ^= tt3;
+            t0 = tt0 ^ tt2;
+
+            tt0 = A[12] ^ A[10];
+            tt1 = A[13] ^ A[11];
+            tt0 ^= A[14] ^ tt1;
+            tt0 = (tt0 << 1) | (tt0 >>> 63);
+            tt2 = A[ 0] ^ A[ 3];
+            tt3 = A[ 1] ^ A[ 4];
+            tt0 ^= A[ 2];
+            tt2 ^= tt3;
+            t1 = tt0 ^ tt2;
+
+            tt0 = A[18] ^ A[16];
+            tt1 = A[19] ^ A[17];
+            tt0 ^= A[15] ^ tt1;
+            tt0 = (tt0 << 1) | (tt0 >>> 63);
+            tt2 = A[ 6] ^ A[ 9];
+            tt3 = A[ 7] ^ A[ 5];
+            tt0 ^= A[ 8];
+            tt2 ^= tt3;
+            t2 = tt0 ^ tt2;
+
+            tt0 = A[24] ^ A[22];
+            tt1 = A[20] ^ A[23];
+            tt0 ^= A[21] ^ tt1;
+            tt0 = (tt0 << 1) | (tt0 >>> 63);
+            tt2 = A[12] ^ A[10];
+            tt3 = A[13] ^ A[11];
+            tt0 ^= A[14];
+            tt2 ^= tt3;
+            t3 = tt0 ^ tt2;
+
+            tt0 = A[ 0] ^ A[ 3];
+            tt1 = A[ 1] ^ A[ 4];
+            tt0 ^= A[ 2] ^ tt1;
+            tt0 = (tt0 << 1) | (tt0 >>> 63);
+            tt2 = A[18] ^ A[16];
+            tt3 = A[19] ^ A[17];
+            tt0 ^= A[15];
+            tt2 ^= tt3;
+            t4 = tt0 ^ tt2;
+
+            A[ 0] = A[ 0] ^ t0;
+            A[ 3] = A[ 3] ^ t0;
+            A[ 1] = A[ 1] ^ t0;
+            A[ 4] = A[ 4] ^ t0;
+            A[ 2] = A[ 2] ^ t0;
+            A[ 6] = A[ 6] ^ t1;
+            A[ 9] = A[ 9] ^ t1;
+            A[ 7] = A[ 7] ^ t1;
+            A[ 5] = A[ 5] ^ t1;
+            A[ 8] = A[ 8] ^ t1;
+            A[12] = A[12] ^ t2;
+            A[10] = A[10] ^ t2;
+            A[13] = A[13] ^ t2;
+            A[11] = A[11] ^ t2;
+            A[14] = A[14] ^ t2;
+            A[18] = A[18] ^ t3;
+            A[16] = A[16] ^ t3;
+            A[19] = A[19] ^ t3;
+            A[17] = A[17] ^ t3;
+            A[15] = A[15] ^ t3;
+            A[24] = A[24] ^ t4;
+            A[22] = A[22] ^ t4;
+            A[20] = A[20] ^ t4;
+            A[23] = A[23] ^ t4;
+            A[21] = A[21] ^ t4;
+            A[ 3] = (A[ 3] << 36) | (A[ 3] >>> (64 - 36));
+            A[ 1] = (A[ 1] << 3) | (A[ 1] >>> (64 - 3));
+            A[ 4] = (A[ 4] << 41) | (A[ 4] >>> (64 - 41));
+            A[ 2] = (A[ 2] << 18) | (A[ 2] >>> (64 - 18));
+            A[ 6] = (A[ 6] << 1) | (A[ 6] >>> (64 - 1));
+            A[ 9] = (A[ 9] << 44) | (A[ 9] >>> (64 - 44));
+            A[ 7] = (A[ 7] << 10) | (A[ 7] >>> (64 - 10));
+            A[ 5] = (A[ 5] << 45) | (A[ 5] >>> (64 - 45));
+            A[ 8] = (A[ 8] << 2) | (A[ 8] >>> (64 - 2));
+            A[12] = (A[12] << 62) | (A[12] >>> (64 - 62));
+            A[10] = (A[10] << 6) | (A[10] >>> (64 - 6));
+            A[13] = (A[13] << 43) | (A[13] >>> (64 - 43));
+            A[11] = (A[11] << 15) | (A[11] >>> (64 - 15));
+            A[14] = (A[14] << 61) | (A[14] >>> (64 - 61));
+            A[18] = (A[18] << 28) | (A[18] >>> (64 - 28));
+            A[16] = (A[16] << 55) | (A[16] >>> (64 - 55));
+            A[19] = (A[19] << 25) | (A[19] >>> (64 - 25));
+            A[17] = (A[17] << 21) | (A[17] >>> (64 - 21));
+            A[15] = (A[15] << 56) | (A[15] >>> (64 - 56));
+            A[24] = (A[24] << 27) | (A[24] >>> (64 - 27));
+            A[22] = (A[22] << 20) | (A[22] >>> (64 - 20));
+            A[20] = (A[20] << 39) | (A[20] >>> (64 - 39));
+            A[23] = (A[23] << 8) | (A[23] >>> (64 - 8));
+            A[21] = (A[21] << 14) | (A[21] >>> (64 - 14));
+            bnn = ~A[13];
+            kt = A[ 9] | A[13];
+            c0 = A[ 0] ^ kt;
+            kt = bnn | A[17];
+            c1 = A[ 9] ^ kt;
+            kt = A[17] & A[21];
+            c2 = A[13] ^ kt;
+            kt = A[21] | A[ 0];
+            c3 = A[17] ^ kt;
+            kt = A[ 0] & A[ 9];
+            c4 = A[21] ^ kt;
+            A[ 0] = c0;
+            A[ 9] = c1;
+            A[13] = c2;
+            A[17] = c3;
+            A[21] = c4;
+            bnn = ~A[14];
+            kt = A[22] | A[ 1];
+            c0 = A[18] ^ kt;
+            kt = A[ 1] & A[ 5];
+            c1 = A[22] ^ kt;
+            kt = A[ 5] | bnn;
+            c2 = A[ 1] ^ kt;
+            kt = A[14] | A[18];
+            c3 = A[ 5] ^ kt;
+            kt = A[18] & A[22];
+            c4 = A[14] ^ kt;
+            A[18] = c0;
+            A[22] = c1;
+            A[ 1] = c2;
+            A[ 5] = c3;
+            A[14] = c4;
+            bnn = ~A[23];
+            kt = A[10] | A[19];
+            c0 = A[ 6] ^ kt;
+            kt = A[19] & A[23];
+            c1 = A[10] ^ kt;
+            kt = bnn & A[ 2];
+            c2 = A[19] ^ kt;
+            kt = A[ 2] | A[ 6];
+            c3 = bnn ^ kt;
+            kt = A[ 6] & A[10];
+            c4 = A[ 2] ^ kt;
+            A[ 6] = c0;
+            A[10] = c1;
+            A[19] = c2;
+            A[23] = c3;
+            A[ 2] = c4;
+            bnn = ~A[11];
+            kt = A[ 3] & A[ 7];
+            c0 = A[24] ^ kt;
+            kt = A[ 7] | A[11];
+            c1 = A[ 3] ^ kt;
+            kt = bnn | A[15];
+            c2 = A[ 7] ^ kt;
+            kt = A[15] & A[24];
+            c3 = bnn ^ kt;
+            kt = A[24] | A[ 3];
+            c4 = A[15] ^ kt;
+            A[24] = c0;
+            A[ 3] = c1;
+            A[ 7] = c2;
+            A[11] = c3;
+            A[15] = c4;
+            bnn = ~A[16];
+            kt = bnn & A[20];
+            c0 = A[12] ^ kt;
+            kt = A[20] | A[ 4];
+            c1 = bnn ^ kt;
+            kt = A[ 4] & A[ 8];
+            c2 = A[20] ^ kt;
+            kt = A[ 8] | A[12];
+            c3 = A[ 4] ^ kt;
+            kt = A[12] & A[16];
+            c4 = A[ 8] ^ kt;
+            A[12] = c0;
+            A[16] = c1;
+            A[20] = c2;
+            A[ 4] = c3;
+            A[ 8] = c4;
+            A[ 0] = A[ 0] ^ RC[j + 1];
+            t = A[ 5];
+            A[ 5] = A[18];
+            A[18] = A[11];
+            A[11] = A[10];
+            A[10] = A[ 6];
+            A[ 6] = A[22];
+            A[22] = A[20];
+            A[20] = A[12];
+            A[12] = A[19];
+            A[19] = A[15];
+            A[15] = A[24];
+            A[24] = A[ 8];
+            A[ 8] = t;
+            t = A[ 1];
+            A[ 1] = A[ 9];
+            A[ 9] = A[14];
+            A[14] = A[ 2];
+            A[ 2] = A[13];
+            A[13] = A[23];
+            A[23] = A[ 4];
+            A[ 4] = A[21];
+            A[21] = A[16];
+            A[16] = A[ 3];
+            A[ 3] = A[17];
+            A[17] = A[ 7];
+            A[ 7] = t;
+        }
+    }
+
+    /** @see DigestEngine */
+    protected void doPadding(byte[] out, int off)
+    {
+        int ptr = flush();
+        byte[] buf = getBlockBuffer();
+        if ((ptr + 1) == buf.length) {
+            buf[ptr] = (byte)0x81;
+        } else {
+            buf[ptr] = (byte)0x01;
+            for (int i = ptr + 1; i < (buf.length - 1); i ++)
+                buf[i] = 0;
+            buf[buf.length - 1] = (byte)0x80;
+        }
+        processBlock(buf);
+        A[ 1] = ~A[ 1];
+        A[ 2] = ~A[ 2];
+        A[ 8] = ~A[ 8];
+        A[12] = ~A[12];
+        A[17] = ~A[17];
+        A[20] = ~A[20];
+        int dlen = getDigestLength();
+        for (int i = 0; i < dlen; i += 8)
+            encodeLELong(A[i >>> 3], tmpOut, i);
+        System.arraycopy(tmpOut, 0, out, off, dlen);
+    }
+
+    /** @see DigestEngine */
+    protected void doInit()
+    {
+        A = new long[25];
+        tmpOut = new byte[(getDigestLength() + 7) & ~7];
+        doReset();
+    }
+
+    /** @see Digest */
+    public int getBlockLength()
+    {
+        return 200 - 2 * getDigestLength();
+    }
+
+    private final void doReset()
+    {
+        for (int i = 0; i < 25; i ++)
+            A[i] = 0;
+        A[ 1] = 0xFFFFFFFFFFFFFFFFL;
+        A[ 2] = 0xFFFFFFFFFFFFFFFFL;
+        A[ 8] = 0xFFFFFFFFFFFFFFFFL;
+        A[12] = 0xFFFFFFFFFFFFFFFFL;
+        A[17] = 0xFFFFFFFFFFFFFFFFL;
+        A[20] = 0xFFFFFFFFFFFFFFFFL;
+    }
+
+    /** @see DigestEngine */
+    protected Digest copyState(KeccakCore dst)
+    {
+        System.arraycopy(A, 0, dst.A, 0, 25);
+        return super.copyState(dst);
+    }
+
+    /** @see Digest */
+    public String toString()
+    {
+        return "Keccak-" + (getDigestLength() << 3);
+    }
+}
\ No newline at end of file
diff --git a/jami-dht/src/main/java/net/jami/jams/dht/hashutils/MDHelper.java b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/MDHelper.java
new file mode 100755
index 0000000000000000000000000000000000000000..b5d4b83f2626aab035d7e52ed206f11044c65de8
--- /dev/null
+++ b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/MDHelper.java
@@ -0,0 +1,154 @@
+// $Id: MDHelper.java 157 2010-04-26 19:03:44Z tp $
+
+package net.jami.jams.dht.hashutils;
+
+/**
+ * <p>This class implements the padding common to MD4, MD5, the SHA family,
+ * and RIPEMD-160. This code works as long as the internal block length
+ * is a power of 2, which is the case for all these algorithms.</p>
+ *
+ * <pre>
+ * ==========================(LICENSE BEGIN)============================
+ *
+ * Copyright (c) 2007-2010  Projet RNRT SAPHIR
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ===========================(LICENSE END)=============================
+ * </pre>
+ *
+ * @version   $Revision: 157 $
+ * @author    Thomas Pornin &lt;thomas.pornin@cryptolog.com&gt;
+ */
+
+abstract class MDHelper extends DigestEngine {
+
+	/**
+	 * Create the object. Little-endian padding is for MD4, MD5 and
+	 * RIPEMD-160; the SHA family uses big-endian padding. The
+	 * MD padding includes an encoding of the input message bit length,
+	 * which is over 64 bits for some algorithms, 128-bit for others
+	 * (namely SHA-384 and SHA-512). Note that this implementation
+	 * handles only message lengths which fit on 64 bits.
+	 *
+	 * @param littleEndian   {@code true} for little-endian padding
+	 * @param lenlen         the length encoding length, in bytes (must
+	 *                       be at least 8)
+	 */
+	MDHelper(boolean littleEndian, int lenlen)
+	{
+		this(littleEndian, lenlen, (byte)0x80);
+	}
+
+	/**
+	 * Create the object. Little-endian padding is for MD4, MD5 and
+	 * RIPEMD-160; the SHA family uses big-endian padding. The
+	 * MD padding includes an encoding of the input message bit length,
+	 * which is over 64 bits for some algorithms, 128-bit for others
+	 * (namely SHA-384 and SHA-512). Note that this implementation
+	 * handles only message lengths which fit on 64 bits. The first
+	 * additional byte value is specified; this is normally 0x80,
+	 * except for Tiger (not Tiger2) which uses 0x01.
+	 *
+	 * @param littleEndian   {@code true} for little-endian padding
+	 * @param lenlen         the length encoding length, in bytes (must
+	 *                       be at least 8)
+	 * @param fbyte          the first padding byte
+	 */
+	MDHelper(boolean littleEndian, int lenlen, byte fbyte)
+	{
+		this.littleEndian = littleEndian;
+		countBuf = new byte[lenlen];
+		this.fbyte = fbyte;
+	}
+
+	private boolean littleEndian;
+	private byte[] countBuf;
+	private byte fbyte;
+
+	/**
+	 * Compute the padding. The padding data is input into the engine,
+	 * which is flushed.
+	 */
+	protected void makeMDPadding()
+	{
+		int dataLen = flush();
+		int blen = getBlockLength();
+		long currentLength = getBlockCount() * (long)blen;
+		currentLength = (currentLength + (long)dataLen) * 8L;
+		int lenlen = countBuf.length;
+		if (littleEndian) {
+			encodeLEInt((int)currentLength, countBuf, 0);
+			encodeLEInt((int)(currentLength >>> 32), countBuf, 4);
+		} else {
+			encodeBEInt((int)(currentLength >>> 32),
+				countBuf, lenlen - 8);
+			encodeBEInt((int)currentLength,
+				countBuf, lenlen - 4);
+		}
+		int endLen = (dataLen + lenlen + blen) & ~(blen - 1);
+		update(fbyte);
+		for (int i = dataLen + 1; i < endLen - lenlen; i ++)
+			update((byte)0);
+		update(countBuf);
+
+		/*
+		 * This code is used only for debugging purposes.
+		 *
+		if (flush() != 0)
+			throw new Error("panic: buffering went astray");
+		 *
+		 */
+	}
+
+	/**
+	 * Encode the 32-bit word {@code val} into the array
+	 * {@code buf} at offset {@code off}, in little-endian
+	 * convention (least significant byte first).
+	 *
+	 * @param val   the value to encode
+	 * @param buf   the destination buffer
+	 * @param off   the destination offset
+	 */
+	private static final void encodeLEInt(int val, byte[] buf, int off)
+	{
+		buf[off + 0] = (byte)val;
+		buf[off + 1] = (byte)(val >>> 8);
+		buf[off + 2] = (byte)(val >>> 16);
+		buf[off + 3] = (byte)(val >>> 24);
+	}
+
+	/**
+	 * Encode the 32-bit word {@code val} into the array
+	 * {@code buf} at offset {@code off}, in big-endian
+	 * convention (most significant byte first).
+	 *
+	 * @param val   the value to encode
+	 * @param buf   the destination buffer
+	 * @param off   the destination offset
+	 */
+	private static final void encodeBEInt(int val, byte[] buf, int off)
+	{
+		buf[off + 0] = (byte)(val >>> 24);
+		buf[off + 1] = (byte)(val >>> 16);
+		buf[off + 2] = (byte)(val >>> 8);
+		buf[off + 3] = (byte)val;
+	}
+}
diff --git a/jami-dht/src/main/java/net/jami/jams/dht/hashutils/SHA2Core.java b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/SHA2Core.java
new file mode 100755
index 0000000000000000000000000000000000000000..de78d1055c6a9bba67a0ac468990f4b59a3b588c
--- /dev/null
+++ b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/SHA2Core.java
@@ -0,0 +1,631 @@
+// $Id: SHA2Core.java 214 2010-06-03 17:25:08Z tp $
+
+package net.jami.jams.dht.hashutils;
+
+/**
+ * This class implements SHA-224 and SHA-256, which differ only by the IV
+ * and the output length.
+ *
+ * <pre>
+ * ==========================(LICENSE BEGIN)============================
+ *
+ * Copyright (c) 2007-2010  Projet RNRT SAPHIR
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ===========================(LICENSE END)=============================
+ * </pre>
+ *
+ * @version   $Revision: 214 $
+ * @author    Thomas Pornin &lt;thomas.pornin@cryptolog.com&gt;
+ */
+
+abstract class SHA2Core extends MDHelper {
+
+	/**
+	 * Create the object.
+	 */
+	SHA2Core()
+	{
+		super(false, 8);
+	}
+
+	/** private special values. */
+	private static final int[] K = {
+		0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
+		0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
+		0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
+		0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
+		0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
+		0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
+		0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
+		0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
+		0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
+		0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
+		0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
+		0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
+		0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
+		0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
+		0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
+		0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2
+	};
+
+	private int[] currentVal, W;
+
+	/** @see DigestEngine */
+	protected Digest copyState(SHA2Core dst)
+	{
+		System.arraycopy(currentVal, 0, dst.currentVal, 0,
+			currentVal.length);
+		return super.copyState(dst);
+	}
+
+	/** @see Digest */
+	public int getBlockLength()
+	{
+		return 64;
+	}
+
+	/** @see DigestEngine */
+	protected void engineReset()
+	{
+		System.arraycopy(getInitVal(), 0, currentVal, 0, 8);
+	}
+
+	/**
+	 * Get the initial value for this algorithm.
+	 *
+	 * @return  the initial value (eight 32-bit words)
+	 */
+	abstract int[] getInitVal();
+
+	/** @see DigestEngine */
+	protected void doPadding(byte[] output, int outputOffset)
+	{
+		makeMDPadding();
+		int olen = getDigestLength();
+		for (int i = 0, j = 0; j < olen; i ++, j += 4)
+			encodeBEInt(currentVal[i], output, outputOffset + j);
+	}
+
+	/** @see DigestEngine */
+	protected void doInit()
+	{
+		currentVal = new int[8];
+		W = new int[64];
+		engineReset();
+	}
+
+	/**
+	 * Encode the 32-bit word {@code val} into the array
+	 * {@code buf} at offset {@code off}, in big-endian
+	 * convention (most significant byte first).
+	 *
+	 * @param val   the value to encode
+	 * @param buf   the destination buffer
+	 * @param off   the destination offset
+	 */
+	private static final void encodeBEInt(int val, byte[] buf, int off)
+	{
+		buf[off + 0] = (byte)(val >>> 24);
+		buf[off + 1] = (byte)(val >>> 16);
+		buf[off + 2] = (byte)(val >>> 8);
+		buf[off + 3] = (byte)val;
+	}
+
+	/**
+	 * Decode a 32-bit big-endian word from the array {@code buf}
+	 * at offset {@code off}.
+	 *
+	 * @param buf   the source buffer
+	 * @param off   the source offset
+	 * @return  the decoded value
+	 */
+	private static final int decodeBEInt(byte[] buf, int off)
+	{
+		return ((buf[off] & 0xFF) << 24)
+			| ((buf[off + 1] & 0xFF) << 16)
+			| ((buf[off + 2] & 0xFF) << 8)
+			| (buf[off + 3] & 0xFF);
+	}
+
+	/**
+	 * Perform a circular rotation by {@code n} to the left
+	 * of the 32-bit word {@code x}. The {@code n} parameter
+	 * must lie between 1 and 31 (inclusive).
+	 *
+	 * @param x   the value to rotate
+	 * @param n   the rotation count (between 1 and 31)
+	 * @return  the rotated value
+	*/
+	static private int circularLeft(int x, int n)
+	{
+		return (x << n) | (x >>> (32 - n));
+	}
+
+	/** @see DigestEngine */
+	protected void processBlock(byte[] data)
+	{
+		int A = currentVal[0];
+		int B = currentVal[1];
+		int C = currentVal[2];
+		int D = currentVal[3];
+		int E = currentVal[4];
+		int F = currentVal[5];
+		int G = currentVal[6];
+		int H = currentVal[7];
+
+		for (int i = 0; i < 16; i ++)
+			W[i] = decodeBEInt(data, 4 * i);
+		for (int i = 16; i < 64; i ++) {
+			W[i] = (circularLeft(W[i - 2], 15)
+				^ circularLeft(W[i - 2], 13)
+				^ (W[i - 2] >>> 10))
+				+ W[i - 7]
+				+ (circularLeft(W[i - 15], 25)
+				^ circularLeft(W[i - 15], 14)
+				^ (W[i - 15] >>> 3))
+				+ W[i - 16];
+		}
+		for (int i = 0; i < 64; i ++) {
+			int T1 = H + (circularLeft(E, 26) ^ circularLeft(E, 21)
+				^ circularLeft(E, 7)) + ((F & E) ^ (G & ~E))
+				+ K[i] + W[i];
+			int T2 = (circularLeft(A, 30) ^ circularLeft(A, 19)
+				^ circularLeft(A, 10))
+				+ ((A & B) ^ (A & C) ^ (B & C));
+			H = G; G = F; F = E; E = D + T1;
+			D = C; C = B; B = A; A = T1 + T2;
+		}
+		currentVal[0] += A;
+		currentVal[1] += B;
+		currentVal[2] += C;
+		currentVal[3] += D;
+		currentVal[4] += E;
+		currentVal[5] += F;
+		currentVal[6] += G;
+		currentVal[7] += H;
+
+		/*
+		 * The version below unrolls 16 rounds and inlines
+		 * rotations. It should avoid many array accesses
+		 * (W[] is transformed into 16 local variables) and
+		 * data routing (16 is a multiple of 8, so the
+		 * big rotation of the eight words becomes trivial).
+		 * Strangely enough, it yields only a very small
+		 * performance gain (less than 10% on Intel x86 with
+		 * Sun JDK 6, both in 32-bit and 64-bit modes). Since
+		 * it also probably consumes much more L1 cache, the
+		 * simpler version above is preferred.
+		 *
+		int A = currentVal[0];
+		int B = currentVal[1];
+		int C = currentVal[2];
+		int D = currentVal[3];
+		int E = currentVal[4];
+		int F = currentVal[5];
+		int G = currentVal[6];
+		int H = currentVal[7];
+		int t1, t2;
+		int pcount = 0;
+		int W0 = decodeBEInt(data, 4 * 0x0);
+		t1 = H + (((E >>> 6) | (E << (32 - 6))) ^ ((E >>> 11)
+			| (E << (32 - 11))) ^ ((E >>> 25) | (E << (32 - 25))))
+			+ (((F ^ G) & E) ^ G) + K[pcount + 0x0] + W0;
+		t2 = (((A >>> 2) | (A << (32 - 2))) ^ ((A >>> 13)
+			| (A << (32 - 13))) ^ ((A >>> 22) | (A << (32 - 22))))
+			+ ((B & C) | ((B | C) & A));
+		D += t1;
+		H = t1 + t2;
+		int W1 = decodeBEInt(data, 4 * 0x1);
+		t1 = G + (((D >>> 6) | (D << (32 - 6))) ^ ((D >>> 11)
+			| (D << (32 - 11))) ^ ((D >>> 25) | (D << (32 - 25))))
+			+ (((E ^ F) & D) ^ F) + K[pcount + 0x1] + W1;
+		t2 = (((H >>> 2) | (H << (32 - 2))) ^ ((H >>> 13)
+			| (H << (32 - 13))) ^ ((H >>> 22) | (H << (32 - 22))))
+			+ ((A & B) | ((A | B) & H));
+		C += t1;
+		G = t1 + t2;
+		int W2 = decodeBEInt(data, 4 * 0x2);
+		t1 = F + (((C >>> 6) | (C << (32 - 6))) ^ ((C >>> 11)
+			| (C << (32 - 11))) ^ ((C >>> 25) | (C << (32 - 25))))
+			+ (((D ^ E) & C) ^ E) + K[pcount + 0x2] + W2;
+		t2 = (((G >>> 2) | (G << (32 - 2))) ^ ((G >>> 13)
+			| (G << (32 - 13))) ^ ((G >>> 22) | (G << (32 - 22))))
+			+ ((H & A) | ((H | A) & G));
+		B += t1;
+		F = t1 + t2;
+		int W3 = decodeBEInt(data, 4 * 0x3);
+		t1 = E + (((B >>> 6) | (B << (32 - 6))) ^ ((B >>> 11)
+			| (B << (32 - 11))) ^ ((B >>> 25) | (B << (32 - 25))))
+			+ (((C ^ D) & B) ^ D) + K[pcount + 0x3] + W3;
+		t2 = (((F >>> 2) | (F << (32 - 2))) ^ ((F >>> 13)
+			| (F << (32 - 13))) ^ ((F >>> 22) | (F << (32 - 22))))
+			+ ((G & H) | ((G | H) & F));
+		A += t1;
+		E = t1 + t2;
+		int W4 = decodeBEInt(data, 4 * 0x4);
+		t1 = D + (((A >>> 6) | (A << (32 - 6))) ^ ((A >>> 11)
+			| (A << (32 - 11))) ^ ((A >>> 25) | (A << (32 - 25))))
+			+ (((B ^ C) & A) ^ C) + K[pcount + 0x4] + W4;
+		t2 = (((E >>> 2) | (E << (32 - 2))) ^ ((E >>> 13)
+			| (E << (32 - 13))) ^ ((E >>> 22) | (E << (32 - 22))))
+			+ ((F & G) | ((F | G) & E));
+		H += t1;
+		D = t1 + t2;
+		int W5 = decodeBEInt(data, 4 * 0x5);
+		t1 = C + (((H >>> 6) | (H << (32 - 6))) ^ ((H >>> 11)
+			| (H << (32 - 11))) ^ ((H >>> 25) | (H << (32 - 25))))
+			+ (((A ^ B) & H) ^ B) + K[pcount + 0x5] + W5;
+		t2 = (((D >>> 2) | (D << (32 - 2))) ^ ((D >>> 13)
+			| (D << (32 - 13))) ^ ((D >>> 22) | (D << (32 - 22))))
+			+ ((E & F) | ((E | F) & D));
+		G += t1;
+		C = t1 + t2;
+		int W6 = decodeBEInt(data, 4 * 0x6);
+		t1 = B + (((G >>> 6) | (G << (32 - 6))) ^ ((G >>> 11)
+			| (G << (32 - 11))) ^ ((G >>> 25) | (G << (32 - 25))))
+			+ (((H ^ A) & G) ^ A) + K[pcount + 0x6] + W6;
+		t2 = (((C >>> 2) | (C << (32 - 2))) ^ ((C >>> 13)
+			| (C << (32 - 13))) ^ ((C >>> 22) | (C << (32 - 22))))
+			+ ((D & E) | ((D | E) & C));
+		F += t1;
+		B = t1 + t2;
+		int W7 = decodeBEInt(data, 4 * 0x7);
+		t1 = A + (((F >>> 6) | (F << (32 - 6))) ^ ((F >>> 11)
+			| (F << (32 - 11))) ^ ((F >>> 25) | (F << (32 - 25))))
+			+ (((G ^ H) & F) ^ H) + K[pcount + 0x7] + W7;
+		t2 = (((B >>> 2) | (B << (32 - 2))) ^ ((B >>> 13)
+			| (B << (32 - 13))) ^ ((B >>> 22) | (B << (32 - 22))))
+			+ ((C & D) | ((C | D) & B));
+		E += t1;
+		A = t1 + t2;
+		int W8 = decodeBEInt(data, 4 * 0x8);
+		t1 = H + (((E >>> 6) | (E << (32 - 6))) ^ ((E >>> 11)
+			| (E << (32 - 11))) ^ ((E >>> 25) | (E << (32 - 25))))
+			+ (((F ^ G) & E) ^ G) + K[pcount + 0x8] + W8;
+		t2 = (((A >>> 2) | (A << (32 - 2))) ^ ((A >>> 13)
+			| (A << (32 - 13))) ^ ((A >>> 22) | (A << (32 - 22))))
+			+ ((B & C) | ((B | C) & A));
+		D += t1;
+		H = t1 + t2;
+		int W9 = decodeBEInt(data, 4 * 0x9);
+		t1 = G + (((D >>> 6) | (D << (32 - 6))) ^ ((D >>> 11)
+			| (D << (32 - 11))) ^ ((D >>> 25) | (D << (32 - 25))))
+			+ (((E ^ F) & D) ^ F) + K[pcount + 0x9] + W9;
+		t2 = (((H >>> 2) | (H << (32 - 2))) ^ ((H >>> 13)
+			| (H << (32 - 13))) ^ ((H >>> 22) | (H << (32 - 22))))
+			+ ((A & B) | ((A | B) & H));
+		C += t1;
+		G = t1 + t2;
+		int WA = decodeBEInt(data, 4 * 0xA);
+		t1 = F + (((C >>> 6) | (C << (32 - 6))) ^ ((C >>> 11)
+			| (C << (32 - 11))) ^ ((C >>> 25) | (C << (32 - 25))))
+			+ (((D ^ E) & C) ^ E) + K[pcount + 0xA] + WA;
+		t2 = (((G >>> 2) | (G << (32 - 2))) ^ ((G >>> 13)
+			| (G << (32 - 13))) ^ ((G >>> 22) | (G << (32 - 22))))
+			+ ((H & A) | ((H | A) & G));
+		B += t1;
+		F = t1 + t2;
+		int WB = decodeBEInt(data, 4 * 0xB);
+		t1 = E + (((B >>> 6) | (B << (32 - 6))) ^ ((B >>> 11)
+			| (B << (32 - 11))) ^ ((B >>> 25) | (B << (32 - 25))))
+			+ (((C ^ D) & B) ^ D) + K[pcount + 0xB] + WB;
+		t2 = (((F >>> 2) | (F << (32 - 2))) ^ ((F >>> 13)
+			| (F << (32 - 13))) ^ ((F >>> 22) | (F << (32 - 22))))
+			+ ((G & H) | ((G | H) & F));
+		A += t1;
+		E = t1 + t2;
+		int WC = decodeBEInt(data, 4 * 0xC);
+		t1 = D + (((A >>> 6) | (A << (32 - 6))) ^ ((A >>> 11)
+			| (A << (32 - 11))) ^ ((A >>> 25) | (A << (32 - 25))))
+			+ (((B ^ C) & A) ^ C) + K[pcount + 0xC] + WC;
+		t2 = (((E >>> 2) | (E << (32 - 2))) ^ ((E >>> 13)
+			| (E << (32 - 13))) ^ ((E >>> 22) | (E << (32 - 22))))
+			+ ((F & G) | ((F | G) & E));
+		H += t1;
+		D = t1 + t2;
+		int WD = decodeBEInt(data, 4 * 0xD);
+		t1 = C + (((H >>> 6) | (H << (32 - 6))) ^ ((H >>> 11)
+			| (H << (32 - 11))) ^ ((H >>> 25) | (H << (32 - 25))))
+			+ (((A ^ B) & H) ^ B) + K[pcount + 0xD] + WD;
+		t2 = (((D >>> 2) | (D << (32 - 2))) ^ ((D >>> 13)
+			| (D << (32 - 13))) ^ ((D >>> 22) | (D << (32 - 22))))
+			+ ((E & F) | ((E | F) & D));
+		G += t1;
+		C = t1 + t2;
+		int WE = decodeBEInt(data, 4 * 0xE);
+		t1 = B + (((G >>> 6) | (G << (32 - 6))) ^ ((G >>> 11)
+			| (G << (32 - 11))) ^ ((G >>> 25) | (G << (32 - 25))))
+			+ (((H ^ A) & G) ^ A) + K[pcount + 0xE] + WE;
+		t2 = (((C >>> 2) | (C << (32 - 2))) ^ ((C >>> 13)
+			| (C << (32 - 13))) ^ ((C >>> 22) | (C << (32 - 22))))
+			+ ((D & E) | ((D | E) & C));
+		F += t1;
+		B = t1 + t2;
+		int WF = decodeBEInt(data, 4 * 0xF);
+		t1 = A + (((F >>> 6) | (F << (32 - 6))) ^ ((F >>> 11)
+			| (F << (32 - 11))) ^ ((F >>> 25) | (F << (32 - 25))))
+			+ (((G ^ H) & F) ^ H) + K[pcount + 0xF] + WF;
+		t2 = (((B >>> 2) | (B << (32 - 2))) ^ ((B >>> 13)
+			| (B << (32 - 13))) ^ ((B >>> 22) | (B << (32 - 22))))
+			+ ((C & D) | ((C | D) & B));
+		E += t1;
+		A = t1 + t2;
+		for (pcount = 16; pcount < 64; pcount += 16) {
+			W0 += (((WE >>> 17) | (WE << (32 - 17))) ^ ((WE >>> 19)
+				| (WE << (32 - 19))) ^ (WE >>> 10)) + W9
+				+ (((W1 >>> 7) | (W1 << (32 - 7)))
+				^ ((W1 >>> 18) | (W1 << (32 - 18)))
+				^ (W1 >>> 3));
+			t1 = H + (((E >>> 6) | (E << (32 - 6))) ^ ((E >>> 11)
+				| (E << (32 - 11))) ^ ((E >>> 25)
+				| (E << (32 - 25)))) + (((F ^ G) & E) ^ G)
+				+ K[pcount + 0x0] + W0;
+			t2 = (((A >>> 2) | (A << (32 - 2))) ^ ((A >>> 13)
+				| (A << (32 - 13))) ^ ((A >>> 22)
+				| (A << (32 - 22))))
+				+ ((B & C) | ((B | C) & A));
+			D += t1;
+			H = t1 + t2;
+			W1 += (((WF >>> 17) | (WF << (32 - 17))) ^ ((WF >>> 19)
+				| (WF << (32 - 19))) ^ (WF >>> 10)) + WA
+				+ (((W2 >>> 7) | (W2 << (32 - 7)))
+				^ ((W2 >>> 18) | (W2 << (32 - 18)))
+				^ (W2 >>> 3));
+			t1 = G + (((D >>> 6) | (D << (32 - 6))) ^ ((D >>> 11)
+				| (D << (32 - 11))) ^ ((D >>> 25)
+				| (D << (32 - 25)))) + (((E ^ F) & D) ^ F)
+				+ K[pcount + 0x1] + W1;
+			t2 = (((H >>> 2) | (H << (32 - 2))) ^ ((H >>> 13)
+				| (H << (32 - 13))) ^ ((H >>> 22)
+				| (H << (32 - 22))))
+				+ ((A & B) | ((A | B) & H));
+			C += t1;
+			G = t1 + t2;
+			W2 += (((W0 >>> 17) | (W0 << (32 - 17))) ^ ((W0 >>> 19)
+				| (W0 << (32 - 19))) ^ (W0 >>> 10)) + WB
+				+ (((W3 >>> 7) | (W3 << (32 - 7)))
+				^ ((W3 >>> 18) | (W3 << (32 - 18)))
+				^ (W3 >>> 3));
+			t1 = F + (((C >>> 6) | (C << (32 - 6))) ^ ((C >>> 11)
+				| (C << (32 - 11))) ^ ((C >>> 25)
+				| (C << (32 - 25)))) + (((D ^ E) & C) ^ E)
+				+ K[pcount + 0x2] + W2;
+			t2 = (((G >>> 2) | (G << (32 - 2))) ^ ((G >>> 13)
+				| (G << (32 - 13))) ^ ((G >>> 22)
+				| (G << (32 - 22))))
+				+ ((H & A) | ((H | A) & G));
+			B += t1;
+			F = t1 + t2;
+			W3 += (((W1 >>> 17) | (W1 << (32 - 17))) ^ ((W1 >>> 19)
+				| (W1 << (32 - 19))) ^ (W1 >>> 10)) + WC
+				+ (((W4 >>> 7) | (W4 << (32 - 7)))
+				^ ((W4 >>> 18) | (W4 << (32 - 18)))
+				^ (W4 >>> 3));
+			t1 = E + (((B >>> 6) | (B << (32 - 6))) ^ ((B >>> 11)
+				| (B << (32 - 11))) ^ ((B >>> 25)
+				| (B << (32 - 25)))) + (((C ^ D) & B) ^ D)
+				+ K[pcount + 0x3] + W3;
+			t2 = (((F >>> 2) | (F << (32 - 2))) ^ ((F >>> 13)
+				| (F << (32 - 13))) ^ ((F >>> 22)
+				| (F << (32 - 22))))
+				+ ((G & H) | ((G | H) & F));
+			A += t1;
+			E = t1 + t2;
+			W4 += (((W2 >>> 17) | (W2 << (32 - 17))) ^ ((W2 >>> 19)
+				| (W2 << (32 - 19))) ^ (W2 >>> 10)) + WD
+				+ (((W5 >>> 7) | (W5 << (32 - 7)))
+				^ ((W5 >>> 18) | (W5 << (32 - 18)))
+				^ (W5 >>> 3));
+			t1 = D + (((A >>> 6) | (A << (32 - 6))) ^ ((A >>> 11)
+				| (A << (32 - 11))) ^ ((A >>> 25)
+				| (A << (32 - 25)))) + (((B ^ C) & A) ^ C)
+				+ K[pcount + 0x4] + W4;
+			t2 = (((E >>> 2) | (E << (32 - 2))) ^ ((E >>> 13)
+				| (E << (32 - 13))) ^ ((E >>> 22)
+				| (E << (32 - 22))))
+				+ ((F & G) | ((F | G) & E));
+			H += t1;
+			D = t1 + t2;
+			W5 += (((W3 >>> 17) | (W3 << (32 - 17))) ^ ((W3 >>> 19)
+				| (W3 << (32 - 19))) ^ (W3 >>> 10)) + WE
+				+ (((W6 >>> 7) | (W6 << (32 - 7)))
+				^ ((W6 >>> 18) | (W6 << (32 - 18)))
+				^ (W6 >>> 3));
+			t1 = C + (((H >>> 6) | (H << (32 - 6))) ^ ((H >>> 11)
+				| (H << (32 - 11))) ^ ((H >>> 25)
+				| (H << (32 - 25)))) + (((A ^ B) & H) ^ B)
+				+ K[pcount + 0x5] + W5;
+			t2 = (((D >>> 2) | (D << (32 - 2))) ^ ((D >>> 13)
+				| (D << (32 - 13))) ^ ((D >>> 22)
+				| (D << (32 - 22))))
+				+ ((E & F) | ((E | F) & D));
+			G += t1;
+			C = t1 + t2;
+			W6 += (((W4 >>> 17) | (W4 << (32 - 17))) ^ ((W4 >>> 19)
+				| (W4 << (32 - 19))) ^ (W4 >>> 10)) + WF
+				+ (((W7 >>> 7) | (W7 << (32 - 7)))
+				^ ((W7 >>> 18) | (W7 << (32 - 18)))
+				^ (W7 >>> 3));
+			t1 = B + (((G >>> 6) | (G << (32 - 6))) ^ ((G >>> 11)
+				| (G << (32 - 11))) ^ ((G >>> 25)
+				| (G << (32 - 25)))) + (((H ^ A) & G) ^ A)
+				+ K[pcount + 0x6] + W6;
+			t2 = (((C >>> 2) | (C << (32 - 2))) ^ ((C >>> 13)
+				| (C << (32 - 13))) ^ ((C >>> 22)
+				| (C << (32 - 22))))
+				+ ((D & E) | ((D | E) & C));
+			F += t1;
+			B = t1 + t2;
+			W7 += (((W5 >>> 17) | (W5 << (32 - 17))) ^ ((W5 >>> 19)
+				| (W5 << (32 - 19))) ^ (W5 >>> 10)) + W0
+				+ (((W8 >>> 7) | (W8 << (32 - 7)))
+				^ ((W8 >>> 18) | (W8 << (32 - 18)))
+				^ (W8 >>> 3));
+			t1 = A + (((F >>> 6) | (F << (32 - 6))) ^ ((F >>> 11)
+				| (F << (32 - 11))) ^ ((F >>> 25)
+				| (F << (32 - 25)))) + (((G ^ H) & F) ^ H)
+				+ K[pcount + 0x7] + W7;
+			t2 = (((B >>> 2) | (B << (32 - 2))) ^ ((B >>> 13)
+				| (B << (32 - 13))) ^ ((B >>> 22)
+				| (B << (32 - 22))))
+				+ ((C & D) | ((C | D) & B));
+			E += t1;
+			A = t1 + t2;
+			W8 += (((W6 >>> 17) | (W6 << (32 - 17))) ^ ((W6 >>> 19)
+				| (W6 << (32 - 19))) ^ (W6 >>> 10)) + W1
+				+ (((W9 >>> 7) | (W9 << (32 - 7)))
+				^ ((W9 >>> 18) | (W9 << (32 - 18)))
+				^ (W9 >>> 3));
+			t1 = H + (((E >>> 6) | (E << (32 - 6))) ^ ((E >>> 11)
+				| (E << (32 - 11))) ^ ((E >>> 25)
+				| (E << (32 - 25)))) + (((F ^ G) & E) ^ G)
+				+ K[pcount + 0x8] + W8;
+			t2 = (((A >>> 2) | (A << (32 - 2))) ^ ((A >>> 13)
+				| (A << (32 - 13))) ^ ((A >>> 22)
+				| (A << (32 - 22))))
+				+ ((B & C) | ((B | C) & A));
+			D += t1;
+			H = t1 + t2;
+			W9 += (((W7 >>> 17) | (W7 << (32 - 17))) ^ ((W7 >>> 19)
+				| (W7 << (32 - 19))) ^ (W7 >>> 10)) + W2
+				+ (((WA >>> 7) | (WA << (32 - 7)))
+				^ ((WA >>> 18) | (WA << (32 - 18)))
+				^ (WA >>> 3));
+			t1 = G + (((D >>> 6) | (D << (32 - 6))) ^ ((D >>> 11)
+				| (D << (32 - 11))) ^ ((D >>> 25)
+				| (D << (32 - 25)))) + (((E ^ F) & D) ^ F)
+				+ K[pcount + 0x9] + W9;
+			t2 = (((H >>> 2) | (H << (32 - 2))) ^ ((H >>> 13)
+				| (H << (32 - 13))) ^ ((H >>> 22)
+				| (H << (32 - 22))))
+				+ ((A & B) | ((A | B) & H));
+			C += t1;
+			G = t1 + t2;
+			WA += (((W8 >>> 17) | (W8 << (32 - 17))) ^ ((W8 >>> 19)
+				| (W8 << (32 - 19))) ^ (W8 >>> 10)) + W3
+				+ (((WB >>> 7) | (WB << (32 - 7)))
+				^ ((WB >>> 18) | (WB << (32 - 18)))
+				^ (WB >>> 3));
+			t1 = F + (((C >>> 6) | (C << (32 - 6))) ^ ((C >>> 11)
+				| (C << (32 - 11))) ^ ((C >>> 25)
+				| (C << (32 - 25)))) + (((D ^ E) & C) ^ E)
+				+ K[pcount + 0xA] + WA;
+			t2 = (((G >>> 2) | (G << (32 - 2))) ^ ((G >>> 13)
+				| (G << (32 - 13))) ^ ((G >>> 22)
+				| (G << (32 - 22))))
+				+ ((H & A) | ((H | A) & G));
+			B += t1;
+			F = t1 + t2;
+			WB += (((W9 >>> 17) | (W9 << (32 - 17))) ^ ((W9 >>> 19)
+				| (W9 << (32 - 19))) ^ (W9 >>> 10)) + W4
+				+ (((WC >>> 7) | (WC << (32 - 7)))
+				^ ((WC >>> 18) | (WC << (32 - 18)))
+				^ (WC >>> 3));
+			t1 = E + (((B >>> 6) | (B << (32 - 6))) ^ ((B >>> 11)
+				| (B << (32 - 11))) ^ ((B >>> 25)
+				| (B << (32 - 25)))) + (((C ^ D) & B) ^ D)
+				+ K[pcount + 0xB] + WB;
+			t2 = (((F >>> 2) | (F << (32 - 2))) ^ ((F >>> 13)
+				| (F << (32 - 13))) ^ ((F >>> 22)
+				| (F << (32 - 22))))
+				+ ((G & H) | ((G | H) & F));
+			A += t1;
+			E = t1 + t2;
+			WC += (((WA >>> 17) | (WA << (32 - 17))) ^ ((WA >>> 19)
+				| (WA << (32 - 19))) ^ (WA >>> 10)) + W5
+				+ (((WD >>> 7) | (WD << (32 - 7)))
+				^ ((WD >>> 18) | (WD << (32 - 18)))
+				^ (WD >>> 3));
+			t1 = D + (((A >>> 6) | (A << (32 - 6))) ^ ((A >>> 11)
+				| (A << (32 - 11))) ^ ((A >>> 25)
+				| (A << (32 - 25)))) + (((B ^ C) & A) ^ C)
+				+ K[pcount + 0xC] + WC;
+			t2 = (((E >>> 2) | (E << (32 - 2))) ^ ((E >>> 13)
+				| (E << (32 - 13))) ^ ((E >>> 22)
+				| (E << (32 - 22))))
+				+ ((F & G) | ((F | G) & E));
+			H += t1;
+			D = t1 + t2;
+			WD += (((WB >>> 17) | (WB << (32 - 17))) ^ ((WB >>> 19)
+				| (WB << (32 - 19))) ^ (WB >>> 10)) + W6
+				+ (((WE >>> 7) | (WE << (32 - 7)))
+				^ ((WE >>> 18) | (WE << (32 - 18)))
+				^ (WE >>> 3));
+			t1 = C + (((H >>> 6) | (H << (32 - 6))) ^ ((H >>> 11)
+				| (H << (32 - 11))) ^ ((H >>> 25)
+				| (H << (32 - 25)))) + (((A ^ B) & H) ^ B)
+				+ K[pcount + 0xD] + WD;
+			t2 = (((D >>> 2) | (D << (32 - 2))) ^ ((D >>> 13)
+				| (D << (32 - 13))) ^ ((D >>> 22)
+				| (D << (32 - 22))))
+				+ ((E & F) | ((E | F) & D));
+			G += t1;
+			C = t1 + t2;
+			WE += (((WC >>> 17) | (WC << (32 - 17))) ^ ((WC >>> 19)
+				| (WC << (32 - 19))) ^ (WC >>> 10)) + W7
+				+ (((WF >>> 7) | (WF << (32 - 7)))
+				^ ((WF >>> 18) | (WF << (32 - 18)))
+				^ (WF >>> 3));
+			t1 = B + (((G >>> 6) | (G << (32 - 6))) ^ ((G >>> 11)
+				| (G << (32 - 11))) ^ ((G >>> 25)
+				| (G << (32 - 25)))) + (((H ^ A) & G) ^ A)
+				+ K[pcount + 0xE] + WE;
+			t2 = (((C >>> 2) | (C << (32 - 2))) ^ ((C >>> 13)
+				| (C << (32 - 13))) ^ ((C >>> 22)
+				| (C << (32 - 22))))
+				+ ((D & E) | ((D | E) & C));
+			F += t1;
+			B = t1 + t2;
+			WF += (((WD >>> 17) | (WD << (32 - 17))) ^ ((WD >>> 19)
+				| (WD << (32 - 19))) ^ (WD >>> 10)) + W8
+				+ (((W0 >>> 7) | (W0 << (32 - 7)))
+				^ ((W0 >>> 18) | (W0 << (32 - 18)))
+				^ (W0 >>> 3));
+			t1 = A + (((F >>> 6) | (F << (32 - 6))) ^ ((F >>> 11)
+				| (F << (32 - 11))) ^ ((F >>> 25)
+				| (F << (32 - 25)))) + (((G ^ H) & F) ^ H)
+				+ K[pcount + 0xF] + WF;
+			t2 = (((B >>> 2) | (B << (32 - 2))) ^ ((B >>> 13)
+				| (B << (32 - 13))) ^ ((B >>> 22)
+				| (B << (32 - 22))))
+				+ ((C & D) | ((C | D) & B));
+			E += t1;
+			A = t1 + t2;
+		}
+
+		currentVal[0] += A;
+		currentVal[1] += B;
+		currentVal[2] += C;
+		currentVal[3] += D;
+		currentVal[4] += E;
+		currentVal[5] += F;
+		currentVal[6] += G;
+		currentVal[7] += H;
+		*/
+	}
+
+	/** @see Digest */
+	public String toString()
+	{
+		return "SHA-" + (getDigestLength() << 3);
+	}
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java
new file mode 100644
index 0000000000000000000000000000000000000000..f00b6d9598e9dea13632e75eef0232bd57e7d9ec
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java
@@ -0,0 +1,15 @@
+package net.jami.jams.common.authentication;
+
+import net.jami.jams.common.objects.user.User;
+import net.jami.jams.common.objects.user.UserProfile;
+
+public interface AuthenticationSource {
+
+    boolean createUser(User user);
+    UserProfile getUserProfile(String username);
+    boolean authenticate(String username, String password);
+    AuthenticationSourceInfo getInfo();
+    boolean testConfiguration(String configuration);
+    boolean updatePassword(User user, String password);
+
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceInfo.java b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f5e494f83600c8e7fd6f364c212b74e5d2595e9
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceInfo.java
@@ -0,0 +1,17 @@
+package net.jami.jams.common.authentication;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class AuthenticationSourceInfo {
+
+    private String realm;
+    private AuthenticationSourceType authenticationSourceType;
+
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceType.java b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceType.java
new file mode 100644
index 0000000000000000000000000000000000000000..329f003b8d5c5ded471bae9d7fc51e1b8053d514
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceType.java
@@ -0,0 +1,7 @@
+package net.jami.jams.common.authentication;
+
+public enum AuthenticationSourceType {
+    AD,
+    LDAP,
+    LOCAL
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/authentication/activedirectory/ActiveDirectorySettings.java b/jams-common/src/main/java/net/jami/jams/common/authentication/activedirectory/ActiveDirectorySettings.java
new file mode 100644
index 0000000000000000000000000000000000000000..55e262bab892c3efd109c1ed020456e4eb28727c
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/authentication/activedirectory/ActiveDirectorySettings.java
@@ -0,0 +1,15 @@
+package net.jami.jams.common.authentication.activedirectory;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class ActiveDirectorySettings {
+    private Boolean ssl;
+    private int     port;
+    private String  realm;
+    private String  host;
+    private String  username;
+    private String  password;
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/authentication/ldap/LDAPSettings.java b/jams-common/src/main/java/net/jami/jams/common/authentication/ldap/LDAPSettings.java
new file mode 100644
index 0000000000000000000000000000000000000000..f4fd0fc4be7362bc8b5eac67bbea5454497471c7
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/authentication/ldap/LDAPSettings.java
@@ -0,0 +1,18 @@
+package net.jami.jams.common.authentication.ldap;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class LDAPSettings {
+
+    private Boolean useStartTLS;
+    private String  realm;
+    private String  baseDN;
+    private String  host;
+    private String  username;
+    private String  password;
+    private String  usernameField;
+
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/configuration/ServerConfiguration.java b/jams-common/src/main/java/net/jami/jams/common/configuration/ServerConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..7cc0d52a62082dd961cba740be623bf69686002b
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/configuration/ServerConfiguration.java
@@ -0,0 +1,12 @@
+package net.jami.jams.common.configuration;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class ServerConfiguration {
+
+    private int port;
+
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/cryptoengineapi/CertificateAuthority.java b/jams-common/src/main/java/net/jami/jams/common/cryptoengineapi/CertificateAuthority.java
new file mode 100644
index 0000000000000000000000000000000000000000..29fb239d8b8c7546d2b7dc9921d7cdbe790e7407
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/cryptoengineapi/CertificateAuthority.java
@@ -0,0 +1,22 @@
+package net.jami.jams.common.cryptoengineapi;
+
+import net.jami.jams.common.objects.devices.Device;
+import net.jami.jams.common.objects.requests.RevocationRequest;
+import net.jami.jams.common.objects.system.SystemAccount;
+import net.jami.jams.common.objects.user.User;
+import org.bouncycastle.cert.X509CRLHolder;
+import org.bouncycastle.cert.ocsp.OCSPReq;
+import org.bouncycastle.cert.ocsp.OCSPResp;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+public interface CertificateAuthority {
+    //Return a signed X509 certificate based on various constraints.
+    void init(String domain, String signingAlgorithm, SystemAccount ca, SystemAccount ocsp);
+    User getSignedCertificate(User user);
+    Device getSignedCertificate(User user, Device device);
+    SystemAccount getSignedCertificate(SystemAccount systemAccount);
+    void revokeCertificate(RevocationRequest revocationRequest);
+    AtomicReference<X509CRLHolder> getLatestCRL();
+    OCSPResp getOCSPResponse(OCSPReq ocspRequest);
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/dao/SelectStatementBuilder.java b/jams-common/src/main/java/net/jami/jams/common/dao/SelectStatementBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5d53104dbda41eeb4b04e4ce6aae7d9189cfa9c
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/dao/SelectStatementBuilder.java
@@ -0,0 +1,42 @@
+package net.jami.jams.common.dao;
+
+import net.jami.jams.common.dao.connectivity.SQLConnection;
+
+import java.sql.PreparedStatement;
+import java.util.List;
+
+public class SelectStatementBuilder {
+
+    public static PreparedStatement buildStatement(String table, List<StatementElement> statementElements,
+                                        SQLConnection connection) throws Exception
+    {
+        PreparedStatement ps = null;
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("SELECT * FROM ").append(table);
+        if(statementElements != null) {
+            stringBuilder.append(" WHERE ");
+            for (StatementElement statementElement : statementElements) {
+                stringBuilder
+                        .append(statementElement.getColumn())
+                        .append(" ")
+                        .append(statementElement.getOperator())
+                        .append(" ")
+                        .append("?")
+                        .append(" ")
+                        .append(statementElement.getNextStatementRelation());
+            }
+            ps = connection.getConnection().prepareStatement(stringBuilder.toString());
+            int i = 1;
+            for (StatementElement statementElement : statementElements) {
+                ps.setString(i, statementElement.getValue());
+                i++;
+            }
+        }
+        else{
+            ps = connection.getConnection().prepareStatement(stringBuilder.toString());
+        }
+        return ps;
+    }
+
+
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/dao/StatementElement.java b/jams-common/src/main/java/net/jami/jams/common/dao/StatementElement.java
new file mode 100644
index 0000000000000000000000000000000000000000..541e48582b5a625c5d6fd9d8c1b6ea8bb58f05df
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/dao/StatementElement.java
@@ -0,0 +1,17 @@
+package net.jami.jams.common.dao;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class StatementElement {
+    private String column;
+    private String operator;
+    private String value;
+    private String nextStatementRelation;
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/dao/StatementList.java b/jams-common/src/main/java/net/jami/jams/common/dao/StatementList.java
new file mode 100644
index 0000000000000000000000000000000000000000..1b4421dc0af3aa0003480c8042b54bb084d500ee
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/dao/StatementList.java
@@ -0,0 +1,19 @@
+package net.jami.jams.common.dao;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Getter
+@Setter
+public class StatementList {
+
+    private List<StatementElement> statements = new ArrayList<>();
+
+    public void addStatement(StatementElement statementElement){
+        statements.add(statementElement);
+    }
+
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/dao/UpdateStatementBuilder.java b/jams-common/src/main/java/net/jami/jams/common/dao/UpdateStatementBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..d67273725f6635649bad1b7e382bd1101b74f8da
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/dao/UpdateStatementBuilder.java
@@ -0,0 +1,44 @@
+package net.jami.jams.common.dao;
+
+import net.jami.jams.common.dao.connectivity.SQLConnection;
+
+import java.sql.PreparedStatement;
+import java.util.List;
+
+public class UpdateStatementBuilder {
+    public static PreparedStatement buildStatement(String table, List<StatementElement> updateElements,
+                                                   List<StatementElement> conditionalElements,
+                                                   SQLConnection connection) throws Exception {
+        PreparedStatement ps = null;
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("UPDATE ").append(table).append(" SET ");
+        for (int i = 0; i < updateElements.size(); i++) {
+            StatementElement statementElement = updateElements.get(i);
+            stringBuilder
+                    .append(statementElement.getColumn())
+                    .append(" = ")
+                    .append("?");
+            if (i != updateElements.size() - 1) stringBuilder.append(",");
+        }
+        stringBuilder.append(" WHERE ");
+        for (StatementElement statementElement : conditionalElements) {
+            stringBuilder
+                    .append(statementElement.getColumn())
+                    .append(" ")
+                    .append(statementElement.getOperator())
+                    .append(" ")
+                    .append("?")
+                    .append(" ")
+                    .append(statementElement.getNextStatementRelation());
+        }
+        ps = connection.getConnection().prepareStatement(stringBuilder.toString());
+        //Now we have to feed this all the elements it hsould have.
+        updateElements.addAll(conditionalElements);
+        int i = 1;
+        for (StatementElement statementElement : updateElements) {
+            ps.setString(i, statementElement.getValue());
+            i++;
+        }
+        return ps;
+    }
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/dao/connectivity/ConnectionPool.java b/jams-common/src/main/java/net/jami/jams/common/dao/connectivity/ConnectionPool.java
new file mode 100644
index 0000000000000000000000000000000000000000..e4ae8891f36fb02f47afcefae15469df5cc47809
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/dao/connectivity/ConnectionPool.java
@@ -0,0 +1,60 @@
+package net.jami.jams.common.dao.connectivity;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.sql.DriverManager;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+@Slf4j
+public class ConnectionPool {
+
+    private final ConcurrentLinkedQueue<SQLConnection> connections = new ConcurrentLinkedQueue<>();
+    private String connectionURI;
+
+    //Load Derby class.
+
+    public ConnectionPool(String connectionURI) {
+        try {
+            Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
+            this.connectionURI = connectionURI;
+            while (connections.size() < 10) {
+                try {
+                    connections.add(new SQLConnection(DriverManager.getConnection(connectionURI)));
+                } catch (Exception e) {
+                    log.error("Could not create a link with the database: " + e.toString());
+                }
+            }
+        }
+        catch (Exception e){
+            log.error("Could not load ApacheDerby class driver: " + e.toString());
+        }
+    }
+
+    public SQLConnection getConnection(){
+        try {
+            SQLConnection sqlConnection = null;
+            while (sqlConnection == null) {
+                sqlConnection = connections.poll();
+            }
+            if (sqlConnection.isStale()) sqlConnection = new SQLConnection(DriverManager.getConnection(connectionURI));
+            return sqlConnection;
+        }
+        catch (Exception e){
+            log.info("A connection could not be obtained with error " + e.toString());
+            return null;
+        }
+    }
+
+    public void returnConnection(SQLConnection sqlConnection){
+        try {
+            if (sqlConnection.isStale()){
+                sqlConnection.getConnection().close();
+                sqlConnection = new SQLConnection(DriverManager.getConnection(connectionURI));
+            }
+            connections.add(sqlConnection);
+        }
+        catch (Exception e){
+            log.error("Could not return a connection!");
+        }
+    }
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/dao/connectivity/SQLConnection.java b/jams-common/src/main/java/net/jami/jams/common/dao/connectivity/SQLConnection.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ee1e227cd2e952917d63c88d7e08e691bbe0608
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/dao/connectivity/SQLConnection.java
@@ -0,0 +1,25 @@
+package net.jami.jams.common.dao.connectivity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.sql.Connection;
+
+@Getter
+@Setter
+public class SQLConnection {
+
+    private static final long TIMEOUT = 30_000L;
+    private Connection connection;
+    private long creationTimestamp;
+
+    public SQLConnection(Connection connection) {
+        this.connection = connection;
+        this.creationTimestamp = System.currentTimeMillis();
+    }
+
+    public boolean isStale(){
+        return (System.currentTimeMillis() - creationTimestamp > TIMEOUT);
+    }
+
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/jami/NameRegistrationRequest.java b/jams-common/src/main/java/net/jami/jams/common/jami/NameRegistrationRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c84f10d349db0282943e8d2852cc1fba027c879d
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/jami/NameRegistrationRequest.java
@@ -0,0 +1,13 @@
+package net.jami.jams.common.jami;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class NameRegistrationRequest {
+    public String addr;
+    public String owner;
+    public String signature;
+    public String publickey;
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/contacts/Contact.java b/jams-common/src/main/java/net/jami/jams/common/objects/contacts/Contact.java
new file mode 100644
index 0000000000000000000000000000000000000000..7b8e46406ad79763e1907980620bdd34b884d124
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/contacts/Contact.java
@@ -0,0 +1,9 @@
+package net.jami.jams.common.objects.contacts;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class Contact {
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/devices/Device.java b/jams-common/src/main/java/net/jami/jams/common/objects/devices/Device.java
new file mode 100644
index 0000000000000000000000000000000000000000..77092714e7e8f0fbab7b0fc60e0eb7991b11640c
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/devices/Device.java
@@ -0,0 +1,51 @@
+package net.jami.jams.common.objects.devices;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import net.jami.jams.common.objects.roots.X509Entity;
+import net.jami.jams.common.serialization.database.DatabaseObject;
+import net.jami.jams.common.utils.X509Utils;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class Device extends X509Entity implements DatabaseObject {
+
+    private String deviceId;
+    private String owner;
+    private String displayName;
+
+    public Device(ResultSet rs) throws Exception {
+        this.deviceId = rs.getString("deviceId");
+        this.owner = rs.getString("owner");
+        this.displayName = rs.getString("displayName");
+        this.setCertificate(X509Utils.getCertificateFromPEMString(rs.getString("certificate")));
+        this.setPrivateKey(X509Utils.getKeyFromPEMString(rs.getString("privatekey")));
+    }
+
+    @Override
+    public PreparedStatement getInsert(PreparedStatement ps) throws Exception {
+        ps.setString(1, deviceId);
+        ps.setString(2, owner);
+        ps.setString(3,displayName);
+        ps.setString(4, X509Utils.getPEMStringFromCertificate(this.getCertificate()));
+        ps.setString(5, X509Utils.getPEMStringFromPrivateKey(this.getPrivateKey()));
+        return ps;
+    }
+
+    @Override
+    public PreparedStatement getDelete(PreparedStatement ps) throws Exception {
+        return null;
+    }
+
+    @Override
+    public PreparedStatement getUpdate(PreparedStatement ps) throws Exception {
+        return null;
+    }
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/requests/EnrollementRequest.java b/jams-common/src/main/java/net/jami/jams/common/objects/requests/EnrollementRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..dda3da98550be76885288c60203fbc0ed45255e8
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/requests/EnrollementRequest.java
@@ -0,0 +1,13 @@
+package net.jami.jams.common.objects.requests;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+
+@Getter
+@Setter
+public class EnrollementRequest {
+
+    private PKCS10CertificationRequest csr;
+
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationRequest.java b/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e8cd1d01fbafd5bb61a55fca885b81d6cebcff61
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationRequest.java
@@ -0,0 +1,13 @@
+package net.jami.jams.common.objects.requests;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigInteger;
+
+@Getter
+@Setter
+public class RevocationRequest {
+    private BigInteger identifier;
+    private RevocationType revocationType;
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationType.java b/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationType.java
new file mode 100644
index 0000000000000000000000000000000000000000..713723f1cfa49bfe9040d8036e1e9ba514720138
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationType.java
@@ -0,0 +1,6 @@
+package net.jami.jams.common.objects.requests;
+
+public enum RevocationType {
+    USER,
+    DEVICE
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/roots/BlockchainEntity.java b/jams-common/src/main/java/net/jami/jams/common/objects/roots/BlockchainEntity.java
new file mode 100644
index 0000000000000000000000000000000000000000..4c905fc6d4fcefdc77139669e391bba19912d706
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/roots/BlockchainEntity.java
@@ -0,0 +1,8 @@
+package net.jami.jams.common.objects.roots;
+
+public interface BlockchainEntity {
+    String getAddress();
+    void   setAddress(String address);
+    String getKey();
+    void   setKey(String key);
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/roots/X509Entity.java b/jams-common/src/main/java/net/jami/jams/common/objects/roots/X509Entity.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4de1279bf1db6fc943585506d24f1c5e9e46329
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/roots/X509Entity.java
@@ -0,0 +1,18 @@
+package net.jami.jams.common.objects.roots;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+@Getter
+@Setter
+public class X509Entity {
+    private X509Certificate certificate;
+    private PrivateKey privateKey;
+    //These can be null because they are only used if this is a request.
+    private X509Fields x509Fields;
+    private PKCS10CertificationRequest certificationRequest;
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/roots/X509Fields.java b/jams-common/src/main/java/net/jami/jams/common/objects/roots/X509Fields.java
new file mode 100644
index 0000000000000000000000000000000000000000..fc635c69b92fe1809b43ae070e420395c1883cae
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/roots/X509Fields.java
@@ -0,0 +1,29 @@
+package net.jami.jams.common.objects.roots;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class X509Fields {
+
+    private String commonName;
+    private String country;
+    private String state;
+    private String organization;
+    private String organizationUnit;
+
+    public String getDN(){
+        StringBuilder stringBuilder = new StringBuilder();
+        //This makes no sense without a Common Name;
+        if(commonName != null) {
+            stringBuilder.append("CN=").append(commonName);
+        } else return null;
+        if(country != null) stringBuilder.append(",").append("C=").append(country);
+        if(state != null) stringBuilder.append(",").append("ST=").append(state);
+        if(organization != null) stringBuilder.append(",").append("O=").append(organization);
+        if(organizationUnit != null) stringBuilder.append(",").append("OU=").append(organizationUnit);
+        return stringBuilder.toString();
+    }
+
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/system/SystemAccount.java b/jams-common/src/main/java/net/jami/jams/common/objects/system/SystemAccount.java
new file mode 100644
index 0000000000000000000000000000000000000000..4764ec09a8f3b327da9805e458829ef50eb4a299
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/system/SystemAccount.java
@@ -0,0 +1,12 @@
+package net.jami.jams.common.objects.system;
+
+import lombok.Getter;
+import lombok.Setter;
+import net.jami.jams.common.objects.roots.X509Entity;
+
+@Getter
+@Setter
+public class SystemAccount extends X509Entity {
+
+    private SystemAccountType systemAccountType;
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/system/SystemAccountType.java b/jams-common/src/main/java/net/jami/jams/common/objects/system/SystemAccountType.java
new file mode 100644
index 0000000000000000000000000000000000000000..b85bb07f298b66a2c07716d2da5c8b36c4014d05
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/system/SystemAccountType.java
@@ -0,0 +1,6 @@
+package net.jami.jams.common.objects.system;
+
+public enum SystemAccountType {
+    CA,
+    OCSP
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/user/AccessLevel.java b/jams-common/src/main/java/net/jami/jams/common/objects/user/AccessLevel.java
new file mode 100644
index 0000000000000000000000000000000000000000..e579388f2cdaa952f248322bc132788fcf89fea3
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/user/AccessLevel.java
@@ -0,0 +1,6 @@
+package net.jami.jams.common.objects.user;
+
+public enum  AccessLevel {
+    ADMIN,
+    USER
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/user/User.java b/jams-common/src/main/java/net/jami/jams/common/objects/user/User.java
new file mode 100644
index 0000000000000000000000000000000000000000..09ec4da70e29031fcc9775f8b5c2e787b4d9a6f3
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/user/User.java
@@ -0,0 +1,107 @@
+package net.jami.jams.common.objects.user;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import net.jami.jams.common.authentication.AuthenticationSourceType;
+import net.jami.jams.common.objects.roots.BlockchainEntity;
+import net.jami.jams.common.objects.roots.X509Entity;
+import net.jami.jams.common.serialization.database.DatabaseObject;
+import net.jami.jams.common.utils.X509Utils;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class User extends X509Entity implements BlockchainEntity, DatabaseObject {
+
+    private String username;
+    private String password;
+    private AuthenticationSourceType userType;
+    private String realm; //sort of the domain.
+    private AccessLevel accessLevel = AccessLevel.USER;
+    private Boolean needsPasswordReset = false;
+
+    private String ethAddress;
+    private String ethKey;
+    private String jamiId;
+
+    public User(ResultSet rs) throws Exception {
+        this.username = rs.getString("username");
+        this.password = rs.getString("password");
+        this.userType = AuthenticationSourceType.valueOf(rs.getString("userType"));
+        this.accessLevel = AccessLevel.valueOf(rs.getString("accessLevel"));
+        this.needsPasswordReset = Boolean.parseBoolean(rs.getString("needsPasswordReset"));
+        this.realm = rs.getString("realm");
+        this.ethAddress = rs.getString("ethAddress");
+        this.ethKey = rs.getString("ethKey");
+        this.jamiId = rs.getString("jamiId");
+        if(rs.getString("certificate") != null && !rs.getString("certificate").isBlank()) {
+            this.setCertificate(X509Utils.getCertificateFromPEMString(rs.getString("certificate")));
+        }
+        if(rs.getString("privatekey") != null && !rs.getString("privatekey").isBlank()) {
+            this.setPrivateKey(X509Utils.getKeyFromPEMString(rs.getString("privatekey")));
+        }
+    }
+
+    @Override
+    public String getAddress() {
+        return this.ethAddress;
+    }
+
+    @Override
+    public void setAddress(String address) {
+        this.ethAddress = address;
+    }
+
+    @Override
+    public String getKey() {
+        return ethKey;
+    }
+
+    @Override
+    public void setKey(String key) {
+        this.ethKey = key;
+    }
+
+    @Override
+    public PreparedStatement getInsert(PreparedStatement ps) throws Exception {
+        ps.setString(1, username);
+        //We don't store the user's password if he is remote.
+        if(userType != null && userType.equals(AuthenticationSourceType.LOCAL)) ps.setString(2, password);
+        else ps.setString(2, null);
+        if(userType != null) ps.setString(3, userType.toString());
+        else ps.setString(3, null);
+        ps.setString(4, realm);
+        ps.setString(5, ethAddress);
+        ps.setString(6, ethKey);
+        ps.setString(7, jamiId);
+        if (this.getCertificate() != null) {
+            ps.setString(8, X509Utils.getPEMStringFromCertificate(this.getCertificate()));
+        } else {
+            ps.setString(8, "");
+        }
+        if (this.getPrivateKey() != null) {
+            ps.setString(9, X509Utils.getPEMStringFromPrivateKey(this.getPrivateKey()));
+        } else {
+            ps.setString(9, "");
+        }
+        ps.setString(10,accessLevel.toString());
+        ps.setString(11,needsPasswordReset.toString());
+        return ps;
+    }
+
+    @Override
+    public PreparedStatement getDelete(PreparedStatement ps) throws Exception {
+        return null;
+    }
+
+    @Override
+    public PreparedStatement getUpdate(PreparedStatement ps) throws Exception {
+        return null;
+    }
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java b/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java
new file mode 100644
index 0000000000000000000000000000000000000000..92ce57987b27a24d981edb5d1ccc8284b8bc8e0f
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java
@@ -0,0 +1,14 @@
+package net.jami.jams.common.objects.user;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class UserProfile {
+    private String firstName;
+    private String lastName;
+    private String phoneNumber;
+    private String profilePicture;
+    private String email;
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/serialization/JsoniterRegistry.java b/jams-common/src/main/java/net/jami/jams/common/serialization/JsoniterRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..673c85c1b0e415b86a6d3b2a5c390640f8606ad1
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/serialization/JsoniterRegistry.java
@@ -0,0 +1,31 @@
+package net.jami.jams.common.serialization;
+
+import com.jsoniter.JsonIterator;
+import com.jsoniter.output.EncodingMode;
+import com.jsoniter.output.JsonStream;
+import com.jsoniter.spi.DecodingMode;
+import com.jsoniter.spi.JsoniterSpi;
+import net.jami.jams.common.serialization.decoders.CSRDecoder;
+import net.jami.jams.common.serialization.decoders.PrivateKeyDecoder;
+import net.jami.jams.common.serialization.decoders.X509CertificateDecoder;
+import net.jami.jams.common.serialization.encoders.PrivateKeyEncoder;
+import net.jami.jams.common.serialization.encoders.X509CertificateEncoder;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+public class JsoniterRegistry {
+
+    public static void initCodecs(){
+        JsonStream.setMode(EncodingMode.DYNAMIC_MODE);
+        JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH);
+        JsoniterSpi.registerTypeDecoder(X509Certificate.class,new X509CertificateDecoder());
+        JsoniterSpi.registerTypeDecoder(PrivateKey.class,new PrivateKeyDecoder());
+        JsoniterSpi.registerTypeEncoder(X509Certificate.class,new X509CertificateEncoder());
+        JsoniterSpi.registerTypeEncoder(PrivateKey.class,new PrivateKeyEncoder());
+        JsoniterSpi.registerTypeDecoder(PKCS10CertificationRequest.class,new CSRDecoder());
+    }
+
+
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/serialization/database/DatabaseObject.java b/jams-common/src/main/java/net/jami/jams/common/serialization/database/DatabaseObject.java
new file mode 100644
index 0000000000000000000000000000000000000000..2913b16051880305f6e1ce1696a14dcc01f6e43f
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/serialization/database/DatabaseObject.java
@@ -0,0 +1,10 @@
+package net.jami.jams.common.serialization.database;
+
+import java.sql.PreparedStatement;
+
+public interface DatabaseObject {
+
+    PreparedStatement getInsert(PreparedStatement ps) throws Exception;
+    PreparedStatement getDelete(PreparedStatement ps) throws Exception;
+    PreparedStatement getUpdate(PreparedStatement ps) throws Exception;
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/CSRDecoder.java b/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/CSRDecoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..fb0da96fca7849da56acd1a492a5b763c58c60b9
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/CSRDecoder.java
@@ -0,0 +1,15 @@
+package net.jami.jams.common.serialization.decoders;
+
+import com.jsoniter.JsonIterator;
+import com.jsoniter.spi.Decoder;
+import net.jami.jams.common.utils.X509Utils;
+
+import java.io.IOException;
+
+public class CSRDecoder implements Decoder {
+
+    @Override
+    public Object decode(JsonIterator jsonIterator) throws IOException {
+        return X509Utils.getCSRFromString(jsonIterator.readString());
+    }
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/PrivateKeyDecoder.java b/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/PrivateKeyDecoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..dc021598b787fd892c0bd651c82a101e2338d960
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/PrivateKeyDecoder.java
@@ -0,0 +1,15 @@
+package net.jami.jams.common.serialization.decoders;
+
+import com.jsoniter.JsonIterator;
+import com.jsoniter.spi.Decoder;
+import net.jami.jams.common.utils.X509Utils;
+
+import java.io.IOException;
+
+public class PrivateKeyDecoder implements Decoder {
+
+    @Override
+    public Object decode(JsonIterator jsonIterator) throws IOException {
+        return X509Utils.getKeyFromPEMString(jsonIterator.readString());
+    }
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/X509CertificateDecoder.java b/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/X509CertificateDecoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..d01919ac46df370d0d51fc0be8244485e4a86cfe
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/X509CertificateDecoder.java
@@ -0,0 +1,15 @@
+package net.jami.jams.common.serialization.decoders;
+
+import com.jsoniter.JsonIterator;
+import com.jsoniter.spi.Decoder;
+import net.jami.jams.common.utils.X509Utils;
+
+import java.io.IOException;
+
+public class X509CertificateDecoder implements Decoder {
+
+    @Override
+    public Object decode(JsonIterator jsonIterator) throws IOException {
+        return X509Utils.getCertificateFromPEMString(jsonIterator.readString());
+    }
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/serialization/encoders/PrivateKeyEncoder.java b/jams-common/src/main/java/net/jami/jams/common/serialization/encoders/PrivateKeyEncoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..01231a4b844ebf2b70087b66e43afa43898ab881
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/serialization/encoders/PrivateKeyEncoder.java
@@ -0,0 +1,16 @@
+package net.jami.jams.common.serialization.encoders;
+
+import com.jsoniter.output.JsonStream;
+import com.jsoniter.spi.Encoder;
+import net.jami.jams.common.utils.X509Utils;
+
+import java.io.IOException;
+import java.security.PrivateKey;
+
+public class PrivateKeyEncoder implements Encoder {
+
+    @Override
+    public void encode(Object o, JsonStream jsonStream) throws IOException {
+        jsonStream.write(("\"" + X509Utils.getPEMStringFromPrivateKey((PrivateKey) o) + "\"").getBytes());
+    }
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/serialization/encoders/X509CertificateEncoder.java b/jams-common/src/main/java/net/jami/jams/common/serialization/encoders/X509CertificateEncoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..0c39ca54007361dbceb42bf217e68e04465a8a55
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/serialization/encoders/X509CertificateEncoder.java
@@ -0,0 +1,16 @@
+package net.jami.jams.common.serialization.encoders;
+
+import com.jsoniter.output.JsonStream;
+import com.jsoniter.spi.Encoder;
+import net.jami.jams.common.utils.X509Utils;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+
+public class X509CertificateEncoder implements Encoder {
+
+    @Override
+    public void encode(Object o, JsonStream jsonStream) throws IOException {
+        jsonStream.write(("\"" + X509Utils.getPEMStringFromCertificate((X509Certificate) o) + "\"").getBytes());
+    }
+}
diff --git a/jams-common/src/main/java/net/jami/jams/common/utils/X509Utils.java b/jams-common/src/main/java/net/jami/jams/common/utils/X509Utils.java
new file mode 100644
index 0000000000000000000000000000000000000000..3eb5068a61730a30a33b636ff1a49b62fbba5ed5
--- /dev/null
+++ b/jams-common/src/main/java/net/jami/jams/common/utils/X509Utils.java
@@ -0,0 +1,96 @@
+package net.jami.jams.common.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+
+@Slf4j
+public class X509Utils {
+
+    private static final String PVK_HEADER = "-----BEGIN PRIVATE KEY-----\n";
+    private static final String PVK_TAIL = "\n-----END PRIVATE KEY-----";
+    private static final String CERT_HEADER = "-----BEGIN CERTIFICATE-----\n";
+    private static final String CERT_TAIL = "\n-----END CERTIFICATE-----";
+
+    public static PrivateKey getKeyFromPEMString(String keyString) {
+        try {
+            PEMParser parser = new PEMParser(new StringReader(keyString));
+            Object parsedObject = parser.readObject();
+            if (parsedObject instanceof PEMKeyPair) {
+                PEMKeyPair pk = (PEMKeyPair) parsedObject;
+                PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pk.getPrivateKeyInfo().getEncoded());
+                return KeyFactory.getInstance("RSA").generatePrivate(keySpec);
+            } else {
+                JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
+                return converter.getPrivateKey((PrivateKeyInfo) parsedObject);
+            }
+        } catch (Exception e) {
+            log.error("An error has occured trying to convert the PEM to PrivateKey, stack trace: " + e.toString());
+            return null;
+        }
+    }
+
+    public static X509Certificate getCertificateFromPEMString(String certificateString) {
+        try {
+            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+            InputStream inputStream = new ByteArrayInputStream(certificateString.getBytes());
+            return (X509Certificate) certificateFactory.generateCertificate(inputStream);
+        } catch (Exception e) {
+            log.error("An error has occured trying to convert the PEM to X509, stack trace: " + e.toString());
+            return null;
+        }
+    }
+
+    public static String getPEMStringFromPrivateKey(PrivateKey privateKey) {
+        StringBuilder stringBuilder = new StringBuilder();
+        try {
+            stringBuilder.append(PVK_HEADER);
+            stringBuilder.append(Base64.getEncoder().encodeToString(privateKey.getEncoded()));
+            stringBuilder.append(PVK_TAIL);
+            return stringBuilder.toString();
+        } catch (Exception e) {
+            log.error("An error has occured trying to convert the Private Key to PEM, stack trace: " + e.toString());
+            return null;
+        }
+    }
+
+    public static String getPEMStringFromCertificate(X509Certificate certificate) {
+        StringBuilder stringBuilder = new StringBuilder();
+        try {
+            stringBuilder.append(CERT_HEADER);
+            stringBuilder.append(Base64.getEncoder().encodeToString(certificate.getEncoded()));
+            stringBuilder.append(CERT_TAIL);
+            return stringBuilder.toString();
+        } catch (Exception e) {
+            log.error("An error has occured trying to convert the Private Key to PEM, stack trace: " + e.toString());
+            return null;
+        }
+    }
+
+    public static PKCS10CertificationRequest getCSRFromString(String pkcs10StringRequest) {
+        try {
+            ByteArrayInputStream pemStream = new ByteArrayInputStream(pkcs10StringRequest.getBytes(StandardCharsets.UTF_8));
+            PEMParser pemParser = new PEMParser(new BufferedReader(new InputStreamReader(pemStream)));
+            Object parsedObj = pemParser.readObject();
+            if (parsedObj instanceof PKCS10CertificationRequest) return (PKCS10CertificationRequest) parsedObj;
+            log.error("The request does not seem to be a CSR request!");
+            return null;
+        } catch (Exception e) {
+            log.error("An error has occured trying to convert a string to a PKCS10 Certification Request, stack trace: " + e.toString());
+            return null;
+        }
+    }
+
+}
diff --git a/jams-common/src/test/java/net/jami/jams/common/serialization/JsoniterRegistryTest.java b/jams-common/src/test/java/net/jami/jams/common/serialization/JsoniterRegistryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a11bfb67c72f4eb8deeb91fe1b5e52d2a6a84abf
--- /dev/null
+++ b/jams-common/src/test/java/net/jami/jams/common/serialization/JsoniterRegistryTest.java
@@ -0,0 +1,60 @@
+package net.jami.jams.common.serialization;
+
+import com.jsoniter.JsonIterator;
+import com.jsoniter.output.JsonStream;
+import net.jami.jams.common.objects.requests.EnrollementRequest;
+import net.jami.jams.common.objects.roots.X509Entity;
+import net.jami.jams.common.utils.X509Utils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.io.InputStream;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+class JsoniterRegistryTest {
+
+    static String strPrivateKey;
+    static String strCertificate;
+    static String strPkcs10Request;
+
+    @BeforeAll
+    public static void loadProps(){
+        try {
+            JsoniterRegistry.initCodecs();
+            InputStream path;
+            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+            path = classLoader.getResourceAsStream("cakey.txt");
+            strPrivateKey = new String(path.readAllBytes());
+            path = classLoader.getResourceAsStream("cacert.txt");
+            strCertificate = new String(path.readAllBytes());
+            path = classLoader.getResourceAsStream("pkcs10request.txt");
+            strPkcs10Request = new String(path.readAllBytes());
+        }
+        catch (Exception e){
+            fail("Could not set-up resources for test!");
+        }
+    }
+
+    @Test
+    void X509SerializationAndDeserialization(){
+        X509Entity entity = new X509Entity();
+        entity.setCertificate(X509Utils.getCertificateFromPEMString(strCertificate));
+        entity.setPrivateKey(X509Utils.getKeyFromPEMString(strPrivateKey));
+        String x = JsonStream.serialize(entity);
+        Assertions.assertNotNull(x,"Serialization failed!");
+        entity = null;
+        entity = JsonIterator.deserialize(x,X509Entity.class);
+        Assertions.assertNotNull(entity.getCertificate(),"Certificate was not parsed!");
+        Assertions.assertNotNull(entity.getPrivateKey(),"Private key was not parsed!");
+    }
+
+    @Test
+    void CSRDeserializeTest(){
+        String input = "{\"csr\":\"" + strPkcs10Request + "\"}";
+        EnrollementRequest request = JsonIterator.deserialize(input,EnrollementRequest.class);
+        Assertions.assertNotNull(request.getCsr(),"CSR Should not have been null!");
+    }
+
+}
\ No newline at end of file
diff --git a/jams-common/src/test/java/net/jami/jams/common/utils/X509UtilsTest.java b/jams-common/src/test/java/net/jami/jams/common/utils/X509UtilsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e986a52d475e4df946403132eced09c998618db
--- /dev/null
+++ b/jams-common/src/test/java/net/jami/jams/common/utils/X509UtilsTest.java
@@ -0,0 +1,83 @@
+package net.jami.jams.common.utils;
+
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.io.InputStream;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+class X509UtilsTest {
+
+    static String strPrivateKey;
+    static String strCertificate;
+    static String strPkcs10Request;
+
+    @BeforeAll
+    public static void loadProps(){
+        try {
+            InputStream path;
+            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+            path = classLoader.getResourceAsStream("cakey.txt");
+            strPrivateKey = new String(path.readAllBytes());
+            path = classLoader.getResourceAsStream("cacert.txt");
+            strCertificate = new String(path.readAllBytes());
+            path = classLoader.getResourceAsStream("pkcs10request.txt");
+            strPkcs10Request = new String(path.readAllBytes());
+        }
+        catch (Exception e){
+            fail("Could not set-up resources for test!");
+        }
+    }
+
+    @Test
+    void getKeyFromPEMString() {
+        PrivateKey privateKey;
+        privateKey = X509Utils.getKeyFromPEMString(strPrivateKey);
+        Assertions.assertNotNull(privateKey,"Could not decode a correctly formatted private key!");
+        privateKey = X509Utils.getKeyFromPEMString(strPrivateKey.substring(10));
+        Assertions.assertNull(privateKey,"Decoded a private key from an incorrect string!");
+    }
+
+    @Test
+    void getCertificateFromPEMString() {
+        X509Certificate x509Certificate;
+        x509Certificate = X509Utils.getCertificateFromPEMString(strCertificate);
+        Assertions.assertNotNull(x509Certificate,"Could not decoded a correctly formatted certificate!");
+        x509Certificate = X509Utils.getCertificateFromPEMString(strCertificate.substring(25));
+        Assertions.assertNull(x509Certificate,"Decoded certificate from an incorrect string!");
+    }
+
+    @Test
+    void getPEMStringFromPrivateKey() {
+        PrivateKey privateKey;
+        privateKey = X509Utils.getKeyFromPEMString(strPrivateKey);
+        Assertions.assertNotNull(privateKey,"Could not decode a correctly formatted private key!");
+        String str = X509Utils.getPEMStringFromPrivateKey(privateKey);
+        PrivateKey privateKey1 = X509Utils.getKeyFromPEMString(str);
+        Assertions.assertEquals(privateKey,privateKey1,"Keys do not match - although they should!");
+    }
+
+    @Test
+    void getPEMStringFromCertificate() {
+        X509Certificate x509Certificate;
+        x509Certificate = X509Utils.getCertificateFromPEMString(strCertificate);
+        Assertions.assertNotNull(x509Certificate,"Could not decoded a correctly formatted certificate!");
+        String str = X509Utils.getPEMStringFromCertificate(x509Certificate);
+        X509Certificate x509Certificate1 = X509Utils.getCertificateFromPEMString(str);
+        Assertions.assertEquals(x509Certificate,x509Certificate1,"Certificates do not match - although they should!");
+    }
+
+    @Test
+    void getCSRFromString() {
+        PKCS10CertificationRequest certificationRequest;
+        certificationRequest = X509Utils.getCSRFromString(strPkcs10Request);
+        Assertions.assertNotNull(certificationRequest,"Certification request should have been parsed correctly!");
+        certificationRequest = X509Utils.getCSRFromString(strPkcs10Request.substring(23));
+        Assertions.assertNull(certificationRequest,"Certification request should not have been parsed!");
+    }
+}
\ No newline at end of file
diff --git a/jams-server/src/main/java/net/jami/jams/server/Server.java b/jams-server/src/main/java/net/jami/jams/server/Server.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec8b11c87a3efeb112f93324ccb3bfe9967f2f14
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/Server.java
@@ -0,0 +1,63 @@
+package net.jami.jams.server;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.datastore.main.DataStore;
+import net.jami.jams.common.authentication.AuthenticationSource;
+import net.jami.jams.common.cryptoengineapi.CertificateAuthority;
+import net.jami.jams.common.serialization.JsoniterRegistry;
+import net.jami.jams.server.core.TomcatLauncher;
+import net.jami.jams.server.core.usermanagement.UserAuthenticationModule;
+import net.jami.jams.server.startup.CryptoEngineLoader;
+import net.jami.jams.server.startup.LibraryLoader;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@Slf4j
+public class Server {
+
+    public static List<String> authModuleClasses = new ArrayList<>();
+    public static HashMap<String, AuthenticationSource> authenticationSources = new HashMap<>();
+    public static AtomicBoolean isInstalled = new AtomicBoolean(false);
+
+    static {
+        JsoniterRegistry.initCodecs();
+    }
+
+    public static DataStore dataStore;
+    //This one gets loaded via JAR, to make it more flexible.
+    public static CertificateAuthority certificateAuthority;
+    public static UserAuthenticationModule userAuthenticationModule;
+
+    public static void main(String[] args){
+        //Pre-load the libraries we should pre-load.
+        LibraryLoader.loadlibs("libs");
+        //Step 1: Create the data store.
+        dataStore = new DataStore("jdbc:derby:jams;create=true");
+        userAuthenticationModule = new UserAuthenticationModule();
+        //Test block
+        //Step 2: if the server is initialized,
+        certificateAuthority = CryptoEngineLoader.loadCryptoEngine(dataStore);
+        //nasty injection to test this flow.
+
+
+        //Okay this is me cheating again heavily, just for testing purposes.
+        //try {
+        //    InputStream path;
+        //    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        //    path = classLoader.getResourceAsStream("ldapconfig.json");
+        //    userAuthenticationModule.attachLDAPAuthSource(new String(path.readAllBytes()));
+        //}
+        //catch (Exception e){
+        //    log.error("Could not load and inject active directory connector with error: " + e.toString());
+        //}
+
+        //Start tomcat.
+        TomcatLauncher tomcatLauncher = new TomcatLauncher();
+        tomcatLauncher.startServer();
+    }
+
+
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/core/TomcatConnectorFactory.java b/jams-server/src/main/java/net/jami/jams/server/core/TomcatConnectorFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..578ebead2ead816f6f2579eec8a99d0d3fd47edb
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/core/TomcatConnectorFactory.java
@@ -0,0 +1,56 @@
+package net.jami.jams.server.core;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.catalina.connector.Connector;
+
+import java.io.File;
+
+@Slf4j
+public class TomcatConnectorFactory {
+
+    public static Connector getSSLConnectorWithTrustStore(String certificateFile, String keyFile, int port) {
+        Connector connector = new Connector();
+        connector.setPort(port);
+        connector.setSecure(true);
+        connector.setScheme("https");
+        connector.setAttribute("protocol", "org.apache.coyote.http11.Http11NioProtocol");
+        connector.setAttribute("SSLCertificateFile", System.getProperty("user.dir") + File.separator + certificateFile);
+        connector.setAttribute("SSLCertificateKeyFile", System.getProperty("user.dir") + File.separator + keyFile);
+        connector.setAttribute("truststoreFile",System.getProperty("user.dir") + File.separator  + "keystore.jks");
+        connector.setAttribute("clientAuth","optional");
+        connector.setAttribute("truststorePassword","changeit");
+        connector.setAttribute("protocol", "HTTP/1.1");
+        connector.setAttribute("sslProtocol", "TLSv1.3");
+        connector.setAttribute("maxThreads", "200");
+        connector.setAttribute("SSLEnabled", "true");
+        return connector;
+    }
+
+    public static Connector getSSLConnectorWithoutTrustStore(String certificateFile, String keyFile, int port) {
+        Connector connector = new Connector();
+        connector.setPort(port);
+        connector.setSecure(true);
+        connector.setScheme("https");
+        connector.setAttribute("protocol", "org.apache.coyote.http11.Http11NioProtocol");
+        connector.setAttribute("SSLCertificateFile", System.getProperty("user.dir") + File.separator + certificateFile);
+        connector.setAttribute("SSLCertificateKeyFile", System.getProperty("user.dir") + File.separator + keyFile);
+        connector.setAttribute("protocol", "HTTP/1.1");
+        connector.setAttribute("sslProtocol", "TLSv1.3");
+        connector.setAttribute("maxThreads", "200");
+        connector.setAttribute("SSLEnabled", "true");
+        return connector;
+    }
+
+
+    public static Connector getNoSSLConnector(int port) {
+        Connector connector = new Connector();
+        connector.setPort(port);
+        connector.setScheme("http");
+        connector.setAttribute("protocol", "org.apache.coyote.http11.Http11NioProtocol");
+        connector.setAttribute("protocol", "HTTP/1.1");
+        connector.setAttribute("maxThreads", "200");
+        return connector;
+    }
+
+
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/core/TomcatLauncher.java b/jams-server/src/main/java/net/jami/jams/server/core/TomcatLauncher.java
new file mode 100644
index 0000000000000000000000000000000000000000..5b713d97595b403612041975c0e400000a7d3f1c
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/core/TomcatLauncher.java
@@ -0,0 +1,57 @@
+package net.jami.jams.server.core;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.server.Server;
+import org.apache.catalina.WebResourceRoot;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.webresources.DirResourceSet;
+import org.apache.catalina.webresources.JarResourceSet;
+import org.apache.catalina.webresources.StandardRoot;
+
+import java.io.File;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+
+//This class boots the tomcat server which provides the subsystem
+//for the API calls.
+@Slf4j
+public class TomcatLauncher {
+
+    private static final Tomcat tomcat = new Tomcat();
+    private static StandardContext context;
+
+    public void startServer() {
+        tomcat.getService().addConnector(TomcatConnectorFactory.getNoSSLConnector(8080));
+        String jarName = URLDecoder.decode(new File(Server.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getAbsolutePath(), StandardCharsets.UTF_8);
+        log.info("JAR Resource File = " + jarName);
+        context = (StandardContext) tomcat.addWebapp("", new File(System.getProperty("user.dir")).getAbsolutePath());
+        WebResourceRoot resources = new StandardRoot(context);
+        if (jarName.contains(".jar")) {
+            resources.addPreResources(new JarResourceSet(resources, "/WEB-INF/classes", jarName, "/net/jami/jams/server/servlets"));
+            resources.addPreResources(new JarResourceSet(resources, "/", jarName, "/webapp"));
+        }
+        else {
+            log.info("WARNING: You are running from your local filesystem, this makes sense only for developers!");
+            StringBuilder basePath = new StringBuilder();
+            String[] paths = System.getProperty("user.dir").split("/");
+            for(int i=0; i < paths.length-1; i++){
+                basePath.append("/").append(paths[i]);
+            }
+            basePath.append("/jams-server");
+            resources.addPreResources(new DirResourceSet(resources, "/WEB-INF/classes", basePath.toString() + "/target/classes/net/jami/jams/server/servlets", "/"));
+            resources.addPreResources(new DirResourceSet(resources, "/", basePath.toString() + "/target/classes", "/webapp"));
+
+        }
+        context.setResources(resources);
+        try {
+            tomcat.start();
+            tomcat.getServer().await();
+        } catch
+        (Exception e) {
+            log.error("Web-server has failed to start - this is critical!");
+        }
+    }
+
+
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/core/usermanagement/AuthModuleKey.java b/jams-server/src/main/java/net/jami/jams/server/core/usermanagement/AuthModuleKey.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a74cbcb2f6b98ff058e510ffe9216f13572b571
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/core/usermanagement/AuthModuleKey.java
@@ -0,0 +1,16 @@
+package net.jami.jams.server.core.usermanagement;
+
+import lombok.*;
+import net.jami.jams.common.authentication.AuthenticationSourceType;
+
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+@EqualsAndHashCode
+public class AuthModuleKey {
+
+    private String realm;
+    private AuthenticationSourceType type;
+
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/core/usermanagement/UserAuthenticationModule.java b/jams-server/src/main/java/net/jami/jams/server/core/usermanagement/UserAuthenticationModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..5cb1816f54e838e6a99b4c902a1466d385b5588f
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/core/usermanagement/UserAuthenticationModule.java
@@ -0,0 +1,83 @@
+package net.jami.jams.server.core.usermanagement;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.common.authentication.AuthenticationSource;
+import net.jami.jams.common.authentication.AuthenticationSourceType;
+import net.jami.jams.common.dao.StatementElement;
+import net.jami.jams.common.dao.StatementList;
+import net.jami.jams.common.objects.user.User;
+import net.jami.jams.server.startup.LibraryLoader;
+
+import java.util.HashMap;
+
+import static net.jami.jams.server.Server.dataStore;
+
+@Slf4j
+public class UserAuthenticationModule {
+    //This contains the DOMAIN-SOURCE.
+    //In general there is at most 2 here.
+    private final HashMap<AuthModuleKey, AuthenticationSource> authenticationSources = new HashMap<>();
+
+    //We should initialize the authentication sources here, based on configurations, by default,
+    //we only attach the basic one.
+    public UserAuthenticationModule() {
+        authenticationSources.put(new AuthModuleKey("LOCAL", AuthenticationSourceType.LOCAL), dataStore);
+    }
+
+    public void attachAuthSource(String realm, AuthenticationSourceType type, AuthenticationSource source){
+        authenticationSources.put(new AuthModuleKey(realm,type), source);
+    }
+
+    public void attachLDAPAuthSource(String settings){
+        try {
+            Class<?> cls = LibraryLoader.classLoader.loadClass("net.jami.jams.ldap.connector.LDAPConnector");
+            AuthenticationSource source = (AuthenticationSource) cls.getConstructor(String.class).newInstance(settings);
+            authenticationSources.put(new AuthModuleKey(source.getInfo().getRealm(),source.getInfo().getAuthenticationSourceType()), source);
+        }
+        catch (Exception e){
+            log.error("Could not load an ldap directory connector with reason: " + e.toString());
+        }
+    }
+
+    //Repeat the same technique for the LDAP connector.
+    public void attachADAuthSource(String settings){
+        try {
+            Class<?> cls = LibraryLoader.classLoader.loadClass("net.jami.jams.ad.connector.ADConnector");
+            AuthenticationSource source = (AuthenticationSource) cls.getConstructor(String.class).newInstance(settings);
+            authenticationSources.put(new AuthModuleKey(source.getInfo().getRealm(),source.getInfo().getAuthenticationSourceType()), source);
+        }
+        catch (Exception e){
+            log.error("Could not load an active directory connector with reason: " + e.toString());
+        }
+    }
+
+    public boolean authenticateUser(String username, String password){
+        //The user could already exist, so we pull him up.
+        if(dataStore.userExists(username)){
+            StatementList statementList = new StatementList();
+            StatementElement statementElement = new StatementElement("username","=",username,"");
+            statementList.addStatement(statementElement);
+            User user = dataStore.getUserDao().getObjects(statementList).get(0);
+            return authenticationSources.get(new AuthModuleKey(user.getRealm(),user.getUserType()))
+                    .authenticate(username,password);
+        }
+        //The second case is much more violent, because we don't know in advance "where" this user comes
+        //from, so we have to infer (this is only really true for "users", all others are usually pre-marked)
+        //This is also the case when we store the user into the DAO - because he never existed before.
+        for(AuthModuleKey key : authenticationSources.keySet()){
+            if(authenticationSources.get(key).authenticate(username,password)){
+                User user = new User();
+                user.setUsername(username);
+                user.setRealm(key.getRealm());
+                user.setUserType(key.getType());
+                if(key.getType().equals(AuthenticationSourceType.LOCAL)) user.setPassword(password);
+                //TODO: We need a function here which generates the user (obvious workflow).
+                //The user never existed, obviously.
+                dataStore.getUserDao().storeObject(user);
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/core/utils/ResponseBuilder.java b/jams-server/src/main/java/net/jami/jams/server/core/utils/ResponseBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..498e32cd17692cc31dd24746d578eb5e57479df9
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/core/utils/ResponseBuilder.java
@@ -0,0 +1,13 @@
+package net.jami.jams.server.core.utils;
+
+import jakarta.servlet.http.HttpServletResponse;
+
+public class ResponseBuilder {
+
+    public void  buildResponse(Integer status, String body, HttpServletResponse resp) throws Exception{
+        resp.setStatus(status);
+        resp.getOutputStream().write(body.getBytes());
+    }
+
+
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java
new file mode 100644
index 0000000000000000000000000000000000000000..13f2518c961884235a31c543167e09cc2c47ddcd
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java
@@ -0,0 +1,14 @@
+package net.jami.jams.server.core.workflows;
+
+public class RegisterDeviceFlow {
+
+    public static void registerDevice(){
+        //Build "Device" from the JSON that came in.
+        //Check if user exists
+        //if does not exist -> create user -> confirm created
+        //create device
+    }
+
+
+
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterUserFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterUserFlow.java
new file mode 100644
index 0000000000000000000000000000000000000000..03bca2f28df33e5ce7e6dee11001d020efede204
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterUserFlow.java
@@ -0,0 +1,24 @@
+package net.jami.jams.server.core.workflows;
+
+import net.jami.jams.common.objects.user.User;
+
+import static net.jami.jams.server.Server.certificateAuthority;
+
+public class RegisterUserFlow {
+
+    //Get the CA, sign, return the Jami ID.
+    public static User createUser(User user){
+        user = certificateAuthority.getSignedCertificate(user);
+        //while(ethKeyPair == null){
+        //    ethKeyPair = ETHAddressGenerator.generateAddress();
+        //}
+        //user.setEthAddress(ethKeyPair[0]);
+        //user.setEthKey(ethKeyPair[1]);
+        //user.setJamiId(Hex.encodeHexString(MessageDigest.getInstance(MessageDigestAlgorithms.SHA_1).digest(userKeyPair.getPublic().getEncoded())));
+        //user.setEthAddress();
+        //user.setJamiId();
+        return user;
+    }
+
+
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeDeviceFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeDeviceFlow.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea433ee5ed2420848a78507ddfa44a0ac6c5733e
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeDeviceFlow.java
@@ -0,0 +1,4 @@
+package net.jami.jams.server.core.workflows;
+
+public class RevokeDeviceFlow {
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeUserFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeUserFlow.java
new file mode 100644
index 0000000000000000000000000000000000000000..59bc146717729cd36b853be788145030dad0a9ad
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeUserFlow.java
@@ -0,0 +1,4 @@
+package net.jami.jams.server.core.workflows;
+
+public class RevokeUserFlow {
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/DeviceServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/DeviceServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5e15ce4e2ddf7957d3c3b25e158a1eaad4efe68
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/DeviceServlet.java
@@ -0,0 +1,32 @@
+package net.jami.jams.server.servlets.api.auth;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+@WebServlet("/api/auth/device")
+public class DeviceServlet extends HttpServlet {
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        resp.getOutputStream().print("Hello World!");
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+    }
+
+    @Override
+    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        super.doPut(req, resp);
+    }
+
+    @Override
+    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        super.doDelete(req, resp);
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/DevicesServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/DevicesServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..fcab7cc56cbbfa5cce6036281ee9ebbe715a1676
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/DevicesServlet.java
@@ -0,0 +1,19 @@
+package net.jami.jams.server.servlets.api.auth;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+@WebServlet("/api/auth/devices")
+public class DevicesServlet extends HttpServlet {
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        super.doGet(req, resp);
+    }
+
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/contacts/ContactServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/contacts/ContactServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..880e92879701bbc97f7e28f9eccdd81156f0ea67
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/contacts/ContactServlet.java
@@ -0,0 +1,28 @@
+package net.jami.jams.server.servlets.api.contacts;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+@WebServlet("/api/contacts")
+public class ContactServlet extends HttpServlet {
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        super.doGet(req, resp);
+    }
+
+    @Override
+    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        super.doPut(req, resp);
+    }
+
+    @Override
+    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        super.doDelete(req, resp);
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/directory/SearchDirectoryServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/directory/SearchDirectoryServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..099a787011307bfe390505fd64351cde5f7040e6
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/directory/SearchDirectoryServlet.java
@@ -0,0 +1,18 @@
+package net.jami.jams.server.servlets.api.directory;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+@WebServlet("/api/directory/search")
+public class SearchDirectoryServlet extends HttpServlet {
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        super.doGet(req, resp);
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/APIFilter.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/APIFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..956495e1edceb91d9f9907c0e4cdfc6c5cf1a25f
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/APIFilter.java
@@ -0,0 +1,48 @@
+package net.jami.jams.server.servlets.filters;
+
+import jakarta.servlet.*;
+import jakarta.servlet.annotation.WebFilter;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.common.objects.user.User;
+
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static net.jami.jams.server.Server.userAuthenticationModule;
+
+@WebFilter(urlPatterns = {"/api/*"})
+@Slf4j
+public class APIFilter implements Filter {
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest) servletRequest;
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
+        boolean authsuccess = false;
+        /*
+        The API endpoint has only 2 possible states: auth with basic Auth, and auth with SSL.
+        I left the possibility of working with HTTP sessions if someday, this takes place.
+        We should keep in mind, that we will NEVER need to create a user if an SSL certificate has
+        been presented.
+         */
+        //First try with SSL
+        if(Decoders.decodeSSLAuth(request) != null){
+            //From the certificate, we can pull up the deviceId, and find the owner.
+            authsuccess = true;
+        }
+        else if(Decoders.decodeAuthHeader(request) != null){
+            String[] authCredentials = Decoders.decodeAuthHeader(request);
+            if(authCredentials != null && authCredentials.length == 2) {
+                if(userAuthenticationModule.authenticateUser(authCredentials[0],authCredentials[1])){
+                    log.info("User " + authCredentials[0] + " has succesfully logged in!");
+                    authsuccess = true;
+                }
+            }
+        }
+        if(authsuccess) filterChain.doFilter(servletRequest,servletResponse);
+        else response.setStatus(403);
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/Decoders.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/Decoders.java
new file mode 100644
index 0000000000000000000000000000000000000000..302996a39edc5deaeb877e9d864c6abf57e0bbc9
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/Decoders.java
@@ -0,0 +1,39 @@
+package net.jami.jams.server.servlets.filters;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+public class Decoders {
+
+    public static String[] decodeAuthHeader(HttpServletRequest request){
+        if(request.getHeader("authorization") != null){
+            return decodeAuthHeader(request.getHeader("authorization"));
+        }
+        else return null;
+    }
+
+    private static String[] decodeAuthHeader(String authorization){
+        if (authorization != null && authorization.toLowerCase().startsWith("basic")) {
+            // Authorization: Basic base64credentials
+            String base64Credentials = authorization.substring("Basic".length()).trim();
+            byte[] credDecoded = Base64.getDecoder().decode(base64Credentials);
+            //TODO: change this to char array
+            String credentials = new String(credDecoded, StandardCharsets.UTF_8);
+            // credentials = username:password
+            return credentials.split(":", 2);
+        }
+        return null;
+    }
+
+    //This just return whatever we need in the header.
+    public static String decodeSSLAuth(HttpServletRequest request){
+        if(request.getAttribute("jakarta.servlet.request.X509Certificate") != null){
+            return null;
+        }
+        return null;
+    }
+
+
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/WebAppFilter.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/WebAppFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..da7300b5f9064484b4141133c6b80c11a68c4443
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/WebAppFilter.java
@@ -0,0 +1,37 @@
+package net.jami.jams.server.servlets.filters;
+
+import jakarta.servlet.*;
+import jakarta.servlet.annotation.WebFilter;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
+import net.jami.jams.common.objects.user.User;
+
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+
+/*
+
+ */
+@WebFilter("/web/*")
+public class WebAppFilter implements Filter {
+
+    public static final ConcurrentHashMap<HttpSession, User> authenticatedUsers = new ConcurrentHashMap<>();
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest) servletRequest;
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
+        boolean authsuccess = authenticatedUsers.containsKey(request.getSession());
+
+        if(authsuccess){
+            filterChain.doFilter(request,response);
+        }
+        if(!authsuccess && request.getServletPath().contains("login")){
+            filterChain.doFilter(request,response);
+        }
+        else{
+            response.setStatus(403);
+        }
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/general/ServerStatusServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/general/ServerStatusServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..ba5f1fa8dda7b27e7cc76b439c2970f97b35d142
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/general/ServerStatusServlet.java
@@ -0,0 +1,19 @@
+package net.jami.jams.server.servlets.general;
+
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+@WebServlet("/api/info")
+public class ServerStatusServlet extends HttpServlet {
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        resp.getOutputStream().print("OK");
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/web/IndexServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/web/IndexServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec287893c188cc39192eaf89993972212624efeb
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/web/IndexServlet.java
@@ -0,0 +1,24 @@
+package net.jami.jams.server.servlets.web;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.server.Server;
+
+import java.io.IOException;
+
+@Slf4j
+@WebServlet("/web/index")
+public class IndexServlet extends HttpServlet {
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        req.setAttribute("test","AB");
+        if(Server.isInstalled.get()) req.getRequestDispatcher("login.jsp").forward(req,resp);
+        else req.getRequestDispatcher("register.jsp").forward(req,resp);
+    }
+
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/web/LoginServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/web/LoginServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..3486b21421547bac6d08a19cf79b61b59e57f25f
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/web/LoginServlet.java
@@ -0,0 +1,21 @@
+package net.jami.jams.server.servlets.web;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import net.jami.jams.server.Server;
+import net.jami.jams.server.servlets.filters.WebAppFilter;
+
+import java.io.IOException;
+
+@WebServlet("/web/login")
+public class LoginServlet extends HttpServlet {
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        if(Server.isInstalled.get()) req.getRequestDispatcher("/login.jsp").forward(req,resp);
+        else req.getRequestDispatcher("/register.jsp").forward(req,resp);
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateAdminAccountServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateAdminAccountServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3dbeb6bd71ba9220d7c10caa1a52a226a347721
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateAdminAccountServlet.java
@@ -0,0 +1,18 @@
+package net.jami.jams.server.servlets.web.install;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+@WebServlet("/web/install/admin")
+public class CreateAdminAccountServlet extends HttpServlet {
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        super.doPost(req, resp);
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateAuthSourceServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateAuthSourceServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..061af70b91e017850924e35f7791d4f3f66472d0
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateAuthSourceServlet.java
@@ -0,0 +1,23 @@
+package net.jami.jams.server.servlets.web.install;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+@WebServlet("/web/install/auth")
+public class CreateAuthSourceServlet extends HttpServlet {
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        super.doGet(req, resp);
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        super.doPost(req, resp);
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateCAServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateCAServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..54bc13937ca97e4d8adc18c02c9b248e138b9d52
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateCAServlet.java
@@ -0,0 +1,23 @@
+package net.jami.jams.server.servlets.web.install;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+@WebServlet("/web/install/ca")
+public class CreateCAServlet extends HttpServlet {
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        super.doGet(req, resp);
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        super.doPost(req, resp);
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateGeneralSettingsServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateGeneralSettingsServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..519aa0d25263e3b298aa13a7f9b732feec63ad44
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateGeneralSettingsServlet.java
@@ -0,0 +1,23 @@
+package net.jami.jams.server.servlets.web.install;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+@WebServlet("/web/install/settings")
+public class CreateGeneralSettingsServlet extends HttpServlet {
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        super.doGet(req, resp);
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        super.doPost(req, resp);
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/x509/CRLServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/x509/CRLServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..92f2f116f86ab4aca86428a277b87df3447074ba
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/x509/CRLServlet.java
@@ -0,0 +1,22 @@
+package net.jami.jams.server.servlets.x509;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+import static net.jami.jams.server.Server.certificateAuthority;
+
+
+@WebServlet("/api/auth/crl")
+public class CRLServlet extends HttpServlet {
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        certificateAuthority.getLatestCRL();
+        super.doGet(req, resp);
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/x509/OCSPServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/x509/OCSPServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..2cfd39a2b963cabfb5bb58157a7d9cfece8d70d8
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/x509/OCSPServlet.java
@@ -0,0 +1,21 @@
+package net.jami.jams.server.servlets.x509;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+import static net.jami.jams.server.Server.certificateAuthority;
+
+@WebServlet("/api/auth/ocsp")
+public class OCSPServlet extends HttpServlet {
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        certificateAuthority.getOCSPResponse(null);
+        super.doGet(req, resp);
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/startup/CryptoEngineLoader.java b/jams-server/src/main/java/net/jami/jams/server/startup/CryptoEngineLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac62227229016efba8666f04f09153316081f546
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/startup/CryptoEngineLoader.java
@@ -0,0 +1,46 @@
+package net.jami.jams.server.startup;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.datastore.main.DataStore;
+import net.jami.jams.common.cryptoengineapi.CertificateAuthority;
+import net.jami.jams.common.dao.StatementElement;
+import net.jami.jams.common.dao.StatementList;
+import net.jami.jams.common.objects.system.SystemAccount;
+import net.jami.jams.server.Server;
+
+import java.util.List;
+
+@Slf4j
+public class CryptoEngineLoader {
+
+    public static CertificateAuthority loadCryptoEngine(DataStore dataStore){
+        try {
+            Class<?> cls = LibraryLoader.classLoader.loadClass("net.jami.jams.cryptoengine.CryptoEngine");
+            CertificateAuthority certificateAuthority = (CertificateAuthority) cls.getConstructor().newInstance();
+            StatementList statementList = new StatementList();
+            statementList.addStatement(new StatementElement("entity","=","CA",""));
+            List<SystemAccount> accounts = dataStore.getSystemDao().getObjects(statementList);
+            if(accounts.size() == 0){
+                log.info("This is an fresh install, and it has no CA or any system accounts!");
+                Server.isInstalled.set(false);
+            }
+            else{
+                statementList = new StatementList();
+                statementList.addStatement(new StatementElement("entity","=","CA",""));
+                SystemAccount caAccount = dataStore.getSystemDao().getObjects(statementList).get(0);
+                statementList = new StatementList();
+                statementList.addStatement(new StatementElement("entity","=","OCSP",""));
+                SystemAccount ocspAccount = dataStore.getSystemDao().getObjects(statementList).get(0);
+                log.info("Injecting OCSP and CA acocunts...");
+                certificateAuthority.init("domain.com","SHA512WITHRSA",caAccount,ocspAccount);
+            }
+            log.info("Loaded X509 Engine - please make sure it is initialized before using it to sign requests!");
+            return certificateAuthority;
+        }
+        catch (Exception e){
+            log.error("Unable to load X509 Engine!");
+            return null;
+        }
+    }
+
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/startup/LibraryLoader.java b/jams-server/src/main/java/net/jami/jams/server/startup/LibraryLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..83338c4187193b8301ace763e13e63331e881291
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/startup/LibraryLoader.java
@@ -0,0 +1,37 @@
+package net.jami.jams.server.startup;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.server.Server;
+import org.apache.xbean.classloader.JarFileClassLoader;
+
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+
+@Slf4j
+public class LibraryLoader {
+
+    public static JarFileClassLoader classLoader;
+
+    public static void loadlibs(String libDir){
+        try {
+            File dependencyDirectory = new File(libDir);
+            File[] files = dependencyDirectory.listFiles();
+            ArrayList<URL> urls = new ArrayList<>();
+            assert files != null;
+            for (int i = 0; i < files.length; i++) {
+                if (files[i].getName().endsWith(".jar")) {
+                    urls.add(files[i].toURI().toURL());
+                    log.info("Successfully loaded the library " + files[i]);
+                }
+            }
+            classLoader = new JarFileClassLoader("Scheduler CL" + System.currentTimeMillis(), urls.toArray(new URL[urls.size()]), Server.class.getClassLoader());
+        }
+        catch (Exception e){
+            log.error("Errors occured while trying to load libraries...");
+        }
+    }
+
+
+
+}
diff --git a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e646a51460524fc21374503ce73fbc8790ba2cd
--- /dev/null
+++ b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java
@@ -0,0 +1,68 @@
+package net.jami.jams.ldap.connector;
+
+import com.jsoniter.JsonIterator;
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.common.authentication.AuthenticationSource;
+import net.jami.jams.common.authentication.AuthenticationSourceInfo;
+import net.jami.jams.common.authentication.AuthenticationSourceType;
+import net.jami.jams.common.authentication.ldap.LDAPSettings;
+import net.jami.jams.common.objects.user.User;
+import net.jami.jams.common.objects.user.UserProfile;
+import net.jami.jams.common.serialization.JsoniterRegistry;
+import net.jami.jams.ldap.connector.service.AuthenticationService;
+import org.ldaptive.BindConnectionInitializer;
+import org.ldaptive.ConnectionConfig;
+import org.ldaptive.Credential;
+import org.ldaptive.DefaultConnectionFactory;
+
+@Slf4j
+public class LDAPConnector implements AuthenticationSource {
+
+    private AuthenticationService authenticationService;
+    public static LDAPSettings settings;
+
+    public LDAPConnector(String strSettings) {
+        JsoniterRegistry.initCodecs();
+        LDAPConnector.settings = JsonIterator.deserialize(strSettings,LDAPSettings.class);
+        BindConnectionInitializer bindConnectionInitializer = new BindConnectionInitializer();
+        bindConnectionInitializer.setBindDn(settings.getUsername());
+        bindConnectionInitializer.setBindCredential(new Credential(settings.getPassword()));
+        ConnectionConfig connConfig = ConnectionConfig.builder()
+                .url(settings.getHost())
+                .useStartTLS(settings.getUseStartTLS())
+                .connectionInitializers(bindConnectionInitializer)
+                .build();
+        authenticationService = new AuthenticationService(new DefaultConnectionFactory(connConfig));
+        log.info("Started LDAP Connector!");
+    }
+
+    @Override
+    public boolean createUser(User user) {
+        return false;
+    }
+
+    @Override
+    public UserProfile getUserProfile(String username) {
+        return null;
+    }
+
+    @Override
+    public boolean authenticate(String username, String password) {
+        return authenticationService.authenticateUser(username,password);
+    }
+
+    @Override
+    public AuthenticationSourceInfo getInfo() {
+        return new AuthenticationSourceInfo("savoirfairelinux", AuthenticationSourceType.LDAP);
+    }
+
+    @Override
+    public boolean testConfiguration(String configuration) {
+        return false;
+    }
+
+    @Override
+    public boolean updatePassword(User user, String password) {
+        return false;
+    }
+}
diff --git a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/AuthenticationService.java b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/AuthenticationService.java
new file mode 100644
index 0000000000000000000000000000000000000000..90b07eec2aa19b78f0e525f45a7f998dd3908afe
--- /dev/null
+++ b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/AuthenticationService.java
@@ -0,0 +1,38 @@
+package net.jami.jams.ldap.connector.service;
+
+import lombok.extern.slf4j.Slf4j;
+import org.ldaptive.ConnectionFactory;
+import org.ldaptive.Credential;
+import org.ldaptive.auth.*;
+
+import static net.jami.jams.ldap.connector.LDAPConnector.settings;
+
+@Slf4j
+public class AuthenticationService {
+
+    private final ConnectionFactory connectionFactory;
+
+    public AuthenticationService(ConnectionFactory connectionFactory) {
+        this.connectionFactory = connectionFactory;
+    }
+
+    public boolean authenticateUser(String username, String password) {
+        try {
+            FormatDnResolver dnResolver = new FormatDnResolver();
+            dnResolver.setFormat(settings.getUsernameField() + "," + settings.getBaseDN());
+            SimpleBindAuthenticationHandler bindAuthenticationHandler = new SimpleBindAuthenticationHandler(connectionFactory);
+            Authenticator auth = new Authenticator();
+            auth.setDnResolver(dnResolver);
+            auth.setAuthenticationHandler(bindAuthenticationHandler);
+            AuthenticationResponse resp = auth.authenticate(new AuthenticationRequest(username, new Credential(password)));
+            log.info("User " + username + " has tried to authenticate with result: " + resp.getAuthenticationResultCode());
+            return resp.isSuccess();
+        }
+        catch (Exception e){
+            log.info("An exception has occured trying to process and authentication request: " + e.toString());
+            return false;
+        }
+    }
+
+
+}
diff --git a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java
new file mode 100644
index 0000000000000000000000000000000000000000..0fa7b5472464117406ecf75883b3e710a76c1293
--- /dev/null
+++ b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java
@@ -0,0 +1,4 @@
+package net.jami.jams.ldap.connector.service;
+
+public class UserProfileService {
+}