temp push

This commit is contained in:
Cédric Seron
2025-11-12 16:15:39 +01:00
parent c44f31a8ea
commit fa02d69063

View File

@@ -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;
@@ -68,6 +87,101 @@ 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:<base64(nonce||ciphertext)>
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 {
// Build the concatenation: g as 1 byte, p/A/B as 256 bytes each
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write( toFixedLen(g, 8) );
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());
}
}