- Hashage du mot de passe lors de la création du compte

-  Localisation d'un problème empêchant de se connecter plusieurs fois (ca path est mis à NULL)
This commit is contained in:
Benjamin 2022-03-11 22:05:09 +01:00
parent a2e86eb02d
commit 8746fa3b65
13 changed files with 227 additions and 48 deletions

View File

@ -13,21 +13,20 @@ import lightcontainer.repository.FileFrontEnd;
import lightcontainer.repository.ProtocolRepositoryImpl; import lightcontainer.repository.ProtocolRepositoryImpl;
import lightcontainer.repository.StoreProcessorRepository; import lightcontainer.repository.StoreProcessorRepository;
import lightcontainer.storage.AppData; import lightcontainer.storage.AppData;
import lightcontainer.storage.JsonAdapter;
import lightcontainer.storage.Repository;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class App { public class App {
// Constant config server
// -- Unicast client port
private static final int UNICAST_PORT = 8000;
// -- Multicast listener ip, port
private static final String MULTICAST_IP = "226.66.66.1";
private static final int MULTICAST_PORT = 15502;
public static void main(String[] args) { public static void main(String[] args) {
setupVM(); setupVM();
Repository repositoryStorage = prepareStorage();
AppData appData = AppData.getInstance();
// Create all repository // Create all repository
ClientHandlerRepository clientRep = new ClientHandlerRepository(); ClientHandlerRepository clientRep = new ClientHandlerRepository();
@ -49,8 +48,8 @@ public class App {
protocolRep.addWriter(new SendfileRule()); protocolRep.addWriter(new SendfileRule());
FileFrontEnd ffe = new FileFrontEnd(clientRep, storeRep, protocolRep); FileFrontEnd ffe = new FileFrontEnd(clientRep, storeRep, protocolRep);
new UnicastServerListener(ffe, clientRep, protocolRep, UNICAST_PORT); new UnicastServerListener(ffe, clientRep, protocolRep, repositoryStorage, repositoryStorage.getUnicastPort());
new MulticastServerListener(ffe, storeRep, protocolRep, MULTICAST_IP, MULTICAST_PORT); new MulticastServerListener(ffe, storeRep, protocolRep, repositoryStorage.getMulticastIp(), repositoryStorage.getMulticastPort());
// close repo et client et server. // close repo et client et server.
@ -60,6 +59,17 @@ public class App {
// storeRep.close(); // storeRep.close();
} }
private static Repository prepareStorage() {
AppData appData = AppData.getInstance();
Repository repository = new Repository(
Paths.get("src", "main", "resources", "appdata.json").toAbsolutePath().toString(),
appData, new JsonAdapter()
);
repository.load();
return repository;
}
private static void setupVM() { private static void setupVM() {
System.setProperty("javax.net.ssl.keyStore","../ffe.labo.swilabus.com.p12"); System.setProperty("javax.net.ssl.keyStore","../ffe.labo.swilabus.com.p12");
System.setProperty("javax.net.ssl.keyStorePassword","labo2022"); System.setProperty("javax.net.ssl.keyStorePassword","labo2022");

View File

@ -12,6 +12,8 @@ import lightcontainer.protocol.rules.reader.SignupRule;
import lightcontainer.protocol.rules.writer.SignErrorRule; import lightcontainer.protocol.rules.writer.SignErrorRule;
import lightcontainer.protocol.rules.writer.SignOkRule; import lightcontainer.protocol.rules.writer.SignOkRule;
import javax.crypto.BadPaddingException;
import javax.net.ssl.SSLHandshakeException;
import java.io.*; import java.io.*;
import java.net.Socket; import java.net.Socket;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -64,6 +66,7 @@ public class ClientHandler implements Runnable, AutoCloseable {
* @see PrintWriter * @see PrintWriter
*/ */
private void initClient() { private void initClient() {
// Start the thread
try { try {
this.reader = new BufferedReader(new InputStreamReader( this.reader = new BufferedReader(new InputStreamReader(
this.client.getInputStream(), this.client.getInputStream(),
@ -94,18 +97,18 @@ public class ClientHandler implements Runnable, AutoCloseable {
System.out.println("Client: " + command); System.out.println("Client: " + command);
} else { } else {
repository.disconnect(this); repository.disconnect(this);
break;
} }
ProtocolReader.ProtocolResult ruleResult = protocolRep.executeReader(context, command + "\r\n"); ProtocolReader.ProtocolResult ruleResult = protocolRep.executeReader(context, command + "\r\n");
if (ruleResult == null) { if (ruleResult == null) {
repository.disconnect(this); repository.disconnect(this);
return; break;
} }
if (checkAccess(ruleResult)) { if (checkAccess(ruleResult)) {
ruleResult.read( ruleResult.read(this.client.getInputStream());
this.client.getInputStream()
);
ProtocolWriter.ProtocolResult writerCommand = ruleResult.getResultCommand(); ProtocolWriter.ProtocolResult writerCommand = ruleResult.getResultCommand();
if (ruleResult.getReceiver() == ProtocolReader.ResultCmdReceiver.STOREBACKEND) { if (ruleResult.getReceiver() == ProtocolReader.ResultCmdReceiver.STOREBACKEND) {
@ -123,14 +126,23 @@ public class ClientHandler implements Runnable, AutoCloseable {
} }
} else { } else {
System.out.println(4);
accessDenied(); accessDenied();
} }
} catch (IOException ignore) { } catch (IOException ignore) {
ignore.printStackTrace(); ignore.printStackTrace();
repository.disconnect(this); repository.disconnect(this);
break;
} }
} }
try {
this.reader.close();
this.writer.close();
this.client.close();
System.out.printf("[CLIENT] %s s'est déconnecté\n", context.getLogin());
} catch (IOException ignored) { }
} }
/** /**
@ -178,6 +190,18 @@ public class ClientHandler implements Runnable, AutoCloseable {
} catch (ClassCastException e2) { } } catch (ClassCastException e2) { }
} }
/**
* Vérifie s'il s'âgit d'une demande de déconnexion
* @param ruleResult
*/
private void checkSignError(ProtocolWriter.ProtocolResult ruleResult) {
if (ruleResult.getCommand().startsWith(SignErrorRule.NAME)) {
System.out.println("Pas pu connecter");
repository.disconnect(this);
}
}
/** /**
* Permet au Client d'attendre la fin de la réalisation de sa tâche * Permet au Client d'attendre la fin de la réalisation de sa tâche
*/ */
@ -210,16 +234,12 @@ public class ClientHandler implements Runnable, AutoCloseable {
*/ */
@Override @Override
public void close() { public void close() {
if (this.client_run) { System.out.println("Call close");
try {
this.client_run = false; this.client_run = false;
this.client.close();
System.out.printf("[CLIENT] %s s'est déconnecté\n", context.getLogin());
} catch (IOException ignored) { }
}
} }
public String getLogin() { public String getLogin() {
return this.context.getLogin(); return this.context.getLogin();
} }
} }

View File

@ -1,8 +1,10 @@
package lightcontainer.domains.client; package lightcontainer.domains.client;
import lightcontainer.storage.AppData; import lightcontainer.storage.AppData;
import lightcontainer.storage.Repository;
import lightcontainer.storage.User; import lightcontainer.storage.User;
import lightcontainer.utils.AES_GCM; import lightcontainer.utils.AES_GCM;
import lightcontainer.utils.ShaHasher;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.LinkedList; import java.util.LinkedList;
@ -13,7 +15,8 @@ import java.util.LinkedList;
*/ */
public class Context { public class Context {
private AppData appData; private Repository repository;
/** /**
* Login de l'utilisateur * Login de l'utilisateur
@ -21,8 +24,8 @@ public class Context {
private String login; private String login;
// Constructeur // Constructeur
public Context(AppData appData) { public Context(Repository repository) {
this.appData = appData; this.repository = repository;
} }
@ -35,7 +38,12 @@ public class Context {
public boolean createUser(String login, String password) { public boolean createUser(String login, String password) {
try { try {
String key = AES_GCM.generateSecretKey(); String key = AES_GCM.generateSecretKey();
if (this.appData.addUser(login, password, key)) {
ShaHasher hasher = new ShaHasher(password);
password = hasher.nextHashing();
String passwordSalt = hasher.getSalt();
if (this.repository.addUser(login, password, key, passwordSalt)) {
this.login = login; this.login = login;
return true; return true;
} }
@ -59,10 +67,15 @@ public class Context {
* @return TRUE si l'utilisateur a été authentifié * @return TRUE si l'utilisateur a été authentifié
*/ */
public boolean signIn(String login, String password) { public boolean signIn(String login, String password) {
if (login.equals("aaaaa") && password.equals("aaaaa")) { String passwordSalt = this.repository.getUserPasswordSalt(login);
if (passwordSalt != null) {
ShaHasher hasher = new ShaHasher(password);
System.out.println(hasher.fromSalt(passwordSalt));
if (this.repository.verifyUser(login, hasher.fromSalt(passwordSalt))) {
this.login = login; this.login = login;
return true; return true;
} }
}
return false; return false;
} }

View File

@ -6,9 +6,11 @@ import lightcontainer.interfaces.ProtocolRepository;
import lightcontainer.interfaces.UnicastCHR; import lightcontainer.interfaces.UnicastCHR;
import lightcontainer.repository.FileFrontEnd; import lightcontainer.repository.FileFrontEnd;
import lightcontainer.storage.AppData; import lightcontainer.storage.AppData;
import lightcontainer.storage.Repository;
import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import java.io.IOException; import java.io.IOException;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
@ -21,12 +23,14 @@ public class UnicastServerListener implements Runnable {
private ProtocolRepository protocolRep; private ProtocolRepository protocolRep;
private final int server_port; private final int server_port;
private boolean server_run; private boolean server_run;
private Repository repositoryStorage;
// Constructor // Constructor
public UnicastServerListener(FileFrontEnd ffe, UnicastCHR repository, ProtocolRepository protocolRep, int port) { public UnicastServerListener(FileFrontEnd ffe, UnicastCHR repository, ProtocolRepository protocolRep, Repository repositoryStorage, int port) {
this.ffe = ffe; this.ffe = ffe;
this.repository = repository; this.repository = repository;
this.protocolRep = protocolRep; this.protocolRep = protocolRep;
this.repositoryStorage = repositoryStorage;
this.server_port = port; this.server_port = port;
this.server_run = false; this.server_run = false;
repository.setServerListener(this); repository.setServerListener(this);
@ -48,10 +52,10 @@ public class UnicastServerListener implements Runnable {
this.server_run = true; this.server_run = true;
while (this.server_run) { while (this.server_run) {
// Accepting connection requests (blocking) // Accepting connection requests (blocking)
Socket client = this.server.accept(); SSLSocket client = (SSLSocket) this.server.accept();
System.out.println("New Client");
// Create a new Handler client by passing these dependencies to it // Create a new Handler client by passing these dependencies to it
ClientHandler clientHandler = new ClientHandler(this.repository, client, ffe, protocolRep, new Context(AppData.getInstance())); ClientHandler clientHandler = new ClientHandler(this.repository, client, ffe, protocolRep, new Context(repositoryStorage));
// Add the client handler to its repository (clienthandlerrepository) // Add the client handler to its repository (clienthandlerrepository)
this.repository.addClient(clientHandler); this.repository.addClient(clientHandler);
// Start the thread // Start the thread

View File

@ -55,7 +55,7 @@ public class SignupRule extends ProtocolReader {
result.setResultCommand(this.protocolRep.executeWriter(SignErrorRule.NAME), ResultCmdReceiver.CLIENT); result.setResultCommand(this.protocolRep.executeWriter(SignErrorRule.NAME), ResultCmdReceiver.CLIENT);
} }
return null; return result;
} }
@Override @Override

View File

@ -73,8 +73,8 @@ public class AppData {
* *
* @return True if the user was added. False if a user with the same name already exists. * @return True if the user was added. False if a user with the same name already exists.
*/ */
public boolean addUser(String login, String password, String key) { public boolean addUser(String login, String password, String key, String passwordSalt) {
User user = new User(login, password, key, new HashMap<>()); User user = new User(login, password, key, passwordSalt, new HashMap<>());
if (this.users.containsKey(user.getName())) { if (this.users.containsKey(user.getName())) {
return false; return false;
} else { } else {
@ -142,4 +142,13 @@ public class AppData {
} }
} }
public boolean verifyUser(String login, String password) {
User user = getUser(login);
return user != null && user.verifyPassword(password);
}
public String getUserPasswordSalt(String login) {
User user = getUser(login);
return user == null ? null : user.getPasswordSalt();
}
} }

View File

@ -39,6 +39,7 @@ public class JsonAdapter implements Adapter {
user.addProperty("name", current.getName()); user.addProperty("name", current.getName());
user.addProperty("password", current.getPassword()); user.addProperty("password", current.getPassword());
user.addProperty("aes_key", current.getAesKey()); user.addProperty("aes_key", current.getAesKey());
user.addProperty("passwordSalt", current.getPasswordSalt());
JsonArray files = new JsonArray(); JsonArray files = new JsonArray();
Iterator<File> fileIterator = current.fileIterator(); Iterator<File> fileIterator = current.fileIterator();
addFiles(fileIterator, files); addFiles(fileIterator, files);
@ -90,7 +91,7 @@ public class JsonAdapter implements Adapter {
AppData appData = AppData.getInstance(); AppData appData = AppData.getInstance();
appData.setAppConfig(appConfig); appData.setAppConfig(appConfig);
for (User user : users) { for (User user : users) {
appData.addUser(user.getName(), user.getPassword(), user.getAesKey()); appData.addUser(user.getName(), user.getPassword(), user.getAesKey(), "");
} }
return appData; return appData;
} catch (JsonParseException parseException) { } catch (JsonParseException parseException) {
@ -105,10 +106,11 @@ public class JsonAdapter implements Adapter {
String name = jsonUser.get("name").getAsString(); String name = jsonUser.get("name").getAsString();
String password = jsonUser.get("password").getAsString(); String password = jsonUser.get("password").getAsString();
String aeskey = jsonUser.get("aes_key").getAsString(); String aeskey = jsonUser.get("aes_key").getAsString();
String passwordSalt = jsonUser.get("passwordSalt").getAsString();
Map<String, File> userFiles = new HashMap<>(); Map<String, File> userFiles = new HashMap<>();
JsonArray jsonFiles = jsonUser.getAsJsonArray("files"); JsonArray jsonFiles = jsonUser.getAsJsonArray("files");
getFiles(userFiles, jsonFiles); getFiles(userFiles, jsonFiles);
User user = new User(name, password, aeskey, userFiles); User user = new User(name, password, aeskey, passwordSalt, userFiles);
users.add(user); users.add(user);
} }
} }

View File

@ -29,7 +29,7 @@ public class Repository {
*/ */
public void save() { public void save() {
if (filePath != null) { if (filePath != null) {
String jsonAppData = adapter.toString(); String jsonAppData = adapter.toString(appData);
try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get(filePath).toAbsolutePath(), StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get(filePath).toAbsolutePath(), StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
bufferedWriter.write(jsonAppData); bufferedWriter.write(jsonAppData);
bufferedWriter.flush(); bufferedWriter.flush();
@ -39,8 +39,8 @@ public class Repository {
} }
} }
public boolean addUser(String login, String password, String key) { public boolean addUser(String login, String password, String key, String passwordSalt) {
if (appData.addUser(login, password, key)) { if (appData.addUser(login, password, key, passwordSalt)) {
save(); save();
return true; return true;
} }
@ -94,4 +94,24 @@ public class Repository {
} }
return builder.toString(); return builder.toString();
} }
public int getUnicastPort() {
return appData.getAppConfig().getUnicastPort();
}
public String getMulticastIp() {
return appData.getAppConfig().getMulticastIp();
}
public int getMulticastPort() {
return appData.getAppConfig().getMulticastPort();
}
public boolean verifyUser(String login, String password) {
return appData.verifyUser(login, password);
}
public String getUserPasswordSalt(String login) {
return appData.getUserPasswordSalt(login);
}
} }

View File

@ -15,12 +15,14 @@ public class User {
private final String Name; private final String Name;
private final String password; private final String password;
private final String aesKey; private final String aesKey;
private final String passwordSalt;
private final Map<String, File> files; private final Map<String, File> files;
public User(String Name, String password, String aesKey, Map<String, File> files) { public User(String Name, String password, String aesKey, String passwordSalt, Map<String, File> files) {
this.Name = Name; this.Name = Name;
this.password = password; this.password = password;
this.aesKey = aesKey; this.aesKey = aesKey;
this.passwordSalt = passwordSalt;
this.files = files; this.files = files;
} }
@ -36,6 +38,10 @@ public class User {
return aesKey; return aesKey;
} }
public String getPasswordSalt() {
return this.passwordSalt;
}
public Iterator<File> fileIterator() { public Iterator<File> fileIterator() {
return files.values().iterator(); return files.values().iterator();
} }
@ -77,4 +83,9 @@ public class User {
return false; return false;
} }
} }
public boolean verifyPassword(String password) {
return this.password.equals(password);
}
} }

View File

@ -0,0 +1,78 @@
package lightcontainer.utils;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
/**
* Permet de hasher du texte
*/
public class ShaHasher {
/**
* Mot de passe non-hashé
*/
private final String password;
/**
* Sallage appliqué sur le mot de passe précédement généré
*/
private byte[] salt;
public ShaHasher(String password) {
this.password = password;
}
/**
* Permet de demander un nouvel hashage sur le mot de passe
*
* @return Mot de passe hashé
* <p>
* Source : https://www.javaguides.net/2020/02/java-sha-384-hash-with-salt-example.html
*/
public String nextHashing() {
this.salt = generateSalt();
return fromSalt(getSalt());
}
public String fromSalt(String passwordSalt) {
String generatedPassword = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-384");
md.update(saltToByte(passwordSalt));
byte[] bytes = md.digest(password.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
generatedPassword = sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return generatedPassword;
}
private byte[] generateSalt() {
this.salt = new byte[16];
SecureRandom random = new SecureRandom();
random.nextBytes(salt);
return salt;
}
public String getSalt() {
Base64.Encoder b64Encoder = Base64.getEncoder();
return b64Encoder.encodeToString(this.salt);
}
private byte[] saltToByte(String salt) {
Base64.Decoder b64Decoder = Base64.getDecoder();
return b64Decoder.decode(salt);
}
}

View File

@ -0,0 +1,16 @@
{
"unicast_port": 8000,
"multicast_ip": "226.66.66.1",
"multicast_port": 15502,
"network_interface": "My network interface",
"tls": true,
"users": [
{
"name": "benjamin",
"password": "08ffabe5c9577b4c809aa4eeee61c1859d4b5c44b0acfe9534a81ae48c3ba1a1d372f4a6bdaad2bb46483e0899cd765b",
"aes_key": "FaiZVQaeJF1qrbcOsM0yaUdzcmeIZ3p9R3NZwA5zPcs=",
"passwordSalt": "azA4e8Dtw+svxQWWnJ+rlA==",
"files": []
}
]
}

View File

@ -2,9 +2,7 @@ package lightcontainer.storage;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Set; import java.util.Set;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@ -26,7 +24,7 @@ public class JsonAdapterTests {
storage.add("StorBackEnd1"); storage.add("StorBackEnd1");
File file1 = new File("File1", 15, "8d8d8d8d", storage); File file1 = new File("File1", 15, "8d8d8d8d", storage);
appData.setAppConfig(appConfig); appData.setAppConfig(appConfig);
appData.addUser("User1", "Password","djdjjdj"); appData.addUser("User1", "Password","djdjjdj", "");
appData.addFileFor(file1, "User1"); appData.addFileFor(file1, "User1");
JsonAdapter jsonAdapter = new JsonAdapter(); JsonAdapter jsonAdapter = new JsonAdapter();
//WHEN the adapter converts AppData to Json //WHEN the adapter converts AppData to Json

View File

@ -6,9 +6,7 @@ import org.junit.jupiter.api.Test;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Set; import java.util.Set;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@ -39,7 +37,7 @@ public class RepositoryTests {
storage.add("StorBackEnd1"); storage.add("StorBackEnd1");
File file1 = new File("File1", 15, "8d8d8d8d", storage); File file1 = new File("File1", 15, "8d8d8d8d", storage);
appData.setAppConfig(appConfig); appData.setAppConfig(appConfig);
appData.addUser("User1", "Password", "djdjjdj"); appData.addUser("User1", "Password", "djdjjdj", "");
JsonAdapter jsonAdapter = new JsonAdapter(); JsonAdapter jsonAdapter = new JsonAdapter();
appData.addFileFor(file1, "User1"); appData.addFileFor(file1, "User1");
//WHEN Repository calls save method //WHEN Repository calls save method