Commit 13c5d423 authored by Larbi Gharib's avatar Larbi Gharib
Browse files

user: allow to refresh certificate

GitLab: #81
Change-Id: I694f7690856018899c6adb92939f4f8aaa37081f
parent 1295a142
......@@ -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());
......
......@@ -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();
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -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()
);
......
......@@ -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;
}
}
}
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
......@@ -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);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment