How to Make Encryption (c1, c2) Tuple Explicit using Bouncy Castle ElGamal and javax.crypto.Cipher

237 views Asked by At

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

1

There are 1 answers

2
Topaco On BEST ANSWER

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 BigIntegers 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.