2525 * m = c2 * (c1^x)^-1 mod p
2626 * where (c1^x)^-1 is the modular multiplicative inverse
2727 *
28+ * Wikipedia: https://en.wikipedia.org/wiki/ElGamal_encryption
29+ *
2830 * @author TheAlgorithms
2931 */
3032public final class ElGamalCipher {
@@ -37,10 +39,10 @@ private ElGamalCipher() {
3739 * Represents an ElGamal key pair containing public and private keys
3840 */
3941 public static class KeyPair {
40- private final BigInteger p ; // Prime modulus
41- private final BigInteger g ; // Generator
42- private final BigInteger x ; // Private key
43- private final BigInteger y ; // Public key (y = g^x mod p)
42+ private final BigInteger p ;
43+ private final BigInteger g ;
44+ private final BigInteger x ;
45+ private final BigInteger y ;
4446
4547 public KeyPair (BigInteger p , BigInteger g , BigInteger x , BigInteger y ) {
4648 this .p = p ;
@@ -107,47 +109,36 @@ public String toString() {
107109 public static KeyPair generateKeys (int bitLength ) {
108110 SecureRandom random = new SecureRandom ();
109111
110- // Generate a large prime p
111112 BigInteger p = BigInteger .probablePrime (bitLength , random );
112-
113- // Find a generator g (simplified: use a small generator that works for most primes)
114- // In practice, we often use g = 2 or find a primitive root
115113 BigInteger g = findGenerator (p );
116114
117- // Generate private key x: random number in range [2, p-2]
118115 BigInteger x ;
119116 do {
120117 x = new BigInteger (bitLength - 1 , random );
121118 } while (x .compareTo (BigInteger .TWO ) < 0 || x .compareTo (p .subtract (BigInteger .TWO )) > 0 );
122119
123- // Calculate public key y = g^x mod p
124120 BigInteger y = g .modPow (x , p );
125121
126122 return new KeyPair (p , g , x , y );
127123 }
128124
129125 /**
130126 * Finds a generator for the multiplicative group modulo p
131- * Simplified approach: tries small values until finding a suitable generator
132127 *
133128 * @param p The prime modulus
134129 * @return A generator g
135130 */
136131 private static BigInteger findGenerator (BigInteger p ) {
137- // Simplified: use 2 as generator (works for most safe primes)
138- // For production, should verify g is a primitive root
139132 BigInteger g = BigInteger .valueOf (2 );
140133
141- // If 2 doesn't work, try other small values
142134 while (g .compareTo (p ) < 0 ) {
143- // Check if g is a valid generator (simplified check)
144135 if (g .modPow (p .subtract (BigInteger .ONE ), p ).equals (BigInteger .ONE )) {
145136 return g ;
146137 }
147138 g = g .add (BigInteger .ONE );
148139 }
149140
150- return BigInteger .valueOf (2 ); // Fallback
141+ return BigInteger .valueOf (2 );
151142 }
152143
153144 /**
@@ -159,9 +150,6 @@ private static BigInteger findGenerator(BigInteger p) {
159150 * 3. Compute c2 = m * y^k mod p
160151 * 4. Return ciphertext (c1, c2)
161152 *
162- * The random k ensures semantic security - same message encrypted
163- * multiple times produces different ciphertexts
164- *
165153 * @param message The plaintext message as BigInteger (must be < p)
166154 * @param keyPair The key pair containing public parameters
167155 * @return Ciphertext (c1, c2)
@@ -176,16 +164,12 @@ public static Ciphertext encrypt(BigInteger message, KeyPair keyPair) {
176164 BigInteger g = keyPair .getG ();
177165 BigInteger y = keyPair .getPublicKey ();
178166
179- // Choose random k in range [2, p-2]
180167 BigInteger k ;
181168 do {
182169 k = new BigInteger (p .bitLength () - 1 , random );
183170 } while (k .compareTo (BigInteger .TWO ) < 0 || k .compareTo (p .subtract (BigInteger .TWO )) > 0 );
184171
185- // Compute c1 = g^k mod p
186172 BigInteger c1 = g .modPow (k , p );
187-
188- // Compute c2 = m * y^k mod p
189173 BigInteger c2 = message .multiply (y .modPow (k , p )).mod (p );
190174
191175 return new Ciphertext (c1 , c2 );
@@ -199,11 +183,6 @@ public static Ciphertext encrypt(BigInteger message, KeyPair keyPair) {
199183 * 2. Compute s^-1 (modular multiplicative inverse of s)
200184 * 3. Recover m = c2 * s^-1 mod p
201185 *
202- * Mathematical proof:
203- * c2 * (c1^x)^-1 = (m * y^k) * (g^(k*x))^-1
204- * = (m * g^(k*x)) * (g^(k*x))^-1
205- * = m
206- *
207186 * @param ciphertext The ciphertext (c1, c2)
208187 * @param keyPair The key pair containing private key
209188 * @return Decrypted plaintext message
@@ -214,13 +193,8 @@ public static BigInteger decrypt(Ciphertext ciphertext, KeyPair keyPair) {
214193 BigInteger x = keyPair .getPrivateKey ();
215194 BigInteger p = keyPair .getP ();
216195
217- // Compute s = c1^x mod p
218196 BigInteger s = c1 .modPow (x , p );
219-
220- // Compute s^-1 mod p (modular multiplicative inverse)
221197 BigInteger sInverse = s .modInverse (p );
222-
223- // Recover message: m = c2 * s^-1 mod p
224198 BigInteger message = c2 .multiply (sInverse ).mod (p );
225199
226200 return message ;
@@ -245,7 +219,6 @@ public static BigInteger stringToBigInteger(String text) {
245219 */
246220 public static String bigIntegerToString (BigInteger number ) {
247221 byte [] bytes = number .toByteArray ();
248- // Handle sign byte if present
249222 if (bytes [0 ] == 0 && bytes .length > 1 ) {
250223 byte [] tmp = new byte [bytes .length - 1 ];
251224 System .arraycopy (bytes , 1 , tmp , 0 , tmp .length );
0 commit comments