44import java .security .SecureRandom ;
55
66/**
7- * ElGamal Encryption Algorithm Implementation
7+ * Simple ElGamal implementation for educational purposes.
88 *
9- * ElGamal is an asymmetric key encryption algorithm for public-key cryptography
10- * based on the Discrete Logarithm Problem (DLP). It provides semantic security
11- * through randomization in the encryption process.
9+ * <p>Note: This implementation is intended for educational / illustrative use in
10+ * algorithm libraries. Real-world cryptographic usage requires careful review,
11+ * authenticated encryption, parameter validation, constant-time implementations,
12+ * and using well-reviewed libraries.
1213 *
13- * Key Components:
14- * - p: Large prime number (modulus)
15- * - g: Generator of the multiplicative group modulo p
16- * - x: Private key (random integer)
17- * - y: Public key where y = g^x mod p
14+ * <p>References:
15+ * https://en.wikipedia.org/wiki/ElGamal_encryption
1816 *
19- * Encryption: For message m, choose random k and compute:
20- * c1 = g^k mod p
21- * c2 = m * y^k mod p
22- * Ciphertext = (c1, c2)
23- *
24- * Decryption: Recover m using:
25- * m = c2 * (c1^x)^-1 mod p
26- * where (c1^x)^-1 is the modular multiplicative inverse
27- *
28- * Wikipedia: https://en.wikipedia.org/wiki/ElGamal_encryption
29- *
30- * @author TheAlgorithms
17+ * Author: TheAlgorithms
3118 */
3219public final class ElGamalCipher {
3320
21+ private static final SecureRandom RANDOM = new SecureRandom ();
22+
3423 private ElGamalCipher () {
35- // Utility class - prevent instantiation
24+ // Utility class
3625 }
3726
38- /**
39- * Represents an ElGamal key pair containing public and private keys
40- */
41- public static class KeyPair {
42- private final BigInteger p ;
43- private final BigInteger g ;
44- private final BigInteger x ;
45- private final BigInteger y ;
27+ /** Public key container. */
28+ public static final class PublicKey {
29+ public final BigInteger p ; // prime modulus
30+ public final BigInteger g ; // generator
31+ public final BigInteger h ; // h = g^x mod p
4632
47- public KeyPair ( BigInteger p , BigInteger g , BigInteger x , BigInteger y ) {
33+ public PublicKey ( final BigInteger p , final BigInteger g , final BigInteger h ) {
4834 this .p = p ;
4935 this .g = g ;
50- this .x = x ;
51- this .y = y ;
52- }
53-
54- public BigInteger getP () {
55- return p ;
56- }
57-
58- public BigInteger getG () {
59- return g ;
36+ this .h = h ;
6037 }
38+ }
6139
62- public BigInteger getPrivateKey () {
63- return x ;
64- }
40+ /** Private key container. */
41+ public static final class PrivateKey {
42+ public final BigInteger p ;
43+ public final BigInteger x ; // secret exponent
6544
66- public BigInteger getPublicKey () {
67- return y ;
45+ public PrivateKey (final BigInteger p , final BigInteger x ) {
46+ this .p = p ;
47+ this .x = x ;
6848 }
6949 }
7050
71- /**
72- * Represents an ElGamal ciphertext as a pair (c1, c2)
73- */
74- public static class Ciphertext {
75- private final BigInteger c1 ;
76- private final BigInteger c2 ;
51+ /** Ciphertext pair (c1, c2). */
52+ public static final class CipherText {
53+ public final BigInteger c1 ;
54+ public final BigInteger c2 ;
7755
78- public Ciphertext ( BigInteger c1 , BigInteger c2 ) {
56+ public CipherText ( final BigInteger c1 , final BigInteger c2 ) {
7957 this .c1 = c1 ;
8058 this .c2 = c2 ;
8159 }
82-
83- public BigInteger getC1 () {
84- return c1 ;
85- }
86-
87- public BigInteger getC2 () {
88- return c2 ;
89- }
90-
91- @ Override
92- public String toString () {
93- return "Ciphertext{c1=" + c1 + ", c2=" + c2 + "}" ;
94- }
9560 }
9661
9762 /**
98- * Generates ElGamal key pair with specified bit length
63+ * Generates an ElGamal keypair.
9964 *
100- * Steps:
101- * 1. Generate a large prime p
102- * 2. Find a generator g of the multiplicative group mod p
103- * 3. Choose random private key x in range [2, p-2]
104- * 4. Compute public key y = g^x mod p
105- *
106- * @param bitLength The bit length for the prime (e.g., 512, 1024, 2048)
107- * @return KeyPair containing (p, g, x, y)
65+ * @param bitLength size of prime modulus (e.g., 2048)
66+ * @return an array where [0]=PublicKey and [1]=PrivateKey
10867 */
109- public static KeyPair generateKeys (int bitLength ) {
110- SecureRandom random = new SecureRandom ();
111-
112- BigInteger p = BigInteger .probablePrime (bitLength , random );
113- BigInteger g = findGenerator (p );
114-
115- BigInteger x ;
116- do {
117- x = new BigInteger (bitLength - 1 , random );
118- } while (x .compareTo (BigInteger .TWO ) < 0 || x .compareTo (p .subtract (BigInteger .TWO )) > 0 );
119-
120- BigInteger y = g .modPow (x , p );
121-
122- return new KeyPair (p , g , x , y );
68+ public static Object [] generateKeyPair (final int bitLength ) {
69+ final BigInteger p = BigInteger .probablePrime (bitLength , RANDOM );
70+ // find a generator g in [2, p-2] (this simple approach picks a candidate; for
71+ // production use proper safe-prime/generator selection)
72+ final BigInteger g = BigInteger .valueOf (2 );
73+ final BigInteger x = new BigInteger (bitLength - 2 , RANDOM ).mod (p .subtract (BigInteger .TWO )).add (BigInteger .ONE );
74+ final BigInteger h = g .modPow (x , p );
75+ final PublicKey pub = new PublicKey (p , g , h );
76+ final PrivateKey priv = new PrivateKey (p , x );
77+ return new Object [] {pub , priv };
12378 }
12479
12580 /**
126- * Finds a generator for the multiplicative group modulo p
81+ * Encrypts a message m (0 < m < p).
12782 *
128- * @param p The prime modulus
129- * @return A generator g
83+ * @param m message as BigInteger (must be less than p)
84+ * @param pub the public key
85+ * @return ciphertext pair
13086 */
131- private static BigInteger findGenerator (BigInteger p ) {
132- BigInteger g = BigInteger .valueOf (2 );
133-
134- while (g .compareTo (p ) < 0 ) {
135- if (g .modPow (p .subtract (BigInteger .ONE ), p ).equals (BigInteger .ONE )) {
136- return g ;
137- }
138- g = g .add (BigInteger .ONE );
139- }
140-
141- return BigInteger .valueOf (2 );
142- }
143-
144- /**
145- * Encrypts a message using ElGamal encryption
146- *
147- * Process:
148- * 1. Choose random k in range [2, p-2]
149- * 2. Compute c1 = g^k mod p
150- * 3. Compute c2 = m * y^k mod p
151- * 4. Return ciphertext (c1, c2)
152- *
153- * @param message The plaintext message as BigInteger (must be < p)
154- * @param keyPair The key pair containing public parameters
155- * @return Ciphertext (c1, c2)
156- */
157- public static Ciphertext encrypt (BigInteger message , KeyPair keyPair ) {
158- if (message .compareTo (keyPair .getP ()) >= 0 ) {
159- throw new IllegalArgumentException ("Message must be less than modulus p" );
87+ public static CipherText encrypt (final BigInteger m , final PublicKey pub ) {
88+ if (m .compareTo (BigInteger .ZERO ) <= 0 || m .compareTo (pub .p ) >= 0 ) {
89+ throw new IllegalArgumentException ("Message out of range" );
16090 }
161-
162- SecureRandom random = new SecureRandom ();
163- BigInteger p = keyPair .getP ();
164- BigInteger g = keyPair .getG ();
165- BigInteger y = keyPair .getPublicKey ();
166-
167- BigInteger k ;
168- do {
169- k = new BigInteger (p .bitLength () - 1 , random );
170- } while (k .compareTo (BigInteger .TWO ) < 0 || k .compareTo (p .subtract (BigInteger .TWO )) > 0 );
171-
172- BigInteger c1 = g .modPow (k , p );
173- BigInteger c2 = message .multiply (y .modPow (k , p )).mod (p );
174-
175- return new Ciphertext (c1 , c2 );
91+ final BigInteger y = new BigInteger (pub .p .bitLength () - 1 , RANDOM ).mod (pub .p .subtract (BigInteger .TWO )).add (BigInteger .ONE );
92+ final BigInteger c1 = pub .g .modPow (y , pub .p );
93+ final BigInteger s = pub .h .modPow (y , pub .p ); // shared secret
94+ final BigInteger c2 = s .multiply (m ).mod (pub .p );
95+ return new CipherText (c1 , c2 );
17696 }
17797
17898 /**
179- * Decrypts a ciphertext using ElGamal decryption
180- *
181- * Process:
182- * 1. Compute s = c1^x mod p (shared secret)
183- * 2. Compute s^-1 (modular multiplicative inverse of s)
184- * 3. Recover m = c2 * s^-1 mod p
99+ * Decrypts a ciphertext using the private key.
185100 *
186- * @param ciphertext The ciphertext (c1, c2)
187- * @param keyPair The key pair containing private key
188- * @return Decrypted plaintext message
101+ * @param ct ciphertext
102+ * @param priv private key
103+ * @return decrypted message as BigInteger
189104 */
190- public static BigInteger decrypt (Ciphertext ciphertext , KeyPair keyPair ) {
191- BigInteger c1 = ciphertext .getC1 ();
192- BigInteger c2 = ciphertext .getC2 ();
193- BigInteger x = keyPair .getPrivateKey ();
194- BigInteger p = keyPair .getP ();
195-
196- BigInteger s = c1 .modPow (x , p );
197- BigInteger sInverse = s .modInverse (p );
198- BigInteger message = c2 .multiply (sInverse ).mod (p );
199-
200- return message ;
201- }
202-
203- /**
204- * Converts a string to BigInteger for encryption
205- *
206- * @param text The input string
207- * @return BigInteger representation
208- */
209- public static BigInteger stringToBigInteger (String text ) {
210- byte [] bytes = text .getBytes ();
211- return new BigInteger (1 , bytes );
212- }
213-
214- /**
215- * Converts BigInteger back to string after decryption
216- *
217- * @param number The BigInteger to convert
218- * @return Original string
219- */
220- public static String bigIntegerToString (BigInteger number ) {
221- byte [] bytes = number .toByteArray ();
222- if (bytes [0 ] == 0 && bytes .length > 1 ) {
223- byte [] tmp = new byte [bytes .length - 1 ];
224- System .arraycopy (bytes , 1 , tmp , 0 , tmp .length );
225- bytes = tmp ;
226- }
227- return new String (bytes );
105+ public static BigInteger decrypt (final CipherText ct , final PrivateKey priv ) {
106+ final BigInteger s = ct .c1 .modPow (priv .x , priv .p );
107+ final BigInteger sInv = s .modInverse (priv .p );
108+ return ct .c2 .multiply (sInv ).mod (priv .p );
228109 }
229- }
110+ }
0 commit comments