From 15e33fcfdd8f65c7c658567d8eaddd7e6ce4ac11 Mon Sep 17 00:00:00 2001 From: Laurent Date: Sun, 7 Dec 2025 00:38:22 +0100 Subject: [PATCH] Complete token generation and exchange between ACS and externalApp --- src/main/java/acq/acq/src/Main.java | 10 ++- src/main/java/acs/acs/src/Main.java | 42 ++++++++++--- .../java/acs/acs/src/json/CreditCard.java | 10 +++ src/main/java/acs/acs/src/json/Payload.java | 10 +++ .../common/common/src/crypto/CryptoUtils.java | 63 +++++++++++++++++++ .../common/common/src/crypto/KeyFactory.java | 36 ++++------- .../common/common/src/json/JsonManager.java | 33 ++++++++++ .../java/common/common/src/ports/Ports.java | 2 +- .../externalApp/externalApp/src/Main.java | 35 +++++++---- .../externalApp/src/json/Payload.java | 10 +++ 10 files changed, 202 insertions(+), 49 deletions(-) create mode 100644 src/main/java/acs/acs/src/json/CreditCard.java create mode 100644 src/main/java/acs/acs/src/json/Payload.java create mode 100644 src/main/java/common/common/src/crypto/CryptoUtils.java create mode 100644 src/main/java/common/common/src/json/JsonManager.java create mode 100644 src/main/java/externalApp/externalApp/src/json/Payload.java diff --git a/src/main/java/acq/acq/src/Main.java b/src/main/java/acq/acq/src/Main.java index 31ce7d3..b48f3c6 100644 --- a/src/main/java/acq/acq/src/Main.java +++ b/src/main/java/acq/acq/src/Main.java @@ -28,15 +28,16 @@ public class Main { SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); - //requestACS(ctx); + requestACS(ctx, ACS_SERVER_PORT); + requestACS(ctx, AUTH_PORT); Thread ACQServer = SSLServerFactory.createServer(ctx, ACQ_SERVER_PORT, Main::handleRequest); ACQServer.start(); } - public static void requestACS(SSLContext ctx) { + public static void requestACS(SSLContext ctx, int port) { SSLSocketFactory factory = ctx.getSocketFactory(); - try (SSLSocket socket = (SSLSocket) factory.createSocket(Main.HOST, ACS_SERVER_PORT)) { + try (SSLSocket socket = (SSLSocket) factory.createSocket(Main.HOST, port)) { socket.startHandshake(); String message = "Hello ACS"; @@ -50,6 +51,9 @@ public class Main { } } + + + public static void handleRequest(SSLSocket serverSocket) { Logger.displayInfo("Request handled"); } diff --git a/src/main/java/acs/acs/src/Main.java b/src/main/java/acs/acs/src/Main.java index 4d31dfe..e0050e0 100644 --- a/src/main/java/acs/acs/src/Main.java +++ b/src/main/java/acs/acs/src/Main.java @@ -1,15 +1,21 @@ package acs.acs.src; +import acs.acs.src.json.Payload; import common.common.src.crypto.KeyFactory; +import common.common.src.crypto.CryptoUtils; +import common.common.src.json.JsonManager; import common.common.src.logger.Logger; import common.common.src.requestHandlers.SSLServerFactory; import common.common.src.socket.SocketManager; import javax.net.ssl.*; import java.io.IOException; +import java.security.*; +import java.util.Base64; +import java.util.Map; import static common.common.src.ports.Ports.ACS_SERVER_PORT; -import static common.common.src.ports.Ports.PORT_AUTH; +import static common.common.src.ports.Ports.AUTH_PORT; public class Main { @@ -20,6 +26,8 @@ public class Main { private static final String TRUST_STORE_PATH = "assets/certs/acs/acs.truststore.p12"; private static final String TRUST_STORE_PWD = "hepl_truststore"; + private static String generatedToken; + public static void main(String[] args) throws Exception { KeyFactory loader = new KeyFactory(); @@ -27,11 +35,12 @@ public class Main { KeyManagerFactory kmf = loader.loadKeyStore(KEY_STORE_PATH, KEY_STORE_PWD); TrustManagerFactory tmf = loader.loadTrustStore(TRUST_STORE_PATH, TRUST_STORE_PWD); + SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); Thread serverThread = SSLServerFactory.createServer(ctx, ACS_SERVER_PORT, Main::handleRequest); - Thread authThread = SSLServerFactory.createServer(ctx, PORT_AUTH, Main::handleAuth); + Thread authThread = SSLServerFactory.createServer(ctx, AUTH_PORT, Main::handleAuth); serverThread.start(); authThread.start(); @@ -53,11 +62,30 @@ public class Main { private static void handleAuth(SSLSocket clientSocket) { try{ - String response = SocketManager.readResponse(clientSocket); - Logger.displayReceived(response); - SocketManager.send(clientSocket, "TOKEN_HERE"); - } catch (IOException e) { - throw new RuntimeException(e); + String msg = SocketManager.readResponse(clientSocket); + Logger.displayReceived(msg); + + Payload payload = JsonManager.deserialize(msg, Payload.class); + PublicKey publicKey = CryptoUtils.getPublicKey(TRUST_STORE_PATH, TRUST_STORE_PWD, "externalApp"); + + byte[] signatureBytes = Base64.getDecoder().decode(payload.signature()); + if (!CryptoUtils.checkSignature(publicKey, payload.data(), signatureBytes)) { + clientSocket.close(); + return; + } + + Logger.displayInfo("Signature OK"); + + PrivateKey privateKey = CryptoUtils.getPrivateKey(KEY_STORE_PATH, KEY_STORE_PWD, "acs"); + String token = CryptoUtils.generateToken(); + String signedToken = CryptoUtils.signData(privateKey, token); + + Map data = Map.of("token", token, "signature", signedToken); + String response = JsonManager.serialize(data); + SocketManager.send(clientSocket, response); + generatedToken = token; + } catch (IOException ioe) { + throw new RuntimeException(ioe); } } diff --git a/src/main/java/acs/acs/src/json/CreditCard.java b/src/main/java/acs/acs/src/json/CreditCard.java new file mode 100644 index 0000000..05dc9ab --- /dev/null +++ b/src/main/java/acs/acs/src/json/CreditCard.java @@ -0,0 +1,10 @@ +package acs.acs.src.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record CreditCard( + @JsonProperty("cardNumber") + String number, + @JsonProperty("expirationDate") + String expirationDate) { +} diff --git a/src/main/java/acs/acs/src/json/Payload.java b/src/main/java/acs/acs/src/json/Payload.java new file mode 100644 index 0000000..75673be --- /dev/null +++ b/src/main/java/acs/acs/src/json/Payload.java @@ -0,0 +1,10 @@ +package acs.acs.src.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record Payload( + @JsonProperty("data") + String data, + @JsonProperty("signature") + String signature) { +} diff --git a/src/main/java/common/common/src/crypto/CryptoUtils.java b/src/main/java/common/common/src/crypto/CryptoUtils.java new file mode 100644 index 0000000..539f902 --- /dev/null +++ b/src/main/java/common/common/src/crypto/CryptoUtils.java @@ -0,0 +1,63 @@ +package common.common.src.crypto; + +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.cert.Certificate; +import java.util.Base64; + +public class CryptoUtils { + + public static String signData(PrivateKey privateKey, String plainData) { + try { + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initSign(privateKey); + signature.update(plainData.getBytes(StandardCharsets.UTF_8)); + byte[] signedBytes = signature.sign(); + + return Base64.getEncoder().encodeToString(signedBytes); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static boolean checkSignature(PublicKey publicKey, String data, byte[] signature) { + try { + Signature sig = Signature.getInstance("SHA256withRSA"); + sig.initVerify(publicKey); + + sig.update(data.getBytes(StandardCharsets.UTF_8)); + + return sig.verify(signature); + } catch (Exception e){ + throw new RuntimeException(e); + } + } + + public static String generateToken(){ + byte[] bytes = new byte[32]; + new SecureRandom().nextBytes(bytes); + return Base64.getEncoder().encodeToString(bytes); + } + + public static PrivateKey getPrivateKey(String keyStorePath, String keyStorePwd, String alias) { + try { + KeyFactory keyFactory = new KeyFactory(); + KeyStore keyStore = keyFactory.createStore("PKCS12", keyStorePath, keyStorePwd); + return keyFactory.createPrivateKey(keyStore, alias, keyStorePwd); + } catch (Exception e){ + throw new RuntimeException(e); + } + } + + public static PublicKey getPublicKey(String trustStorePath, String truStorePwd, String alias) { + try { + KeyFactory loader = new KeyFactory(); + KeyStore trustStore = loader.createStore("PKCS12", trustStorePath, truStorePwd); + Certificate cert = trustStore.getCertificate(alias); + return cert.getPublicKey(); + } catch (Exception e){ + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/common/common/src/crypto/KeyFactory.java b/src/main/java/common/common/src/crypto/KeyFactory.java index 20d2b88..abebcc9 100644 --- a/src/main/java/common/common/src/crypto/KeyFactory.java +++ b/src/main/java/common/common/src/crypto/KeyFactory.java @@ -11,7 +11,7 @@ public class KeyFactory { return (PrivateKey) keyStore.getKey(alias, keystorePwd.toCharArray()); } - public KeyStore createKeyStore(String algorithm, String keyStorePath, String pwd){ + public KeyStore createStore(String algorithm, String keyStorePath, String pwd){ try (FileInputStream fis = new FileInputStream(keyStorePath)) { char[] keystorePass = pwd.toCharArray(); @@ -25,40 +25,26 @@ public class KeyFactory { public KeyManagerFactory loadKeyStore(String path, String pwd) { - KeyManagerFactory kmf = null; - - try (FileInputStream fis = new FileInputStream(path)) { - char[] keystorePass = pwd.toCharArray(); - - KeyStore ks = KeyStore.getInstance("PKCS12"); - ks.load(fis, keystorePass); - - kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks, keystorePass); - } catch (Exception e) { + try{ + KeyStore keyStore = createStore("PKCS12", path, pwd); + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(keyStore, pwd.toCharArray()); + return kmf; + } catch (Exception e){ throw new RuntimeException(e); } - - return kmf; } public TrustManagerFactory loadTrustStore(String path, String pwd) { - TrustManagerFactory tmf = null; - - try (FileInputStream fis = new FileInputStream(path)) { - char[] truststorePass = pwd.toCharArray(); - - KeyStore ts = KeyStore.getInstance("PKCS12"); - ts.load(fis, truststorePass); - - tmf = TrustManagerFactory.getInstance("SunX509"); + try{ + KeyStore ts = createStore("PKCS12", path, pwd); + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(ts); + return tmf; } catch (Exception e){ throw new RuntimeException(e); } - - return tmf; } } diff --git a/src/main/java/common/common/src/json/JsonManager.java b/src/main/java/common/common/src/json/JsonManager.java new file mode 100644 index 0000000..1fcecff --- /dev/null +++ b/src/main/java/common/common/src/json/JsonManager.java @@ -0,0 +1,33 @@ +package common.common.src.json; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import java.io.IOException; +import java.util.Map; + +public class JsonManager { + + public static T deserialize(String jsonString, Class clazz) { + ObjectMapper mapper = new ObjectMapper(); + try { + return mapper.readValue(jsonString, clazz); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static String serialize(Map map) { + try { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode json = mapper.createObjectNode(); + map.forEach((k, v) -> { + json.put(k, v.toString()); + }); + return mapper.writeValueAsString(json); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/common/common/src/ports/Ports.java b/src/main/java/common/common/src/ports/Ports.java index ac81d22..1d74b13 100644 --- a/src/main/java/common/common/src/ports/Ports.java +++ b/src/main/java/common/common/src/ports/Ports.java @@ -4,7 +4,7 @@ public class Ports { /** * Port d'écoute du service ACS pour la communication avec l'application externe. */ - public static int PORT_AUTH = 8786; + public static int AUTH_PORT = 8786; public static final int HTTP_SERVER_PORT = 8043; public static final int ACS_SERVER_PORT = 8443; public static final int ACQ_SERVER_PORT = 8543; diff --git a/src/main/java/externalApp/externalApp/src/Main.java b/src/main/java/externalApp/externalApp/src/Main.java index b815681..2609aa7 100644 --- a/src/main/java/externalApp/externalApp/src/Main.java +++ b/src/main/java/externalApp/externalApp/src/Main.java @@ -1,25 +1,26 @@ package externalApp.externalApp.src; +import common.common.src.crypto.CryptoUtils; import common.common.src.crypto.KeyFactory; -import common.common.src.ports.Ports; +import common.common.src.json.JsonManager; import common.common.src.logger.Logger; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import common.common.src.socket.SocketManager; +import externalApp.externalApp.src.json.Payload; import javax.net.ssl.*; import java.io.*; -import java.net.MalformedURLException; -import java.net.URI; import java.net.URISyntaxException; -import java.net.URL; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.*; +import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.Base64; +import java.util.Map; -import static common.common.src.ports.Ports.ACS_HOST; -import static common.common.src.ports.Ports.ACS_SERVER_PORT; +import static common.common.src.ports.Ports.*; public class Main { @@ -42,7 +43,7 @@ public class Main { KeyFactory keyFactory = new KeyFactory(); - KeyStore keyStore = keyFactory.createKeyStore("PKCS12", KEY_STORE_PATH, KEY_STORE_PWD); + KeyStore keyStore = keyFactory.createStore("PKCS12", KEY_STORE_PATH, KEY_STORE_PWD); KeyManagerFactory kmf = keyFactory.loadKeyStore(KEY_STORE_PATH, KEY_STORE_PWD); TrustManagerFactory tmf = keyFactory.loadTrustStore(TRUST_STORE_PATH, TRUST_STORE_PWD); @@ -59,7 +60,7 @@ public class Main { SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); - sendToACS(ctx, finalPayload); + sendToACS(ctx, finalPayload, AUTH_PORT); } @@ -92,11 +93,10 @@ public class Main { } } - private static void sendToACS(SSLContext ctx, String payload) { - Logger.displayInfo("Envoi des informations au service d'authentification à l'hôte " + Ports.ACS_HOST + ":" + Ports.PORT_AUTH); + private static void sendToACS(SSLContext ctx, String payload, int port) { SSLSocketFactory factory = ctx.getSocketFactory(); - try (SSLSocket socket = (SSLSocket) factory.createSocket(ACS_HOST, ACS_SERVER_PORT)) { + try (SSLSocket socket = (SSLSocket) factory.createSocket(ACS_HOST, port)) { socket.startHandshake(); SocketManager.send(socket, payload); @@ -104,10 +104,19 @@ public class Main { String response = SocketManager.readResponse(socket); Logger.displayReceived(response); + + PublicKey publicKey = CryptoUtils.getPublicKey(TRUST_STORE_PATH, TRUST_STORE_PWD, "acs"); + Payload responsePayload = JsonManager.deserialize(response, Payload.class); + + byte[] signatureBytes = Base64.getDecoder().decode(responsePayload.signature()); + if(CryptoUtils.checkSignature(publicKey, responsePayload.token(), signatureBytes)){ + Logger.displayInfo(responsePayload.token()); + } else { + Logger.displayInfo("Token NOK"); + } + } catch (Exception e) { throw new RuntimeException(e); } - } - } diff --git a/src/main/java/externalApp/externalApp/src/json/Payload.java b/src/main/java/externalApp/externalApp/src/json/Payload.java new file mode 100644 index 0000000..967fe3a --- /dev/null +++ b/src/main/java/externalApp/externalApp/src/json/Payload.java @@ -0,0 +1,10 @@ +package externalApp.externalApp.src.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record Payload( + @JsonProperty("token") + String token, + @JsonProperty("signature") + String signature) { +}