Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a qrcode MFA, webservice, service and encryption for the secret key #19

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion plume-admin-security/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,23 @@
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>

<!-- Authenticator -->
<dependency>
<groupId>com.warrenstrange</groupId>
<artifactId>googleauth</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.1</version>
</dependency>
</dependencies>

<dependencyManagement>
Expand All @@ -97,4 +114,4 @@
</dependencies>
</dependencyManagement>

</project>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ public String jwtSecret() {
return config.getString("admin.jwt-secret");
}

public String mfaSecret() {
return config.getString("admin.mfa-secret");
}

public boolean sessionUseFingerprintCookie() {
return config.getBoolean("admin.session.use-fingerprint-cookie");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.coreoz.plume.admin.websession;

import java.security.SecureRandom;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class MfaSecretKeyEncryption {

private static final String ALGORITHM = "AES/GCM/NoPadding";
private static final int IV_SIZE = 12; // 96 bits
private static final int TAG_SIZE = 128; // 128 bits
private final SecretKey secretKey;

public MfaSecretKeyEncryption(String base64SecretKey) {
byte[] decodedKey = Base64.getDecoder().decode(base64SecretKey);
this.secretKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
}

public String encrypt(String data) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
byte[] iv = new byte[IV_SIZE];
SecureRandom random = new SecureRandom();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SecureRandom est thread safe => autant l'initialiser qu'une seule fois

random.nextBytes(iv);
GCMParameterSpec parameterSpec = new GCMParameterSpec(TAG_SIZE, iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
byte[] encrypted = cipher.doFinal(data.getBytes());
byte[] encryptedWithIv = new byte[IV_SIZE + encrypted.length];
System.arraycopy(iv, 0, encryptedWithIv, 0, IV_SIZE);
System.arraycopy(encrypted, 0, encryptedWithIv, IV_SIZE, encrypted.length);
return Base64.getEncoder().encodeToString(encryptedWithIv);
}

public String decrypt(String encryptedData) throws Exception {
byte[] encryptedWithIv = Base64.getDecoder().decode(encryptedData);
byte[] iv = new byte[IV_SIZE];
byte[] encrypted = new byte[encryptedWithIv.length - IV_SIZE];
System.arraycopy(encryptedWithIv, 0, iv, 0, IV_SIZE);
System.arraycopy(encryptedWithIv, IV_SIZE, encrypted, 0, encrypted.length);
Cipher cipher = Cipher.getInstance(ALGORITHM);
GCMParameterSpec parameterSpec = new GCMParameterSpec(TAG_SIZE, iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);
byte[] original = cipher.doFinal(encrypted);
return new String(original);
}

public static String generateSecretKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256); // Use 256 bits for strong encryption
SecretKey secretKey = keyGen.generateKey();
return Base64.getEncoder().encodeToString(secretKey.getEncoded());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.coreoz.plume.admin.websession;

import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;

import com.coreoz.plume.admin.services.configuration.AdminSecurityConfigurationService;

@Singleton
public class MfaSecretKeyEncryptionProvider implements Provider<MfaSecretKeyEncryption> {

private final MfaSecretKeyEncryption mfaSecretKeyEncryption;

@Inject
private MfaSecretKeyEncryptionProvider(AdminSecurityConfigurationService conf) {
this.mfaSecretKeyEncryption = new MfaSecretKeyEncryption(conf.mfaSecret());
}

@Override
public MfaSecretKeyEncryption get() {
return mfaSecretKeyEncryption;
}
}
24 changes: 21 additions & 3 deletions plume-admin-ws/pom.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.coreoz</groupId>
<artifactId>plume-admin-parent</artifactId>
Expand Down Expand Up @@ -29,7 +29,7 @@
<groupId>com.coreoz</groupId>
<artifactId>plume-admin-security</artifactId>
</dependency>

<dependency>
<groupId>com.coreoz</groupId>
<artifactId>plume-services</artifactId>
Expand Down Expand Up @@ -101,6 +101,24 @@
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>

<!-- Web authenticator
https://developers.yubico.com/java-webauthn-server/ -->
<dependency>
<groupId>com.yubico</groupId>
<artifactId>webauthn-server-core</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>2.14.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-cbor</artifactId>
<version>2.14.1</version>
</dependency>
</dependencies>

<dependencyManagement>
Expand All @@ -115,4 +133,4 @@
</dependencies>
</dependencyManagement>

</project>
</project>
3 changes: 2 additions & 1 deletion plume-admin-ws/sql/setup-mssql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ CREATE TABLE PLM_USER (
EMAIL varchar(255) NOT NULL,
USER_NAME varchar(255) NOT NULL,
PASSWORD varchar(255) NOT NULL,
SECRET_KEY varchar(255) NOT NULL,
CONSTRAINT plm_user_pk PRIMARY KEY (ID),
CONSTRAINT uniq_plm_user_email UNIQUE (EMAIL),
CONSTRAINT uniq_plm_user_username UNIQUE (USER_NAME),
Expand All @@ -34,4 +35,4 @@ INSERT INTO PLM_ROLE_PERMISSION VALUES(1, 'MANAGE_USERS');
INSERT INTO PLM_ROLE_PERMISSION VALUES(1, 'MANAGE_ROLES');
INSERT INTO PLM_ROLE_PERMISSION VALUES(1, 'MANAGE_SYSTEM');

GO
GO
43 changes: 40 additions & 3 deletions plume-admin-ws/sql/setup-mysql.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
DROP TABLE IF EXISTS `PLM_USER_MFA`;
DROP TABLE IF EXISTS `PLM_ROLE_PERMISSION`;
DROP TABLE IF EXISTS `PLM_USER`;
DROP TABLE IF EXISTS `PLM_ROLE`;
CREATE TABLE `PLM_ROLE` (
`id` bigint(20) NOT NULL,
Expand All @@ -6,7 +9,7 @@ CREATE TABLE `PLM_ROLE` (
UNIQUE KEY `uniq_plm_role_label` (`label`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `PLM_USER`;

CREATE TABLE `PLM_USER` (
`id` bigint(20) NOT NULL,
`id_role` bigint(20) NOT NULL,
Expand All @@ -16,13 +19,14 @@ CREATE TABLE `PLM_USER` (
`email` varchar(255) NOT NULL,
`user_name` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`mfa_user_handle` BLOB DEFAULT NULL,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pourquoi ce n'est pas dans la table PLM_USER_MFA ?

PRIMARY KEY (`id`),
UNIQUE KEY `uniq_plm_user_email` (`email`),
UNIQUE KEY `uniq_plm_user_username` (`user_name`),
CONSTRAINT `plm_user_role` FOREIGN KEY (`id_role`) REFERENCES `PLM_ROLE` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `PLM_ROLE_PERMISSION`;

CREATE TABLE `PLM_ROLE_PERMISSION` (
`id_role` bigint(20) NOT NULL,
`permission` varchar(255) NOT NULL,
Expand All @@ -31,8 +35,41 @@ CREATE TABLE `PLM_ROLE_PERMISSION` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


DROP TABLE IF EXISTS `PLM_MFA_AUTHENTICATOR`;
CREATE TABLE `PLM_MFA_AUTHENTICATOR` (
`id` bigint(20) NOT NULL,
`secret_key` varchar(255) DEFAULT NULL,
`credential_id` BLOB DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `PLM_MFA_BROWSER`;
CREATE TABLE `PLM_MFA_BROWSER` (
`id` bigint(20) NOT NULL,
`key_id` BLOB NOT NULL,
`public_key_cose` BLOB NOT NULL,
`attestation` BLOB NOT NULL,
`client_data_json` BLOB NOT NULL,
`is_discoverable` tinyint(1) DEFAULT NULL,
`signature_count` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE `PLM_USER_MFA` (
`id` bigint(20) NOT NULL,
`type` ENUM('authenticator', 'browser') NOT NULL,
`id_user` bigint(20) NOT NULL,
`id_mfa_authenticator` bigint(20) DEFAULT NULL,
`id_mfa_browser` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `plm_user_mfa_user` FOREIGN KEY (`id_user`) REFERENCES `PLM_USER` (`id`),
CONSTRAINT `plm_user_mfa_mfa_authenticator` FOREIGN KEY (`id_mfa_authenticator`) REFERENCES `PLM_MFA_AUTHENTICATOR` (`id`),
CONSTRAINT `plm_user_mfa_mfa_browser` FOREIGN KEY (`id_mfa_browser`) REFERENCES `PLM_MFA_BROWSER` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO PLM_ROLE VALUES(1, 'Administrator');
INSERT INTO PLM_USER VALUES(1, 1, NOW(), 'Admin', 'Admin', 'admin@admin', 'admin', '$2a$11$FfgtfoHeNo/m9jGj9D5rTO0zDDI4LkMXnXHai744Ee32P3CHoBVqm');
INSERT INTO PLM_USER VALUES(1, 1, NOW(), 'Admin', 'Admin', 'admin@admin', 'admin', '$2a$11$FfgtfoHeNo/m9jGj9D5rTO0zDDI4LkMXnXHai744Ee32P3CHoBVqm', NULL);
INSERT INTO PLM_ROLE_PERMISSION VALUES(1, 'MANAGE_USERS');
INSERT INTO PLM_ROLE_PERMISSION VALUES(1, 'MANAGE_ROLES');
INSERT INTO PLM_ROLE_PERMISSION VALUES(1, 'MANAGE_SYSTEM');
1 change: 1 addition & 0 deletions plume-admin-ws/sql/setup-oracle.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ CREATE TABLE PLM_USER (
EMAIL varchar(255) NOT NULL,
USER_NAME varchar(255) NOT NULL,
PASSWORD varchar(255) NOT NULL,
SECRET_KEY varchar(255) NOT NULL,
CONSTRAINT plm_user_pk PRIMARY KEY (ID),
CONSTRAINT uniq_plm_user_email UNIQUE (EMAIL),
CONSTRAINT uniq_plm_user_username UNIQUE (USER_NAME),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.coreoz.plume.admin.db.daos;

import javax.inject.Inject;
import javax.inject.Singleton;

import com.coreoz.plume.admin.db.generated.AdminMfaAuthenticator;
import com.coreoz.plume.admin.db.generated.QAdminMfaAuthenticator;
import com.coreoz.plume.db.querydsl.crud.CrudDaoQuerydsl;
import com.coreoz.plume.db.querydsl.transaction.TransactionManagerQuerydsl;

@Singleton
public class AdminMfaAuthenticatorDao extends CrudDaoQuerydsl<AdminMfaAuthenticator> {

@Inject
private AdminMfaAuthenticatorDao(TransactionManagerQuerydsl transactionManager) {
super(transactionManager, QAdminMfaAuthenticator.adminMfaAuthenticator);
}
}
Loading