diff --git a/datastore/src/main/java/net/jami/datastore/dao/UserDao.java b/datastore/src/main/java/net/jami/datastore/dao/UserDao.java index b2e0308576caf97408fdfa7e1f8f98556221bd0d..4f3723d855a96f63d8e224ca8b854bc9048b77ff 100644 --- a/datastore/src/main/java/net/jami/datastore/dao/UserDao.java +++ b/datastore/src/main/java/net/jami/datastore/dao/UserDao.java @@ -59,17 +59,31 @@ public class UserDao extends AbstractDao<User> { @Override public boolean updateObject(StatementList update, StatementList constraints) { + if(update.getStatements().get(0).getColumn() == "certificate"){ + + SQLConnection connection = DataStore.connectionPool.getConnection(); + try { + String certificate = update.getStatements().get(0).getValue(); + String user = constraints.getStatements().get(0).getValue(); + PreparedStatement ps = connection.getConnection().prepareStatement("UPDATE users SET certificate = ? WHERE username = ?"); + ps.setString(1, certificate); + ps.setString(2, user); + return ps.executeUpdate() != 0; + } catch (Exception e) { + log.error("An error has occurred while trying to update a user certificate: " + e.toString()); + return false; + } finally { + DataStore.connectionPool.returnConnection(connection); + } + + } + String pw = update.getStatements().get(0).getValue(); String salt = ""; if (update.getStatements().size() > 1) salt = update.getStatements().get(1).getValue(); String user = constraints.getStatements().get(0).getValue(); -// String pwReset = "false"; -// -// if (update.getStatements().size() > 1) { -// pwReset = update.getStatements().get(1).getValue(); -// } SQLConnection connection = DataStore.connectionPool.getConnection(); @@ -78,12 +92,7 @@ public class UserDao extends AbstractDao<User> { ps.setString(1, pw); ps.setString(2, salt); ps.setString(3, user); -// ps.executeUpdate(); -// -// ps = connection.getConnection().prepareStatement("UPDATE users SET needsPasswordReset = ? WHERE username = ?"); -// ps.setString(1, pwReset); -// -// ps.setString(2, user); + return ps.executeUpdate() != 0; } catch (Exception e) { log.error("An error has occurred while trying to update a user: " + e.toString()); diff --git a/datastore/src/main/java/net/jami/datastore/main/DataStore.java b/datastore/src/main/java/net/jami/datastore/main/DataStore.java index ae4c229c3d80b6f17cfad1718564c34033823ead..8aad8af84676b28bcb91baee3a5f2e5a896c9778 100644 --- a/datastore/src/main/java/net/jami/datastore/main/DataStore.java +++ b/datastore/src/main/java/net/jami/datastore/main/DataStore.java @@ -42,12 +42,16 @@ import net.jami.jams.common.dao.connectivity.ConnectionPool; import net.jami.jams.common.objects.user.User; import net.jami.jams.common.objects.user.UserGroupMapping; import net.jami.jams.common.objects.user.UserProfile; +import net.jami.jams.common.utils.X509Utils; + import org.flywaydb.core.Flyway; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import javax.swing.plaf.nimbus.State; + @Getter @Setter public class DataStore implements AuthenticationSource { @@ -161,6 +165,18 @@ public class DataStore implements AuthenticationSource { return userProfileDao.updateObject(update, null); } + public boolean updateUserCertificate(User user) { + + StatementList update = new StatementList(); + StatementList constraints = new StatementList(); + + update.addStatement(new StatementElement("certificate","=", X509Utils.getPEMStringFromCertificate(user.getCertificate()),"")); + + constraints.addStatement(new StatementElement("username","=", user.getUsername(),"")); + + return userDao.updateObject(update, constraints); + } + @Override public boolean authenticate(String username, String password) { StatementList statementList = new StatementList(); diff --git a/jams-ca/src/main/java/net/jami/jams/ca/JamsCA.java b/jams-ca/src/main/java/net/jami/jams/ca/JamsCA.java index 00fb29f0f9237b375ce890e6615cb09038bfd3ca..317b1b0a3e10240361dc641dafe34fc67fa69f3f 100644 --- a/jams-ca/src/main/java/net/jami/jams/ca/JamsCA.java +++ b/jams-ca/src/main/java/net/jami/jams/ca/JamsCA.java @@ -97,6 +97,11 @@ public class JamsCA implements CertificateAuthority { return CertificateWorker.getSignedCertificate(user); } + @Override + public User getRefreshedCertificate(User user) { + return CertificateWorker.getRefreshedCertificate(user); + } + @Override public Device getSignedCertificate(User user, Device device) { return CertificateWorker.getSignedCertificate(user, device); diff --git a/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/CertificateWorker.java b/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/CertificateWorker.java index 20211087fbd8e97be6af5f728bdbb0c98d136ade..845ac79326dac47e1007695724382ff95edf956b 100644 --- a/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/CertificateWorker.java +++ b/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/CertificateWorker.java @@ -48,6 +48,10 @@ public class CertificateWorker { return UserBuilder.generateUser(user); } + public static User getRefreshedCertificate(User user){ + return UserBuilder.refreshUser(user); + } + public static Device getSignedCertificate(User user, Device device){ return DeviceBuilder.generateDevice(user,device); } diff --git a/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/DeviceBuilder.java b/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/DeviceBuilder.java index d5c470b4540e593f66bbd56d00f1362a73eb152d..8f54fdce131471237204622a1ea2e6a097a83bf2 100644 --- a/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/DeviceBuilder.java +++ b/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/DeviceBuilder.java @@ -52,11 +52,12 @@ public class DeviceBuilder { public static Device generateDevice(User user, Device device){ try { + long now = System.currentTimeMillis(); X509v3CertificateBuilder builder = new X509v3CertificateBuilder( new JcaX509CertificateHolder(user.getCertificate()).getSubject(), new BigInteger(256, new SecureRandom()), - new Date(System.currentTimeMillis() - SHIFT), - new Date(System.currentTimeMillis() + JamsCA.deviceLifetime), + new Date(now - SHIFT), + new Date(now + JamsCA.deviceLifetime), device.getCertificationRequest().getSubject(), device.getCertificationRequest().getSubjectPublicKeyInfo() ); diff --git a/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/UserBuilder.java b/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/UserBuilder.java index 6570601bb556d0c72c05c111c815303340e9f966..7da79d586193f49cb1686ba41515edb040e11d83 100644 --- a/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/UserBuilder.java +++ b/jams-ca/src/main/java/net/jami/jams/ca/workers/csr/builders/UserBuilder.java @@ -35,34 +35,66 @@ import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.MessageDigest; import java.security.SecureRandom; import java.util.Date; import static net.jami.jams.ca.workers.csr.CertificateWorker.SHIFT; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.codec.digest.MessageDigestAlgorithms; + + @Slf4j public class UserBuilder { public static User generateUser(User user) { try { + long now = System.currentTimeMillis(); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(4096); KeyPair keyPair = keyPairGenerator.generateKeyPair(); X509v3CertificateBuilder builder = new X509v3CertificateBuilder( new JcaX509CertificateHolder(JamsCA.CA.getCertificate()).getSubject(), - new BigInteger(256, new SecureRandom()), - new Date(System.currentTimeMillis() - SHIFT), - new Date(System.currentTimeMillis() + JamsCA.userLifetime), + new BigInteger(128, new SecureRandom()), + new Date(now - SHIFT), + new Date(now + JamsCA.userLifetime), new X500Name(user.getX509Fields().getDN()), SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()) ); + user.setPrivateKey(keyPair.getPrivate()); user.setCertificate(CertificateSigner.signCertificate(JamsCA.CA.getPrivateKey(), builder, ExtensionLibrary.userExtensions)); - + log.info("New user certificate: Not valid after: " + user.getCertificate().getNotAfter()); return user; } catch (Exception e) { log.error("Could not generate a user certificate: " + e.toString()); return null; } } + + public static User refreshUser(User user) { + return refreshUser(user, JamsCA.userLifetime); + } + + public static User refreshUser(User user, long userLifeTime) { + try { + long now = System.currentTimeMillis(); + X509v3CertificateBuilder builder = new X509v3CertificateBuilder( + new JcaX509CertificateHolder(JamsCA.CA.getCertificate()).getSubject(), + new BigInteger(128, new SecureRandom()), + new Date(now - SHIFT), + new Date(now + userLifeTime), + new X500Name(user.getX509Fields().getDN()), + new JcaX509CertificateHolder(user.getCertificate()).getSubjectPublicKeyInfo() + ); + user.setCertificate(CertificateSigner.signCertificate(JamsCA.CA.getPrivateKey(), builder, ExtensionLibrary.userExtensions)); + log.info("====> Refreshed user certificate: Not valid after: " + user.getCertificate().getNotAfter()); + + return user; + } catch (Exception e) { + log.error("Could not refresh user certificate: " + e.toString()); + return null; + } + } } diff --git a/jams-ca/src/test/java/net/jami/jams/ca/workers/csr/builders/UserBuilderTest.java b/jams-ca/src/test/java/net/jami/jams/ca/workers/csr/builders/UserBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..468a9b4ee5cb7e4652ceec3cc738fada07f18319 --- /dev/null +++ b/jams-ca/src/test/java/net/jami/jams/ca/workers/csr/builders/UserBuilderTest.java @@ -0,0 +1,80 @@ +package net.jami.jams.ca.workers.csr.builders; + +import net.jami.jams.ca.JamsCA; +import lombok.extern.slf4j.Slf4j; + +import net.jami.jams.common.authentication.AuthenticationSourceType; +import net.jami.jams.common.objects.roots.X509Fields; +import net.jami.jams.common.objects.user.AccessLevel; +import net.jami.jams.common.objects.user.User; +import net.jami.jams.common.utils.X509Utils; +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Base64; +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Assertions; + +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; + +@Slf4j +class UserBuilderTest { + + + @Test + void generateUserCertificate() { + + User user = new User(); + user.setUsername("TestUser"); + user.setUserType(AuthenticationSourceType.LOCAL); + user.setX509Fields(new X509Fields()); + user.getX509Fields().setCommonName("TestUser's Personal Certificate"); + user = UserBuilder.generateUser(user); + + Assertions.assertNotNull(user.getCertificate(),"User Certificate was not generated!"); + } + + @Test + void refreshUserCertificate() { + + User user = new User(); + user.setUsername("TestUser"); + //user.setJamiId(""); + user.setUserType(AuthenticationSourceType.LOCAL); + user.setX509Fields(new X509Fields()); + user.getX509Fields().setCommonName("TestUser's Personal Certificate"); + user = UserBuilder.generateUser(user); + + Assertions.assertNotNull(user,"User was not generated!"); + + X509Certificate cert = user.getCertificate(); + Assertions.assertNotNull(cert,"User Certificate was not generated!"); + + User refreshedUser = UserBuilder.refreshUser(user, 465_000_000); + Assertions.assertNotNull(refreshedUser,"User was not generated!"); + X509Certificate new_cert = refreshedUser.getCertificate(); + + Assertions.assertArrayEquals(cert.getPublicKey().getEncoded(), new_cert.getPublicKey().getEncoded(), "PK is different"); + + Assertions.assertNotNull(new_cert,"User Certificate was not updated!"); + Assertions.assertEquals(user.getAddress(), refreshedUser.getAddress(), "User address is different"); + + Assertions.assertNotEquals(cert.getNotAfter(), new_cert.getNotAfter()); + try { + Assertions.assertEquals(new JcaX509CertificateHolder(cert).getSubjectPublicKeyInfo().getPublicKey(), + new JcaX509CertificateHolder(new_cert).getSubjectPublicKeyInfo().getPublicKey()); + } catch (Exception e) { + log.error("Error comparing two public keys information: ", e.getMessage()); + } + } +} \ No newline at end of file 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 index dfd933c124010a02e185d97be35d7aecb575b151..a8de02acd58ec763ca5235a1371d1b38b67b2f83 100644 --- 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 @@ -37,6 +37,7 @@ public interface CertificateAuthority { //Return a signed X509 certificate based on various constraints. void init(String settings, SystemAccount ca, SystemAccount ocsp); User getSignedCertificate(User user); + User getRefreshedCertificate(User user); Device getSignedCertificate(User user, Device device); SystemAccount getSignedCertificate(SystemAccount systemAccount); void revokeCertificate(RevocationRequest revocationRequest);