http://www.bitpapers.com/2012/04/el-gamal-algorithm.html
The El Gamal Algorithm
El Gamal is a variant on Diffie-Hellman and derives its security from the same ideas. Although in some ways it is not as "neat" as the RSA algorithm, El Gamal is still very widely used—it is still the algorithm of choice for most keys used for encryption in Open PGP (RFC 2440).
In El Gamal, to send a
message to another
party whose public key is
Gy mod
P, you create a temporary public key,
Gx mod
P, encrypt the
message by multiplying it by
Gxy mod
P, the multiplication also being modulo
P, and send the temporary public key and the enciphered
message as a
single block. Although it works, as you will see, it does have the effect of making the ciphertext twice the size of the key.
Try It Out: El Gamal Encryption
Following is an example of random key generation and encryption using El Gamal. You will find it runs a lot slower than the equivalent RSA example. You will read about the reasons for this a bit later, but if you run it first, you will get a much better idea of what is meant by "a lot slower."
package chapter4; import java.security.Key; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.SecureRandom; import javax.crypto.Cipher; /** * El Gamal example with random key generation. */ public class RandomKeyElGamalExample { public static void main(String[] args) throws Exception { byte[] input = new byte[] { (byte)0xbe, (byte)0xef }; Cipher cipher = Cipher.getInstance( "ElGamal/None/NoPadding", "BC"); KeyPairGenerator generator = KeyPairGenerator.getInstance("ElGamal", "BC"); SecureRandom random = Utils.createFixedRandom(); // create the keys generator.initialize(256, random); KeyPair pair = generator.generateKeyPair(); Key pubKey = pair.getPublic(); Key privKey = pair.getPrivate(); System.out.println("input : " + Utils.toHex(input)); // encryption step cipher.init(Cipher.ENCRYPT_MODE, pubKey, random); byte[] cipherText = cipher.doFinal(input); System.out.println("cipher: " + Utils.toHex(cipherText)); // decryption step cipher.init(Cipher.DECRYPT_MODE, privKey); byte[] plainText = cipher.doFinal(cipherText); System.out.println("plain : " + Utils.toHex(plainText)); } }
Running the program produces the following:
input : beef cipher: 8c2e699772c14496bc82400d11decae4f662fe90864e8c553b78136679fcdfaa60c378b5 69083525c021fcf77e40f661525da56ed4133df92848aaba2459dff5 plain : beef
How It Works
Overall, there is not much to say here about the use of the algorithm itself. As far as using the public key for encryption, El Gamal is pretty much like RSA; because it is based on
math, you will have trouble with leading zeros if you do not use
padding. The big difference you will notice when comparing the output to that of the RSA example is that
the block of ciphertext produced is twice the size of the key—unlike RSA, where it is the same size as the key. Whether this is important to you really
depends on the constraints of your application, but the larger cipher block size is one of the reasons El Gamal is not favored.
The biggest problem, at least in this case, is the speed of the key generation. As you saw previously, the generation of an El Gamal key pair requires Diffie-Hellman parameters, and
calculating these values from scratch is very expensive. Internally, when initialized only with a key size, the
KeyPairGenerator has to first generate the
P and
G values before it can generate the key pair. You will find, at least in the case of the Bouncy Castle provider, that this is a one-off cost—generating successive key pairs is much quicker because the
P and
G values calculated for the first key pair can be reused. Not surprisingly, you will also find that the Diffie-Hellman key pair generator exhibits the same behavior.
Can you pre-generate a
DHParameterSpec so that you can pass in parameters like you did with Diffie-Hellman? As it turns out, you can; you just need to use an
AlgorithmParametersGenerator object to create them.
The AlgorithmParameterGenerator Class
Like other classes in the JCA,
java.security.AlgorithmParameterGenerator is created using the
getInstance() factory pattern, and further, in keeping with classes like
MessageDigest, it follows the same rules with regard to the precedence rules used if the Java runtime encounters more than one implementation for a given algorithm. The methods on
AlgorithmParameterGenerator that are of most of interest to you are the
init() methods, of which there are four, and the
generateParameters() method, which is used to retrieve the generated
AlgorithmParameters object.
AlgorithmParameter Generator.init()
The
init() method comes in two flavors: two that just take a size value with an optional source of randomness and two that take
AlgortihmParameterSpec objects for situations where it may be necessary to pass parameters other than the size to the generator. It
depends on what you are generating as to what suits best. For something like Diffie-Hellman/ElGamal, the size attribute is enough to generate the prime
P and the generator
G that will provide the basic parameters. Then again, you want to create a parameters object for Diffie-Hellman that takes advantage of the ability to
limit the size of the private value—in this case just the size will not be enough, and you will need an
AlgorithmParameterSpec object to pass the necessary information in.
Take a look at examples of both over
the next two sections.
AlgorithmParameter Generator.generate Parameters()
This returns the
AlgorithmParameters that you want to generate. I already covered the
AlgorithmParameters object in
Chapter 2, so there's no need to go into too much detail here, other than to mention, as you have probably guessed,
AlgorithmParameters is used everywhere.
Enough background though. You can now try using the class.
Try It Out: El Gamal Using AlgorithmParameterGenerator
Have a look at the following example and compare it with the
RandomKeyElGamalExample. Try running the example and then
read on.
package chapter4; import java.security.AlgorithmParameterGenerator; import java.security.AlgorithmParameters; import java.security.Key; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher; import javax.crypto.spec.DHParameterSpec; /** * El Gamal example with random key generation. */ public class AlgorithmParameterExample { public static void main(String[] args) throws Exception { byte[] input = new byte[] { (byte)0xbe, (byte)0xef }; Cipher cipher = Cipher.getInstance( "ElGamal/None/NoPadding", "BC"); SecureRandom random = Utils.createFixedRandom(); // create the parameters AlgorithmParameterGenerator apg = AlgorithmParameterGenerator.getInstance( "ElGamal", "BC"); apg.init(256, random); AlgorithmParameters params = apg.generateParameters(); AlgorithmParameterSpec dhSpec = params.getParameterSpec( DHParameterSpec.class); // create the keys KeyPairGenerator generator = KeyPairGenerator.getInstance("ElGamal", "BC"); generator.initialize(dhSpec, random); KeyPair pair = generator.generateKeyPair(); Key pubKey = pair.getPublic(); Key privKey = pair.getPrivate(); System.out.println("input : " + Utils.toHex(input)); // encryption step cipher.init(Cipher.ENCRYPT_MODE, pubKey, random); byte[] cipherText = cipher.doFinal(input); System.out.println("cipher: " + Utils.toHex(cipherText)); // decryption step cipher.init(Cipher.DECRYPT_MODE, privKey); byte[] plainText = cipher.doFinal(cipherText); System.out.println("plain : " + Utils.toHex(plainText)); } }
Because of the fixed seed used in the "random" number generator, you should see that this prints the same results as the original
RandomKeyElGamalExample. The reason is that the
process that takes place internally to the provider, at least in Bouncy Castle's case, is exactly the same as the
process you have made explicit in the code.
How It Works
In the new example, you are generating the parameters for the
P and
G values and then passing them to the key pair generator class explicitly, rather than letting it create them based on the key size. This relieves the key pair generator of the need to generate its own set of parameters.
Although it is probably hard to tell the difference in speed just from running the examples, if you add some timing code around the key pair generation in
RandomKeyElGamalExample and
AlgorithmParameterExample, you will see that the time spent in
KeyPairGenerator.generateKeyPair() is substantially less in the later case.
You read in earlier discussion that the
AlgorithmParameterGenerator class can also take an
AlgorithmParameterSpec object on its
init() methods. As it happens, there is an
AlgorithmParameterSpec class that is applicable to parameter generation for Diffie-Hellman type parameters—the
DHGenParameterSpec class.
The DHGenParameterSpec Class
As you read earlier, an optimization to Diffie-Hellman is to limit the size of the private value associated with a public key. As you also learned, you can construct a
DHParameterSpec that will limit the size of the private values it generates when used with a suitable
KeyPairGenerator object. It would be useful to be able to incorporate this information into our generated parameters as well, so the JCE provides a class that allows us to configure an
AlgorithmParameterGenerator object created for Diffie-Hellman algorithms—
javax.crypto.spec.DHGenParameterSpec. So rather than simply specifying a size for the prime
P, as you do on the line:
apg.init(256, random);
if you wanted to limit the private value to, say, 200 bits, you could have instead said
apg.init(new DHGenParameterSpec(256, 200), random);
where the arguments to the constructor of
DHGenParameterSpec are the size of the prime
P in bits and the maximum size in bits of the private value
Y. This would then produce
DHParameterSpec objects with the
DHParameterSpec.getL() method returning 200 thus limiting the private values to 200 bits.