From fa02d690630029126df682e0e90e58cf74cec46e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Seron?= Date: Wed, 12 Nov 2025 16:15:39 +0100 Subject: [PATCH] temp push --- src/main/java/step3/Main.java | 180 ++++++++++++++++++++++++++++++++-- 1 file changed, 173 insertions(+), 7 deletions(-) diff --git a/src/main/java/step3/Main.java b/src/main/java/step3/Main.java index 691a952..a6199da 100644 --- a/src/main/java/step3/Main.java +++ b/src/main/java/step3/Main.java @@ -7,9 +7,28 @@ import java.io.InputStreamReader; import java.math.BigInteger; import java.net.ServerSocket; import java.net.Socket; +import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.MessageDigest; +import java.security.PublicKey; +import java.security.PrivateKey; +import java.security.KeyFactory; +import java.security.Signature; +import java.security.spec.X509EncodedKeySpec; +import java.security.spec.PSSParameterSpec; +import javax.crypto.spec.MGF1ParameterSpec; +import java.security.SecureRandom; +import java.util.Arrays; import java.util.Base64; +import javax.crypto.Cipher; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; + import static common.Logger.displayInfo; import static common.Logger.displayReceived; import static common.Logger.displaySent; @@ -67,7 +86,102 @@ public class Main { BigInteger Z = A.modPow(b, P); byte[] Z_bytes = toFixedLen(Z, 256); - + + // compute PBKDF-HMAC key + + byte[] key = PBKDF_HMAC_key(A, B, P, G); + + byte[] nonce = new byte[12]; + new java.security.SecureRandom().nextBytes(nonce); + + // Generate RSA 2048-bit key pair + KeyPair rsaKP = generateRSAKeyPair(); + + // Encrypt the RSA public key (X.509 DER) with AES-GCM using the derived key + byte[] pubBytes = rsaKP.getPublic().getEncoded(); + + // Calculer l'AAD (Additional Authenticated Data) + byte[] aad = AES_GCM(A, B, P, G); // retourne SHA-256(g || p || A || B) + + // Chiffrer avec AAD + SecretKeySpec aesKeySpec = new SecretKeySpec(key, "AES"); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec gcmSpec = new GCMParameterSpec(128, nonce); + cipher.init(Cipher.ENCRYPT_MODE, aesKeySpec, gcmSpec); + cipher.updateAAD(aad); // <-- Ajouter l'AAD AVANT doFinal + byte[] ciphertext = cipher.doFinal(pubBytes); + + // Assemble nonce || ciphertext and Base64-encode + byte[] out = new byte[nonce.length + ciphertext.length]; + System.arraycopy(nonce, 0, out, 0, nonce.length); + System.arraycopy(ciphertext, 0, out, nonce.length, ciphertext.length); + String payloadB64 = Base64.getEncoder().encodeToString(out); + + message = "PUBS:" + payloadB64; + send(socket, message); + displaySent("PUBS sent : " + message); + + // Read client's encrypted public key (PUBC) and encrypted RSA ciphertext (CT_RSA) + String PUBC = readResponse(reader); + displayReceived("Received PUBC : " + PUBC); + + String CT_RSA = readResponse(reader); + displayReceived("Received CT_RSA : " + CT_RSA); + + // Decrypt PUBC: format PUBC: + byte[] pubcDecoded = Base64.getDecoder().decode(PUBC.substring(PUBC.indexOf(':')+1)); + if (pubcDecoded.length < 12) throw new IOException("PUBC too short"); + byte[] pubcNonce = Arrays.copyOfRange(pubcDecoded, 0, 12); + byte[] pubcCt = Arrays.copyOfRange(pubcDecoded, 12, pubcDecoded.length); + + // Decrypt client's public key with AES-GCM + GCMParameterSpec pubcGcm = new GCMParameterSpec(128, pubcNonce); + Cipher aesDec = Cipher.getInstance("AES/GCM/NoPadding"); + aesDec.init(Cipher.DECRYPT_MODE, aesKeySpec, pubcGcm); + aesDec.updateAAD(aad); + byte[] clientPubBytes = aesDec.doFinal(pubcCt); + + // Recreate client's RSA PublicKey + KeyFactory kf = KeyFactory.getInstance("RSA"); + PublicKey clientPub = kf.generatePublic(new X509EncodedKeySpec(clientPubBytes)); + + // Decrypt CT_RSA which is RSA-OAEP encrypted with server's public key (so server uses private key) + byte[] ctRsaDecoded = Base64.getDecoder().decode(CT_RSA.substring(CT_RSA.indexOf(':')+1)); + Cipher rsaDec = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); + rsaDec.init(Cipher.DECRYPT_MODE, rsaKP.getPrivate()); + byte[] Mbytes = rsaDec.doFinal(ctRsaDecoded); + + // Sign M with RSASSA-PSS (SHA-256, MGF1(SHA-256), saltLen=32) + Signature signer = Signature.getInstance("SHA256withRSA/PSS"); + PSSParameterSpec pssSpec = new PSSParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), 32, 1); + signer.setParameter(pssSpec); + signer.initSign(rsaKP.getPrivate(), new SecureRandom()); + signer.update(Mbytes); + byte[] signature = signer.sign(); + + // Signature should be 256 bytes for 2048-bit RSA + if (signature.length != 256) { + // pad or handle as needed, but for now ensure we can split into two 128-byte chunks + } + + // Split signature into 2 chunks of 128 bytes + byte[] sig1 = Arrays.copyOfRange(signature, 0, 128); + byte[] sig2 = Arrays.copyOfRange(signature, 128, 256); + + // Encrypt each chunk with client's public key using RSA/OAEP (SHA-256) + Cipher rsaEnc = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); + rsaEnc.init(Cipher.ENCRYPT_MODE, clientPub); + byte[] encSig1 = rsaEnc.doFinal(sig1); + byte[] encSig2 = rsaEnc.doFinal(sig2); + + // Send two messages (labels SIG1 and SIG2) + send(socket, "SIG1:" + Base64.getEncoder().encodeToString(encSig1)); + displaySent("SIG1 sent"); + send(socket, "SIG2:" + Base64.getEncoder().encodeToString(encSig2)); + displaySent("SIG2 sent"); + + // Cleanup sensitive key material + Arrays.fill(key, (byte)0); } private static byte[] toFixedLen(BigInteger x, int len) { @@ -84,14 +198,66 @@ public class Main { return r; } - private static byte[] PBKDF_HMAC_key(BigInteger A, BigInteger B, BigInteger p, BigInteger g){ + private static byte[] PBKDF_HMAC_key(BigInteger A, BigInteger B, BigInteger p, BigInteger g) throws GeneralSecurityException, IOException { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream( ); - outputStream.write( toFixedLen(g, 8) ); - outputStream.write( toFixedLen(p, 256) ); - outputStream.write( toFixedLen(A, 256) ); - outputStream.write( toFixedLen(B, 256) ); + // Build the concatenation: g as 1 byte, p/A/B as 256 bytes each + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + outputStream.write(toFixedLen(g, 1)); + outputStream.write(toFixedLen(p, 256)); + outputStream.write(toFixedLen(A, 256)); + outputStream.write(toFixedLen(B, 256)); + byte[] concat = outputStream.toByteArray(); + + // SHA-256 over the concatenation + MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); + byte[] digest = sha256.digest(concat); + + // passphrase = Base64(SHA-256(...)) as a UTF-8 string (then used as password chars) + String passphraseB64 = Base64.getEncoder().encodeToString(digest); + char[] passphraseChars = passphraseB64.toCharArray(); + + // salt (UTF-8) with trailing space + byte[] salt = "phase3 aead key ".getBytes(StandardCharsets.UTF_8); + + PBEKeySpec spec = new PBEKeySpec(passphraseChars, salt, 600000, 256); + SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); + byte[] key = skf.generateSecret(spec).getEncoded(); + + // Cleanup sensitive data in memory where practical + Arrays.fill(passphraseChars, '\0'); + spec.clearPassword(); + + return key; + } + + private static byte[] AES_GCM(BigInteger A, BigInteger B, BigInteger p, BigInteger g) throws GeneralSecurityException, IOException{ + + // Build the concatenation: g as 1 byte, p/A/B as 256 bytes each + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + outputStream.write(toFixedLen(g, 1)); + outputStream.write(toFixedLen(p, 256)); + outputStream.write(toFixedLen(A, 256)); + outputStream.write(toFixedLen(B, 256)); + + byte[] concat = outputStream.toByteArray(); + + // SHA-256 over the concatenation + MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); + byte[] AAD = sha256.digest(concat); + + + return AAD; + } + + private static KeyPair generateRSAKeyPair() throws GeneralSecurityException { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(2048, new SecureRandom()); + return kpg.generateKeyPair(); + } + + private static String publicKeyToX509Base64(PublicKey pub) { + return Base64.getEncoder().encodeToString(pub.getEncoded()); } } \ No newline at end of file