diff --git a/app/src/main/java/lightcontainer/App.java b/app/src/main/java/lightcontainer/App.java index fba84bc..88d4ae3 100644 --- a/app/src/main/java/lightcontainer/App.java +++ b/app/src/main/java/lightcontainer/App.java @@ -13,21 +13,20 @@ import lightcontainer.repository.FileFrontEnd; import lightcontainer.repository.ProtocolRepositoryImpl; import lightcontainer.repository.StoreProcessorRepository; 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 { - // 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) { setupVM(); - - AppData appData = AppData.getInstance(); + Repository repositoryStorage = prepareStorage(); // Create all repository ClientHandlerRepository clientRep = new ClientHandlerRepository(); @@ -49,8 +48,8 @@ public class App { protocolRep.addWriter(new SendfileRule()); FileFrontEnd ffe = new FileFrontEnd(clientRep, storeRep, protocolRep); - new UnicastServerListener(ffe, clientRep, protocolRep, UNICAST_PORT); - new MulticastServerListener(ffe, storeRep, protocolRep, MULTICAST_IP, MULTICAST_PORT); + new UnicastServerListener(ffe, clientRep, protocolRep, repositoryStorage, repositoryStorage.getUnicastPort()); + new MulticastServerListener(ffe, storeRep, protocolRep, repositoryStorage.getMulticastIp(), repositoryStorage.getMulticastPort()); // close repo et client et server. @@ -60,6 +59,17 @@ public class App { // 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() { System.setProperty("javax.net.ssl.keyStore","../ffe.labo.swilabus.com.p12"); System.setProperty("javax.net.ssl.keyStorePassword","labo2022"); diff --git a/app/src/main/java/lightcontainer/domains/client/ClientHandler.java b/app/src/main/java/lightcontainer/domains/client/ClientHandler.java index 9da2ee0..380061d 100644 --- a/app/src/main/java/lightcontainer/domains/client/ClientHandler.java +++ b/app/src/main/java/lightcontainer/domains/client/ClientHandler.java @@ -12,6 +12,8 @@ import lightcontainer.protocol.rules.reader.SignupRule; import lightcontainer.protocol.rules.writer.SignErrorRule; import lightcontainer.protocol.rules.writer.SignOkRule; +import javax.crypto.BadPaddingException; +import javax.net.ssl.SSLHandshakeException; import java.io.*; import java.net.Socket; import java.nio.charset.StandardCharsets; @@ -64,6 +66,7 @@ public class ClientHandler implements Runnable, AutoCloseable { * @see PrintWriter */ private void initClient() { + // Start the thread try { this.reader = new BufferedReader(new InputStreamReader( this.client.getInputStream(), @@ -94,18 +97,18 @@ public class ClientHandler implements Runnable, AutoCloseable { System.out.println("Client: " + command); } else { repository.disconnect(this); + break; } ProtocolReader.ProtocolResult ruleResult = protocolRep.executeReader(context, command + "\r\n"); if (ruleResult == null) { repository.disconnect(this); - return; + break; } if (checkAccess(ruleResult)) { - ruleResult.read( - this.client.getInputStream() - ); + ruleResult.read(this.client.getInputStream()); + ProtocolWriter.ProtocolResult writerCommand = ruleResult.getResultCommand(); if (ruleResult.getReceiver() == ProtocolReader.ResultCmdReceiver.STOREBACKEND) { @@ -123,14 +126,23 @@ public class ClientHandler implements Runnable, AutoCloseable { } } else { + System.out.println(4); accessDenied(); } } catch (IOException ignore) { ignore.printStackTrace(); 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) { } } + /** + * 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 */ @@ -210,16 +234,12 @@ public class ClientHandler implements Runnable, AutoCloseable { */ @Override public void close() { - if (this.client_run) { - try { - this.client_run = false; - this.client.close(); - System.out.printf("[CLIENT] %s s'est déconnecté\n", context.getLogin()); - } catch (IOException ignored) { } - } + System.out.println("Call close"); + this.client_run = false; } public String getLogin() { return this.context.getLogin(); } + } diff --git a/app/src/main/java/lightcontainer/domains/client/Context.java b/app/src/main/java/lightcontainer/domains/client/Context.java index fef239e..893df9d 100644 --- a/app/src/main/java/lightcontainer/domains/client/Context.java +++ b/app/src/main/java/lightcontainer/domains/client/Context.java @@ -1,8 +1,10 @@ package lightcontainer.domains.client; import lightcontainer.storage.AppData; +import lightcontainer.storage.Repository; import lightcontainer.storage.User; import lightcontainer.utils.AES_GCM; +import lightcontainer.utils.ShaHasher; import java.security.NoSuchAlgorithmException; import java.util.LinkedList; @@ -13,7 +15,8 @@ import java.util.LinkedList; */ public class Context { - private AppData appData; + private Repository repository; + /** * Login de l'utilisateur @@ -21,8 +24,8 @@ public class Context { private String login; // Constructeur - public Context(AppData appData) { - this.appData = appData; + public Context(Repository repository) { + this.repository = repository; } @@ -35,7 +38,12 @@ public class Context { public boolean createUser(String login, String password) { try { 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; return true; } @@ -59,9 +67,14 @@ public class Context { * @return TRUE si l'utilisateur a été authentifié */ public boolean signIn(String login, String password) { - if (login.equals("aaaaa") && password.equals("aaaaa")) { - this.login = login; - return true; + 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; + return true; + } } return false; } diff --git a/app/src/main/java/lightcontainer/domains/server/UnicastServerListener.java b/app/src/main/java/lightcontainer/domains/server/UnicastServerListener.java index d0bac9d..e68abf1 100644 --- a/app/src/main/java/lightcontainer/domains/server/UnicastServerListener.java +++ b/app/src/main/java/lightcontainer/domains/server/UnicastServerListener.java @@ -6,9 +6,11 @@ import lightcontainer.interfaces.ProtocolRepository; import lightcontainer.interfaces.UnicastCHR; import lightcontainer.repository.FileFrontEnd; import lightcontainer.storage.AppData; +import lightcontainer.storage.Repository; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; @@ -21,12 +23,14 @@ public class UnicastServerListener implements Runnable { private ProtocolRepository protocolRep; private final int server_port; private boolean server_run; + private Repository repositoryStorage; // 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.repository = repository; this.protocolRep = protocolRep; + this.repositoryStorage = repositoryStorage; this.server_port = port; this.server_run = false; repository.setServerListener(this); @@ -48,10 +52,10 @@ public class UnicastServerListener implements Runnable { this.server_run = true; while (this.server_run) { // Accepting connection requests (blocking) - Socket client = this.server.accept(); - System.out.println("New Client"); + SSLSocket client = (SSLSocket) this.server.accept(); + // 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) this.repository.addClient(clientHandler); // Start the thread diff --git a/app/src/main/java/lightcontainer/protocol/rules/reader/SignupRule.java b/app/src/main/java/lightcontainer/protocol/rules/reader/SignupRule.java index f8efa4a..f1ef186 100644 --- a/app/src/main/java/lightcontainer/protocol/rules/reader/SignupRule.java +++ b/app/src/main/java/lightcontainer/protocol/rules/reader/SignupRule.java @@ -55,7 +55,7 @@ public class SignupRule extends ProtocolReader { result.setResultCommand(this.protocolRep.executeWriter(SignErrorRule.NAME), ResultCmdReceiver.CLIENT); } - return null; + return result; } @Override diff --git a/app/src/main/java/lightcontainer/storage/AppData.java b/app/src/main/java/lightcontainer/storage/AppData.java index 2b5db46..88ff36f 100644 --- a/app/src/main/java/lightcontainer/storage/AppData.java +++ b/app/src/main/java/lightcontainer/storage/AppData.java @@ -73,8 +73,8 @@ public class AppData { * * @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) { - User user = new User(login, password, key, new HashMap<>()); + public boolean addUser(String login, String password, String key, String passwordSalt) { + User user = new User(login, password, key, passwordSalt, new HashMap<>()); if (this.users.containsKey(user.getName())) { return false; } 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(); + } } diff --git a/app/src/main/java/lightcontainer/storage/JsonAdapter.java b/app/src/main/java/lightcontainer/storage/JsonAdapter.java index 4cb8060..abb1f45 100644 --- a/app/src/main/java/lightcontainer/storage/JsonAdapter.java +++ b/app/src/main/java/lightcontainer/storage/JsonAdapter.java @@ -39,6 +39,7 @@ public class JsonAdapter implements Adapter { user.addProperty("name", current.getName()); user.addProperty("password", current.getPassword()); user.addProperty("aes_key", current.getAesKey()); + user.addProperty("passwordSalt", current.getPasswordSalt()); JsonArray files = new JsonArray(); Iterator fileIterator = current.fileIterator(); addFiles(fileIterator, files); @@ -90,7 +91,7 @@ public class JsonAdapter implements Adapter { AppData appData = AppData.getInstance(); appData.setAppConfig(appConfig); for (User user : users) { - appData.addUser(user.getName(), user.getPassword(), user.getAesKey()); + appData.addUser(user.getName(), user.getPassword(), user.getAesKey(), ""); } return appData; } catch (JsonParseException parseException) { @@ -105,10 +106,11 @@ public class JsonAdapter implements Adapter { String name = jsonUser.get("name").getAsString(); String password = jsonUser.get("password").getAsString(); String aeskey = jsonUser.get("aes_key").getAsString(); + String passwordSalt = jsonUser.get("passwordSalt").getAsString(); Map userFiles = new HashMap<>(); JsonArray jsonFiles = jsonUser.getAsJsonArray("files"); getFiles(userFiles, jsonFiles); - User user = new User(name, password, aeskey, userFiles); + User user = new User(name, password, aeskey, passwordSalt, userFiles); users.add(user); } } diff --git a/app/src/main/java/lightcontainer/storage/Repository.java b/app/src/main/java/lightcontainer/storage/Repository.java index 6f2d6f2..ecf6579 100644 --- a/app/src/main/java/lightcontainer/storage/Repository.java +++ b/app/src/main/java/lightcontainer/storage/Repository.java @@ -29,7 +29,7 @@ public class Repository { */ public void save() { 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)) { bufferedWriter.write(jsonAppData); bufferedWriter.flush(); @@ -39,8 +39,8 @@ public class Repository { } } - public boolean addUser(String login, String password, String key) { - if (appData.addUser(login, password, key)) { + public boolean addUser(String login, String password, String key, String passwordSalt) { + if (appData.addUser(login, password, key, passwordSalt)) { save(); return true; } @@ -94,4 +94,24 @@ public class Repository { } 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); + } } diff --git a/app/src/main/java/lightcontainer/storage/User.java b/app/src/main/java/lightcontainer/storage/User.java index 47a65f9..d79230f 100644 --- a/app/src/main/java/lightcontainer/storage/User.java +++ b/app/src/main/java/lightcontainer/storage/User.java @@ -15,12 +15,14 @@ public class User { private final String Name; private final String password; private final String aesKey; + private final String passwordSalt; private final Map files; - public User(String Name, String password, String aesKey, Map files) { + public User(String Name, String password, String aesKey, String passwordSalt, Map files) { this.Name = Name; this.password = password; this.aesKey = aesKey; + this.passwordSalt = passwordSalt; this.files = files; } @@ -36,6 +38,10 @@ public class User { return aesKey; } + public String getPasswordSalt() { + return this.passwordSalt; + } + public Iterator fileIterator() { return files.values().iterator(); } @@ -77,4 +83,9 @@ public class User { return false; } } + + public boolean verifyPassword(String password) { + return this.password.equals(password); + } + } diff --git a/app/src/main/java/lightcontainer/utils/ShaHasher.java b/app/src/main/java/lightcontainer/utils/ShaHasher.java new file mode 100644 index 0000000..bcb1019 --- /dev/null +++ b/app/src/main/java/lightcontainer/utils/ShaHasher.java @@ -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é + *

+ * 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); + } + +} diff --git a/app/src/main/resources/appdata.json b/app/src/main/resources/appdata.json new file mode 100644 index 0000000..67faaa8 --- /dev/null +++ b/app/src/main/resources/appdata.json @@ -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": [] + } + ] +} \ No newline at end of file diff --git a/app/src/test/java/lightcontainer/storage/JsonAdapterTests.java b/app/src/test/java/lightcontainer/storage/JsonAdapterTests.java index d00a0eb..70c8b87 100644 --- a/app/src/test/java/lightcontainer/storage/JsonAdapterTests.java +++ b/app/src/test/java/lightcontainer/storage/JsonAdapterTests.java @@ -2,9 +2,7 @@ package lightcontainer.storage; import org.junit.jupiter.api.Test; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Set; import static org.junit.jupiter.api.Assertions.*; @@ -26,7 +24,7 @@ public class JsonAdapterTests { storage.add("StorBackEnd1"); File file1 = new File("File1", 15, "8d8d8d8d", storage); appData.setAppConfig(appConfig); - appData.addUser("User1", "Password","djdjjdj"); + appData.addUser("User1", "Password","djdjjdj", ""); appData.addFileFor(file1, "User1"); JsonAdapter jsonAdapter = new JsonAdapter(); //WHEN the adapter converts AppData to Json diff --git a/app/src/test/java/lightcontainer/storage/RepositoryTests.java b/app/src/test/java/lightcontainer/storage/RepositoryTests.java index 5f96ea5..dfb1864 100644 --- a/app/src/test/java/lightcontainer/storage/RepositoryTests.java +++ b/app/src/test/java/lightcontainer/storage/RepositoryTests.java @@ -6,9 +6,7 @@ import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Set; import static org.junit.jupiter.api.Assertions.*; @@ -39,7 +37,7 @@ public class RepositoryTests { storage.add("StorBackEnd1"); File file1 = new File("File1", 15, "8d8d8d8d", storage); appData.setAppConfig(appConfig); - appData.addUser("User1", "Password", "djdjjdj"); + appData.addUser("User1", "Password", "djdjjdj", ""); JsonAdapter jsonAdapter = new JsonAdapter(); appData.addFileFor(file1, "User1"); //WHEN Repository calls save method