66
77/**
88 * ElGamal Encryption Algorithm Implementation
9- *
9+ *
1010 * ElGamal is an asymmetric key encryption algorithm for public-key cryptography
1111 * based on the Discrete Logarithm Problem (DLP). It provides semantic security
1212 * through randomization in the encryption process.
13- *
13+ *
1414 * Key Components:
1515 * - p: Large prime number (modulus)
1616 * - g: Generator of the multiplicative group modulo p
1717 * - x: Private key (random integer)
1818 * - y: Public key where y = g^x mod p
19- *
19+ *
2020 * Encryption: For message m, choose random k and compute:
2121 * c1 = g^k mod p
2222 * c2 = m * y^k mod p
2323 * Ciphertext = (c1, c2)
24- *
24+ *
2525 * Decryption: Recover m using:
2626 * m = c2 * (c1^x)^-1 mod p
2727 * where (c1^x)^-1 is the modular multiplicative inverse
28- *
28+ *
2929 * @author TheAlgorithms
3030 */
3131public final class ElGamalCipher {
@@ -95,42 +95,42 @@ public String toString() {
9595
9696 /**
9797 * Generates ElGamal key pair with specified bit length
98- *
98+ *
9999 * Steps:
100100 * 1. Generate a large prime p
101101 * 2. Find a generator g of the multiplicative group mod p
102102 * 3. Choose random private key x in range [2, p-2]
103103 * 4. Compute public key y = g^x mod p
104- *
104+ *
105105 * @param bitLength The bit length for the prime (e.g., 512, 1024, 2048)
106106 * @return KeyPair containing (p, g, x, y)
107107 */
108108 public static KeyPair generateKeys (int bitLength ) {
109109 SecureRandom random = new SecureRandom ();
110-
110+
111111 // Generate a large prime p
112112 BigInteger p = BigInteger .probablePrime (bitLength , random );
113-
113+
114114 // Find a generator g (simplified: use a small generator that works for most primes)
115115 // In practice, we often use g = 2 or find a primitive root
116116 BigInteger g = findGenerator (p , random );
117-
117+
118118 // Generate private key x: random number in range [2, p-2]
119119 BigInteger x ;
120120 do {
121121 x = new BigInteger (bitLength - 1 , random );
122122 } while (x .compareTo (BigInteger .TWO ) < 0 || x .compareTo (p .subtract (BigInteger .TWO )) > 0 );
123-
123+
124124 // Calculate public key y = g^x mod p
125125 BigInteger y = g .modPow (x , p );
126-
126+
127127 return new KeyPair (p , g , x , y );
128128 }
129129
130130 /**
131131 * Finds a generator for the multiplicative group modulo p
132132 * Simplified approach: tries small values until finding a suitable generator
133- *
133+ *
134134 * @param p The prime modulus
135135 * @param random Random number generator
136136 * @return A generator g
@@ -139,7 +139,7 @@ private static BigInteger findGenerator(BigInteger p, Random random) {
139139 // Simplified: use 2 as generator (works for most safe primes)
140140 // For production, should verify g is a primitive root
141141 BigInteger g = BigInteger .valueOf (2 );
142-
142+
143143 // If 2 doesn't work, try other small values
144144 while (g .compareTo (p ) < 0 ) {
145145 // Check if g is a valid generator (simplified check)
@@ -148,22 +148,22 @@ private static BigInteger findGenerator(BigInteger p, Random random) {
148148 }
149149 g = g .add (BigInteger .ONE );
150150 }
151-
151+
152152 return BigInteger .valueOf (2 ); // Fallback
153153 }
154154
155155 /**
156156 * Encrypts a message using ElGamal encryption
157- *
157+ *
158158 * Process:
159159 * 1. Choose random k in range [2, p-2]
160160 * 2. Compute c1 = g^k mod p
161161 * 3. Compute c2 = m * y^k mod p
162162 * 4. Return ciphertext (c1, c2)
163- *
163+ *
164164 * The random k ensures semantic security - same message encrypted
165165 * multiple times produces different ciphertexts
166- *
166+ *
167167 * @param message The plaintext message as BigInteger (must be < p)
168168 * @param keyPair The key pair containing public parameters
169169 * @return Ciphertext (c1, c2)
@@ -172,40 +172,40 @@ public static Ciphertext encrypt(BigInteger message, KeyPair keyPair) {
172172 if (message .compareTo (keyPair .getP ()) >= 0 ) {
173173 throw new IllegalArgumentException ("Message must be less than modulus p" );
174174 }
175-
175+
176176 SecureRandom random = new SecureRandom ();
177177 BigInteger p = keyPair .getP ();
178178 BigInteger g = keyPair .getG ();
179179 BigInteger y = keyPair .getPublicKey ();
180-
180+
181181 // Choose random k in range [2, p-2]
182182 BigInteger k ;
183183 do {
184184 k = new BigInteger (p .bitLength () - 1 , random );
185185 } while (k .compareTo (BigInteger .TWO ) < 0 || k .compareTo (p .subtract (BigInteger .TWO )) > 0 );
186-
186+
187187 // Compute c1 = g^k mod p
188188 BigInteger c1 = g .modPow (k , p );
189-
189+
190190 // Compute c2 = m * y^k mod p
191191 BigInteger c2 = message .multiply (y .modPow (k , p )).mod (p );
192-
192+
193193 return new Ciphertext (c1 , c2 );
194194 }
195195
196196 /**
197197 * Decrypts a ciphertext using ElGamal decryption
198- *
198+ *
199199 * Process:
200200 * 1. Compute s = c1^x mod p (shared secret)
201201 * 2. Compute s^-1 (modular multiplicative inverse of s)
202202 * 3. Recover m = c2 * s^-1 mod p
203- *
203+ *
204204 * Mathematical proof:
205205 * c2 * (c1^x)^-1 = (m * y^k) * (g^(k*x))^-1
206206 * = (m * g^(k*x)) * (g^(k*x))^-1
207207 * = m
208- *
208+ *
209209 * @param ciphertext The ciphertext (c1, c2)
210210 * @param keyPair The key pair containing private key
211211 * @return Decrypted plaintext message
@@ -215,22 +215,22 @@ public static BigInteger decrypt(Ciphertext ciphertext, KeyPair keyPair) {
215215 BigInteger c2 = ciphertext .getC2 ();
216216 BigInteger x = keyPair .getPrivateKey ();
217217 BigInteger p = keyPair .getP ();
218-
218+
219219 // Compute s = c1^x mod p
220220 BigInteger s = c1 .modPow (x , p );
221-
221+
222222 // Compute s^-1 mod p (modular multiplicative inverse)
223223 BigInteger sInverse = s .modInverse (p );
224-
224+
225225 // Recover message: m = c2 * s^-1 mod p
226226 BigInteger message = c2 .multiply (sInverse ).mod (p );
227-
227+
228228 return message ;
229229 }
230230
231231 /**
232232 * Converts a string to BigInteger for encryption
233- *
233+ *
234234 * @param text The input string
235235 * @return BigInteger representation
236236 */
@@ -241,7 +241,7 @@ public static BigInteger stringToBigInteger(String text) {
241241
242242 /**
243243 * Converts BigInteger back to string after decryption
244- *
244+ *
245245 * @param number The BigInteger to convert
246246 * @return Original string
247247 */
@@ -261,65 +261,65 @@ public static String bigIntegerToString(BigInteger number) {
261261 */
262262 public static void main (String [] args ) {
263263 System .out .println ("=== ElGamal Encryption Algorithm Demo ===\n " );
264-
264+
265265 // Example 1: Encrypting a small integer
266266 System .out .println ("Example 1: Encrypting a small integer" );
267267 System .out .println ("--------------------------------------" );
268-
268+
269269 // Generate keys with 512-bit prime (use 1024 or 2048 for production)
270270 System .out .println ("Generating keys (512-bit)..." );
271271 KeyPair keyPair = generateKeys (512 );
272-
272+
273273 System .out .println ("Prime (p): " + keyPair .getP ());
274274 System .out .println ("Generator (g): " + keyPair .getG ());
275275 System .out .println ("Private key (x): " + keyPair .getPrivateKey ());
276276 System .out .println ("Public key (y): " + keyPair .getPublicKey ());
277-
277+
278278 // Message to encrypt
279279 BigInteger message = BigInteger .valueOf (12345 );
280280 System .out .println ("\n Original message: " + message );
281-
281+
282282 // Encrypt
283283 Ciphertext ciphertext = encrypt (message , keyPair );
284284 System .out .println ("Encrypted: " + ciphertext );
285-
285+
286286 // Decrypt
287287 BigInteger decrypted = decrypt (ciphertext , keyPair );
288288 System .out .println ("Decrypted message: " + decrypted );
289-
289+
290290 // Verify
291291 System .out .println ("Decryption successful: " + message .equals (decrypted ));
292-
292+
293293 // Example 2: Demonstrating semantic security
294294 System .out .println ("\n \n Example 2: Demonstrating Semantic Security" );
295295 System .out .println ("------------------------------------------" );
296296 System .out .println ("Same message encrypted twice produces different ciphertexts:" );
297-
297+
298298 Ciphertext ct1 = encrypt (message , keyPair );
299299 Ciphertext ct2 = encrypt (message , keyPair );
300-
300+
301301 System .out .println ("Encryption 1: " + ct1 );
302302 System .out .println ("Encryption 2: " + ct2 );
303303 System .out .println ("Are ciphertexts different? " + !ct1 .getC1 ().equals (ct2 .getC1 ()));
304-
304+
305305 // Both decrypt to same message
306306 System .out .println ("Both decrypt to: " + decrypt (ct1 , keyPair ) + " and " + decrypt (ct2 , keyPair ));
307-
307+
308308 // Example 3: String encryption
309309 System .out .println ("\n \n Example 3: Encrypting a String" );
310310 System .out .println ("-------------------------------" );
311-
311+
312312 String text = "Hello" ;
313313 System .out .println ("Original text: " + text );
314-
314+
315315 BigInteger textAsNumber = stringToBigInteger (text );
316316 System .out .println ("Text as BigInteger: " + textAsNumber );
317-
317+
318318 // Check if message is small enough
319319 if (textAsNumber .compareTo (keyPair .getP ()) < 0 ) {
320320 Ciphertext textCiphertext = encrypt (textAsNumber , keyPair );
321321 System .out .println ("Encrypted: " + textCiphertext );
322-
322+
323323 BigInteger decryptedNumber = decrypt (textCiphertext , keyPair );
324324 String decryptedText = bigIntegerToString (decryptedNumber );
325325 System .out .println ("Decrypted text: " + decryptedText );
0 commit comments