temp push
This commit is contained in:
@@ -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:<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 {
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user