To encrypt a message with ElGamal scheme in java code, I proceed as follow:
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("Elgamal/NOne/NoPadding", "BC");
KeyPaireGenerator generator = KeyPairGenerator.getInstance("ElGamal", "BC");
SecureRandom random = new SecureRandom();
generator.initialize(512, random);
KeyPair pair = generator.generateKeyPair();
String message = "myMessageToEncrypt";
cipher.init(Cipher.ENCRYPT_MODE, pair.getPublic(), random);
[]byte cipherText = cipher.doFinal(message);
I know from the ELGamal scheme that cipherText
byte array contains (c1, c2) and I need to access c1 as an BigInteger.
So my question is: how to make the conversion between the byte array and the tuple (c1, c2) ?
Thank you
The byte[]
with the ciphertext has twice the length of the key size, where the first half corresponds to c0
and the second half to c1
. The conversion of ci
is achievable e.g. with new BigInteger(1, ci)
.
Verification is easily possible by performing the decryption manually with the BigInteger
s converted in this way:
int keysizeBits = 512;
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// Key generation
KeyPairGenerator generator = KeyPairGenerator.getInstance("ElGamal", "BC");
SecureRandom random = new SecureRandom();
generator.initialize(keysizeBits, random);
KeyPair pair = generator.generateKeyPair();
BCElGamalPublicKey publicKey = (BCElGamalPublicKey)pair.getPublic();
BCElGamalPrivateKey privateKey = (BCElGamalPrivateKey)pair.getPrivate();
// Encryption
byte[] input = "abcdefgh".getBytes(StandardCharsets.UTF_8);
Cipher cipher = Cipher.getInstance("ElGamal/None/NoPadding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, publicKey, random);
byte[] ciphertext = cipher.doFinal(input);
System.out.println("Ciphertext: " + Hex.toHexString(ciphertext));//new String(cipherText));
// Decryption
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] plaintext = cipher.doFinal(ciphertext);
System.out.println("Plaintext : " + new String(plaintext, StandardCharsets.UTF_8));
// Manual decryption
// 1. Convert c0/c1 into BigInteger
byte[] c0 = new byte[keysizeBits/8];
byte[] c1 = new byte[keysizeBits/8];
System.arraycopy(ciphertext, 0, c0, 0, keysizeBits/8);
System.arraycopy(ciphertext, c0.length, c1, 0, keysizeBits/8);
System.out.println("c0 : " + Hex.toHexString(c0));
System.out.println("c1 : " + Hex.toHexString(c1));
BigInteger c0BI = new BigInteger(1, c0);
BigInteger c1BI = new BigInteger(1, c1);
// 2. Decrypt with c0BI^(-privBI) * c1BI
BigInteger privateKeyBI = privateKey.getX();
BigInteger pBI = privateKey.getParameters().getP();
BigInteger plaintextBI = c0BI.modPow(privateKeyBI.multiply(new BigInteger("-1")), pBI).multiply(c1BI).mod(pBI);
System.out.println("Plaintext : " + new String(plaintextBI.toByteArray(), StandardCharsets.UTF_8));
with e.g. the following output:
Ciphertext: adc32bbd23d80489db5843e26b26c58062a2369912915025574fd8598b8c72665e0a922ad8897719e1f9b0e3fb76e275ed15194534399781017e43c24a92cc77b13a256ff27e12667cc0f5876d1873368449b5a60ecc7a60a6b92f2640608f21dc86e7effe1dc4038b02b8c6c9d7ac03bd2e7d66d803d2a19f459ffeedfcff46
Plaintext : abcdefgh
c0 : adc32bbd23d80489db5843e26b26c58062a2369912915025574fd8598b8c72665e0a922ad8897719e1f9b0e3fb76e275ed15194534399781017e43c24a92cc77
c1 : b13a256ff27e12667cc0f5876d1873368449b5a60ecc7a60a6b92f2640608f21dc86e7effe1dc4038b02b8c6c9d7ac03bd2e7d66d803d2a19f459ffeedfcff46
Plaintext : abcdefgh
Note that a key size of 512 bits is too small nowadays, see e.g. here, and a missing padding is insecure, e.g. here.