Sei sulla pagina 1di 20

An implementation of RSA and ElGamal PKCs using Java BigInteger class

--------------------------------------------------------------------A useful & interesting resource is David Bishop, Introduction to Cryptography


with Java Applets. Some of the following is based on ideas in this book, and
these notes should be read in conjunction with it. Some comments in the source
code refer to it.
The RSA implementation has six classes:
-------------------------------------- MyRSA.class implements the RSA PKC
- StrongPrime.class is a utility class to generate "strong primes" -- primes p
such that p+1 has a large prime factor, and p-1 has a large prime factor, r,
such that r-1 also has a large prime factor. Products of these primes are
resistant to some factorising algorithms.
- MyTransformer.class with static utility methods suggested by Bishop, for
applying the PKC to data files
- MyRSAEncrypter and MyRSADecrypter (subclasses of MyTransformer)
encrypt/decrypt using a MyRSA object.
- MyRSATst.class is an application driver for encypting/decrypting a file.
MyRSA.class
----------- implements the RSA PKC. There are 4 constructors:
(1) Given an integer key size 1<=k<=4000, generate random prime BigIntegers p, q
with k bits; let n = pq and generate random BigInteger d such that p,q < d < n
and relatively prime to (p-1)(q-1). Let e = inverse of d mod (p-1)(q-1). Save p,
q, d in MyRsaConfig.txt, in hexadecimal format.
(2) Given two prime BigIntegers set p, q = these (check their primality) and
generate d, e as in (1). Save p, q, d in MyRsaConfig.txt in hex format.
(3)Given p, q, d, check primality of p, q; check p, q < d < pq and GCD(d, (p-1)
(q-1)) = 1 and if ok, generate e = inverse of d mod (p-1)(q-1). Save p, q, d in
MyRsaConfig.txt.
(4) Default constructor: read p, q, d from MyRsaConfig.txt and compute e =
inverse of d mod (p-1)(q-1).
The class has a main method which can be used to generate RSAs:
- java MyRSA <integer>
- invokes constructor (1) (which saves p, q, d in the config file).
- generates a "random" RSA system of the required bit-size.
- java MyRSA <path>
- opens a text file and attepts to read two BigIntegers in from it, and invoke
constructor (2). p, q, d are saved in the config file.
- Use this to generate a system from a file containing two "strong primes"
(see below).
- java MyRSA invokes constructor (4) - just reads p, q, d from the config file,
and displays the details.
The class is also an encrypter/decrypter factory: there are methods to return a
MyRSAEncrypter object, an MyRSADecrypter object.

StrongPrimes.class
------------------ uses Gordon's Algorithm to generate strong primes.
The main method takes an integer as a run-time argument and generates a strong
prime with at least this many bits.
- java StrongPrimes <nBits> >> <file path>
- Do this multiple times to accumulate some strong primes in <file path>,
separated by new line characters. They are in hexadecimal format.
MyTransformer.class
------------------A base class with methods for blocking/unblocking/padding/unpadding an array
of bytes. An array of bytes needs to be divided into blocks of suitable (and
uniform) size. Each block is interpreted as a BigInteger and transformed using
the encryption or decryption transformation. The result is converted back into
blocks of bytes which are reglued, minus the padding.
The cryptographic transformation appears as an abstract method.
MyRSAEncrypter.class
-------------------Subclass of MyTransformer; implements the encryption transformation.
MyRSADecrypter.class
-------------------Subclass of MyTransformer; implements the decryption transformation.
MyRSATst.class
-------------- Driver for encryption / decryption of files. File is assumed to
be of modest size, small enough to be processed as below entirely in memory.
The main method - constructs a MyRSA object (by default -- using values in MyRSAConfig.txt)
- depending on run-time option calls the MyRSA object's factory method to obtain
an encrypter or a decrypter
- reads input file into an array of bytes
- transforms array using the encrypter/decrypter
- writes the transformed array to a file
To encrypt:
java MyRSATst e <file path>
To decrypt:
java MyRSATst d <file path>
The output appears (eventually) in <file path>.out.
RSA Summary
----------Use java StrongPrimes <int> >> <file> to generate strong primes.
Use java MyRSA <int> to generate a "random" RSA system, or
java MyRSA <file of primes> to generate an RSA system using your own file of
prime BigIntegers (eg strong primes). In both cases, the system is saved in
MyRSAConfig.txt.
Use java MyRSATst [e|d] <file path> to encrypt/decrypt a file using the system
saved in MyRSAConfig.txt. The output file is <file path>.out.

The ElGamal implementation has 7 classes:


---------------------------------------- MyElGamal.class implements the ElGamal PKC
- GeneratorFactory.class is a utility class to generate (probable) primes p and
generators g mod p. -- so that g^k % p for 0 <= k < p-1 run through all the
nonzero integers mod p. (See Bishop's notes on Fermat Little Theorem &
generators).
- GenTest.class is a simple test driver for the isGenerator() method of the
GeneratorFactory. It has been left in so that one may explore pairs (p,g).
- MyTransformer.class is the same as that used with RSA: static utility methods
suggested by Bishop, for applying the PKC to data files
- MyElGamalEncrypter and MyElGamalDecrypter (subclasses of MyTransformer)
encrypt/decrypt using a MyElGamal object.
- MyElGamalTst.class is an application driver for encypting/decrypting a file.
MyElGamal.class
----------- implements the ElGamal PKC. There are 2 constructors:
(1) Given an integer key size k, generate a random prime BigInteger p with k
bits and generator g mod p. Let a be a random BigInteger, 1 < a < p-1, and let r
= g^a % p. Save p, g, a in MyElGamalConfig.txt, in hexadecimal format.
Initialise secure random number generator for use in encryption.
(2) Default constructor: read p, g, a from config file and compute r = g^a % p.
Initialise secure random number generator for use in encryption.
The class has (1) a simple encrypt method which takes a BigInteger m, generates
a random k, 0 < k < p-1, and returns c0 = g^k % p and c1 = m * r^k mod p; and
(2) a companion decrypt method with recovers c1 * c0^-a mod p = m.
The class has also an encrypter/decrypter factory: there are methods to return a
MyElGamalEncrypter object, an MyElGamalDecrypter object for working with data
files.
The class has a main method which can be used to generate ElGamal systems:
- java ElGamal <integer>
- invokes constructor (1) (which saves p, g, a in the config file).
- generates a "random" RSA system of the required bit-size.
- java MyElGamal (with no runtime args) tests the saved system with a random
BigInteger as "message".
- uses the simple encrypt, decrypt methods.
GeneratorFactory.class
---------------------- has a method makeSafePrimeAndGenerator()which makes a
safe prime BigInteger (of specified bit length) p and a generator mod p.
[Fermat little theorem says that if p is prime and not
1) = 1 mod p; g is a generator mod p iff this is false
smaller than p-1: for all 0 < k < p-1, g^k <> 1 mod p.
p generate all the integers 0 < n < p. This helps make
underlying ElGamal hard. See notes in Bishop.]

a factor of g, then g^(pfor all nonzero exponents


It follows that the g^k %
the discrete log problem

MyTransformer.class
------------------This is the same as for RSA: a base class with methods for
blocking/unblocking/padding/unpadding an array of bytes.with an abstract method
for transforming. Again, the encrypter and decrypter are subclasses.
MyElGamalEncrypter.class
-----------------------Subclass of MyTransformer; implements the encryption transformation.

MyElGamalDecrypter.class
-----------------------Subclass of MyTransformer; implements the decryption transformation.
MyElGamalTst.class
------------------ Driver for encryption / decryption of files -- adapted from
RSA version.
The main method - constructs a MyElGamal object (by default -- using values in
MyElGamalConfig.txt)
- depending on run-time [e|d] option calls the MyElGamal object's factory method
to obtain an encrypter or a decrypter
- reads input file into an array of bytes
- transforms array using the encrypter/decrypter
- writes the transformed array to a file
To encrypt:
java MyElGamalTst e <file path>
To decrypt:
java MyElGamalTst d <file path>
The output appears (eventually) in <file path>.out.
ElGamal Summary
--------------Use java MyElGamal <int> to generate a "random" RSA system and save it. Run
without a runtime arg to test the saved config on a random BigInteger.
Use java GenTest <p> <g> to experiment with primes and generators
Use java MyElGamalTst [e|d] <file path> to encrypt/decrypt a file using the
system saved in MyElGamalConfig.txt. The output file is <file path>.out.

import java.math.BigInteger;
import java.io.*;
//Report whether two files are the same
public class Compare {
public static void main(String[] args) throws IOException {
if (args.length < 2) {
System.out.println("Usage: java Compare <path1> <path2>");
return;
}
File f0 = new File(args[0]), f1 = new File(args[1]);
BufferedInputStream s0 = new BufferedInputStream(new FileInputStream(f0)),
s1 = new BufferedInputStream(new FileInputStream(f1));
int ckSum = 0;
long btCt = 0L;
int bt0 = s0.read(), bt1 = s1.read();
while (bt0 >=0 && bt1 >= 0) {
btCt++;
ckSum += (bt0^bt1);
bt0 = s0.read(); bt1 = s1.read();
}
System.out.printf("%d bytes read; checksum = %d\n", btCt,ckSum);
s0.close();
s1.close();
}
}
--------------------------------------------------------RSA Sources
----------/*
* To improve the security of an RSA system, n = pq should be difficult to
* factorise. This is served by making p, q strong primes.
* A strong prime is a prime p such that p+1 has a large prime factor s and
* p-1 has a large prime factor r such that r-1 also has a large prime
* factor t.
* The following implements Gordon's Algorithm. It produces such primes,
* with "large" meaning having at least kSz/2-8 bits.
* The prime produced have kSz or slightly more bits' length.
* Ref: D Bishop, Intro to Cryptography with Java Applets.
*/
import java.math.BigInteger;
import java.security.SecureRandom;
public class StrongPrimes {
//Uses probablilistic primality test, so we must provide a
//certainty level.
public static BigInteger getStrongPrime(
int kSz, int crtty, SecureRandom rng) {
final BigInteger ONE = BigInteger.ONE, TWO = ONE.add(ONE);
if (kSz < 512) {
System.err.println("Too few bits for strong prime");
return null;
}
BigInteger s = new BigInteger(kSz/2-8, crtty, rng);
BigInteger t = new BigInteger(kSz/2-8, crtty, rng);
BigInteger i = BigInteger.valueOf(1); //numerically = ONE, but will vary

BigInteger r;
do {
r = TWO.multiply(i).multiply(t).add(ONE);
i = i.add(ONE);
} while (!r.isProbablePrime(crtty));

BigInteger z = s.modPow(r.subtract(TWO), r);


BigInteger ps = TWO.multiply(z).multiply(s).subtract(ONE);
BigInteger k = BigInteger.valueOf(1);
BigInteger p = TWO.multiply(r).multiply(s).add(ps);
while (p.bitLength() <= kSz) {
k = k.multiply(TWO);
p = TWO.multiply(k).multiply(r).multiply(s).add(ps);
}
while (!p.isProbablePrime(crtty)) {
k = k.add(ONE);
p = TWO.multiply(k).multiply(r).multiply(s).add(ps);
}
return p;

public static void main(String[] args) {


int kSz = Integer.parseInt(args[0]);
System.err.println("Finding a strong prime...");
long startTm = System.currentTimeMillis();
BigInteger sp = getStrongPrime(kSz, 300, new SecureRandom());
if (sp != null) {
System.out.println(sp.toString(16));
System.err.println("... " + sp.bitLength() + " bits; took "
+ (System.currentTimeMillis() - startTm) + " ms");
}
}
}
--------------------------------------------------------import java.math.BigInteger;
import java.security.SecureRandom;
import java.io.*;
public class MyRSA {
private BigInteger p, q, n, d, e;
private static final int CRTTY = 300;
private static final String configPath = "MyRsaConfig.txt";
//Four constructors //Option 1 -- generate a random RSA system using a specified key size:
public MyRSA(int kSz) { //Random system with (kSz) bits in p, q
SecureRandom rng = new SecureRandom();
long startTm = System.currentTimeMillis(), endTm;
p = new BigInteger(kSz, CRTTY, rng);
//prime with prob > 1 - 2^(-CRTTY)
endTm = System.currentTimeMillis();
System.err.println("Generating p took " + (endTm - startTm) + "ms");
startTm = endTm;
q = new BigInteger(kSz, CRTTY, rng);
endTm = System.currentTimeMillis();
System.err.println("Generating q took " + (endTm - startTm) + "ms");
n = p.multiply(q);
generateDE(kSz); //Random d > p,q, d < n, invertible mod (p-1)(q-1)
saveConfig();

}
//Option 2 -- generate an RSA system using p, q given by user;
public MyRSA(BigInteger bp1, BigInteger bp2) {
if (!setPQN(bp1, bp2))
System.exit(1); //terminate abnormally if nums are not prime
int pl = p.bitLength(), ql = q.bitLength();
generateDE(pl>ql ? pl : ql);
saveConfig();
}
//Option 3 -- generate an RSA system using p, q, d given by user:
public MyRSA(BigInteger bp1, BigInteger bp2, BigInteger dnew) {
if (!setPQN(bp1, bp2))
System.exit(1); //terminate abnormally if nums are not prime
int pl = p.bitLength(), ql = q.bitLength();
if (!setDE(dnew)) //terminate abnormally if d value is invalid
System.exit(1);
saveConfig();
}
//Option 4 (default) -- construct an RSA system from saved values:
public MyRSA() {
BigInteger pn, qn, dn;
try {
BufferedReader in = new BufferedReader(new FileReader(configPath));
pn = new BigInteger(in.readLine(), 16);
qn = new BigInteger(in.readLine(), 16);
dn = new BigInteger(in.readLine(), 16);
in.close();
if (!setPQN(pn, qn))
System.exit(1); //terminate abnormally if nums are not prime
if (!setDE(dn))
//terminate abnormally if d value is invalid
System.exit(1);
} catch (NumberFormatException ex) {
System.err.println("Invalid data in config file - " + ex);
System.exit(1);
} catch (EOFException ex) {
System.err.println("Unexpected end of config file");
System.exit(1);
} catch (IOException ex) {
System.err.println("Trouble reading config file");
System.exit(1);
} catch (NullPointerException ex) {
System.err.println("Trouble reading string from config file - " +ex);
System.exit(1);
}
//If we have survived thus far, we have a valid system!
}
public boolean setPQN(BigInteger bp1, BigInteger bp2) {
if (!(bp1.isProbablePrime(CRTTY) && bp2.isProbablePrime(CRTTY))) {
System.err.println("Error: p,q not prime");
return false;
}
p = bp1;
q = bp2;
n = p.multiply(q);
return true;
}

private boolean setDE(BigInteger dnew) {


//Assumes valid p, q, n set
BigInteger pm = p.subtract(BigInteger.ONE),
qm = q.subtract(BigInteger.ONE),
pqm = pm.multiply(qm); //(p-1)(q-1)
if (!(dnew.compareTo(p) > 0 && dnew.compareTo(q) > 0 &&
dnew.compareTo(n) < 0)) {
System.err.println("Error: suggested d out of range");
return false;
}
if (dnew.gcd(pqm).compareTo(BigInteger.ONE) != 0) {
System.err.println("Error: suggested d not invertible mod (p-1)(q-1)");
return false;
}
d = dnew; e = dnew.modInverse(pqm);
return true;
}
private void generateDE(int kSz) {
//Assumes valid p, q, n=pq already set.
// p, q < d < n ; gcd(d, (p-1)(q-1)) = 1
SecureRandom rgn = new SecureRandom();
BigInteger L = p.max(q),
dd = n.subtract(L),
pm = p.subtract(BigInteger.ONE),
qm = q.subtract(BigInteger.ONE),
pqm = pm.multiply(qm); //(p-1)(q-1)
do {
d = L.add(new BigInteger(kSz*2, rgn));
System.err.print(".");
} while (d.compareTo(n) >= 0 || d.gcd(pqm).compareTo(BigInteger.ONE) != 0);
System.err.println("\nd<n: " + d.compareTo(n));
System.err.println("gcd(d, (p-1)(q-1)): " + d.gcd(pqm));
e = d.modInverse(pqm);
}
public String toString() {
String dspStg = "p = " + p.toString(16);
String nl = dspStg.length() > 80? "\n\n": "\n";
dspStg += nl + "q = " + q.toString(16);
dspStg += nl + "n = " + n.toString(16) + "\n(" + n.bitLength() + " bits)";
dspStg += nl + "d = " + d.toString(16);
dspStg += nl + "e = " + e.toString(16);
return dspStg;
}
public void saveConfig() {
try {
PrintWriter out = new PrintWriter(new FileWriter(configPath));
out.println(p.toString(16));
out.println(q.toString(16));
out.println(d.toString(16));
out.close();
} catch (IOException ex) {
System.err.println("Could not save the config");
}
}
public MyRSAEncrypter getEncrypter() {
return new MyRSAEncrypter(e, n);
}

public MyRSADecrypter getDecrypter() {


return new MyRSADecrypter(d, n);
}
public static void main(String[] args) { //Just a test driver
MyRSA rsa = null;
if (args.length == 1) {
try {
int kSz = Integer.parseInt(args[0]);
if (kSz <= 0 || kSz > 4000)
System.out.println("Key size out of range");
else
rsa = new MyRSA(kSz); //this constructor saves the config
} catch (NumberFormatException ex0) { //arg was not an int ...
try {
//assume it is a path to a file of big primes
BufferedReader in = new BufferedReader(new FileReader(args[0]));
BigInteger pn = new BigInteger(in.readLine(), 16),
qn = new BigInteger(in.readLine(), 16);
in.close();
rsa = new MyRSA(pn, qn);
} catch (NumberFormatException ex) {
System.err.println("Invalid data in config file - " + ex);
} catch (EOFException ex) {
System.err.println("Unexpected end of config file");
} catch (IOException ex) {
System.err.println("Trouble reading config file");
} catch (NullPointerException ex) {
System.err.println("Trouble reading config file");
}
}
}
else
rsa = new MyRSA();
if (rsa != null) System.out.println(rsa.toString());
}
} //end MyRSA class
--------------------------------------------------------import java.math.BigInteger;
import java.io.*;
public class MyRSATst {
public static void main(String[] args) throws IOException {
if (args.length < 2) {
System.out.println("Usage: java MyRSATst [e|d] <path>");
return;
}
MyRSA rsa = new MyRSA();
//System.out.println(rsa);
MyTransformer trfmr;
if (args[0].charAt(0) == 'e')
trfmr = rsa.getEncrypter();
else if (args[0].charAt(0) == 'd')
trfmr = rsa.getDecrypter();
else {
System.out.println(rsa);
System.out.println("No encrypt/decrypt option specified");
return;

}
File inFile = new File(args[1]);
int inputLength = (int)(inFile.length()/100 + 1)*100;
BufferedInputStream inStrm = new BufferedInputStream(
new FileInputStream(inFile));
BufferedOutputStream outStrm = new BufferedOutputStream(
new FileOutputStream(args[1]+".out"));
System.err.println("Buffer size = " + inputLength);
byte[] buf = new byte[inputLength];
int nBytes = inStrm.read(buf);
System.out.println("\n" + nBytes + " bytes read");
byte[] msg = new byte[nBytes];
System.arraycopy(buf, 0, msg, 0, nBytes);
byte[] tmsg = trfmr.transform(msg);
System.out.println("" + tmsg.length + " bytes produced");
outStrm.write(tmsg);
inStrm.close();
outStrm.close();
}
}
--------------------------------------------------------import java.math.BigInteger;
//Base class for encrypter, decrypter -- contains static utility methods
//Ref: David Bishop chap 5 and pp 276-7
public abstract class MyTransformer {
//Pad a message to a multiple of the block size, according to PKCS#5 scheme
//Note that 1 <= nToPd <= blkSz extra bytes are added, = (byte)nToPd
protected static byte[] pad(byte[] msg, int blkSz) {
if (blkSz < 1 || blkSz > 255)
throw new IllegalArgumentException("Block size out of range");
int nToPd = blkSz - msg.length%blkSz;
byte[] pdMsg = new byte[msg.length + nToPd];
System.arraycopy(msg, 0, pdMsg, 0, msg.length);
for (int i=msg.length; i< pdMsg.length; i++)
pdMsg[i] = (byte)nToPd;
return pdMsg;
}
//Remove the PKCS#5 padding
protected static byte[] unpad(byte[] msg, int blkSz) {
int nPd = (msg[msg.length - 1] + 256) % 256; //unsigned val in last byte
//Chop off this many bytes
byte[] rslt = new byte[msg.length - nPd];
System.arraycopy(msg, 0, rslt, 0, rslt.length);
return rslt;
}
//Divide a msg into (blkSz)-sized blocks
//Assumes msg has been padded to an integral multiple of blkSz
protected static byte[][] block(byte[] msg, int blkSz) {
int nBlks = msg.length / blkSz;
byte[][] ba = new byte[nBlks][blkSz];

10

for (int i=0; i < nBlks; i++)


for (int j=0; j < blkSz; j++)
ba[i][j] = msg[i*blkSz + j];
return ba;
}
//put a series of encoded blocks back into a single array of byytes.
//The BigInt might be smaller than the block size, so fill the array
//from the rear of each block.
protected static byte[] unblock(byte[][] ba, int blkSz) {
byte[] ub = new byte [ba.length * blkSz];
for (int i=0; i<ba.length; i++) {
int j = blkSz-1, k = ba[i].length-1;
while (k >= 0) {
ub[i*blkSz+j] = ba[i][k];
k--; j--;
}
}
return ub;
}
public static void display(byte[] b) {
String digits = "0123456789abcdef";
for (int i=0; i<b.length; i++) {
System.out.print(digits.charAt((0xff&b[i]) >>> 4));
System.out.print(digits.charAt(b[i] & 0x0f));
}
System.out.println(" - "+ b.length + " bytes");
}
//Strip off the extra byte containing the sign bit (always 0 here)
//returned by toByteArray() (ref D Bishop p123)
protected static byte[] getBytes(BigInteger bg) {
byte[] bts = bg.toByteArray();
if (bg.bitLength()%8 != 0)
return bts;
else {
byte[] sbts = new byte[bg.bitLength()/8];
System.arraycopy(bts, 1, sbts, 0, sbts.length);
return sbts;
}
}
public abstract byte[] transform(byte[] msg);
} //end class MyTransformer
--------------------------------------------------------import java.math.BigInteger;
public class MyRSAEncrypter extends MyTransformer {
private BigInteger e, n;
public MyRSAEncrypter(BigInteger e, BigInteger n) {
this.e = e; this.n = n;
System.out.println("Encryption key:");
System.out.println("n = " + n.toString(16));
System.out.println("e = " + e.toString(16));
}

11

public byte[] transform(byte[] msg) {


long startTm = System.currentTimeMillis();
int blkSz = (n.bitLength() - 1)/8;
byte[][] ba = block(pad(msg, blkSz), blkSz);
BigInteger msgBlkInt;
System.err.println("" + ba.length + " blocks");
for (int i=0; i<ba.length; i++) {
msgBlkInt = new BigInteger(1, ba[i]); //make a +ve BigInt out of current
msgBlkInt = msgBlkInt.modPow(e, n);
// block, enxponentiate it, and
ba[i] = getBytes(msgBlkInt);
// recover the bytes using our mthd
if (i%10 == 0) System.err.print("\rBlock " + i);
}
System.err.println("\rEncryption took " +
(System.currentTimeMillis()-startTm) + " ms");
return unblock(ba, blkSz+1);
}
} //end class MyRSAEncrypter
--------------------------------------------------------import java.math.BigInteger;
public class MyRSADecrypter extends MyTransformer {
private BigInteger d, n;
public MyRSADecrypter(BigInteger d, BigInteger n) {
this.d = d; this.n = n;
System.out.println("Decryption key:");
System.out.println("n = " + n.toString(16));
System.out.println("d = " + d.toString(16));
}
public byte[] transform(byte[] msg) {
long startTm = System.currentTimeMillis();
int blkSz = (n.bitLength()-1)/8 + 1;
byte[][] ba = block(msg, blkSz);
BigInteger msgBlkInt;
System.err.println("" + ba.length + " blocks");
for (int i=0; i<ba.length; i++) {
msgBlkInt = new BigInteger(1, ba[i]); //make a +ve BigInt out of current
msgBlkInt = msgBlkInt.modPow(d, n);
// block, enxponentiate it, and
ba[i] = getBytes(msgBlkInt);
// recover the bytes
if (i%10 == 0) System.err.print("\rBlock " + i);
}
System.err.println("\nDecryption took " +
(System.currentTimeMillis()-startTm) + " ms");
return unpad(unblock(ba, blkSz-1), blkSz-1);
}
} //end class MyRSADecrypter
--------------------------------------------------------ElGamal Sources
--------------/*
* Makes a random safe prime p of a given minimum bit length,
* and a generator mod p.
* [Fermat little theorem => g^(p-1) = 1 mod p if p prime and not p|g;
* A generator mod p is an integer g such that for all 0 < k < p-1, g^k <> 1
* mod p. It follows that the g^k % p generate all the integers 0 < n < p.]

12

*/
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.*;
public class GeneratorFactory {
private int minBits, crtty;
private SecureRandom srng;
private BigInteger p, g;
private static final BigInteger
ZERO = BigInteger.ZERO,
ONE = BigInteger.ONE,
TWO = ONE.add(ONE),
THREE = TWO.add(ONE);
//Constructors
public GeneratorFactory (int bits) { this(bits, 300); }
public GeneratorFactory (int bits, int crtty) {
this(bits, crtty, new SecureRandom());
}
public GeneratorFactory (int bits, int crtty, SecureRandom sr) {
if (bits < 512)
System.err.println("WARNING: Safe primes should be >= 512 bits long");
this.minBits = bits;
this.crtty
= crtty;
this.srng
= sr;

System.out.printf("Making a safe prime of at least %d bits...\n", bits);


long startTm = System.currentTimeMillis(), endTm;
makeSafePrimeAndGenerator();
endTm = System.currentTimeMillis();
System.err.printf("Generating p, g took %d ms\n", endTm - startTm);
System.out.printf("p = %x (%d bits)\n", p, p.bitLength());
System.out.printf("g = %x (%d bits)\n", g, g.bitLength());

/* Method to make a safe prime (stored in this.p)


* and a generator (this.g) mod p. Uses method suggested by D Bishop:
* (1) Find a safe prime p = 2rt + 1 where r is smallish (~10^9), t prime;
* (2) Obtain the prime factorization of p-1 (quickish in view of (1));
* (3) Repeatedly make a random g, 1 < g < p until
*
for each prime factor f of p-1, g^((p-1)/f) is incogruent to 1 mod p.
*/
public void makeSafePrimeAndGenerator() {
BigInteger r = BigInteger.valueOf(0x7fffffff),
t = new BigInteger(minBits, crtty, srng);
//(1) make prime p
do {
r = r.add(ONE);
p = TWO.multiply(r).multiply(t).add(ONE);
} while (!p.isProbablePrime(crtty));
//(2) obtain prime factorization of p-1 = 2rt
HashSet<BigInteger> factors = new HashSet<BigInteger>();
factors.add(t); factors.add(TWO);
if (r.isProbablePrime(crtty))
factors.add(r);
else
factors.addAll(primeFact(r));
//We have set of prime factors of p-1.
//Now (3) look for a generator mod p. Repeatedly make

13

// a random g, 1 < g < p, until for each prime factor f of p-1,


// g^((p-1)/f) is incogruent to 1 mod p.
BigInteger pMinusOne = p.subtract(ONE), z, lnr;
boolean isGen;
do {
isGen = true;
g = new BigInteger(p.bitLength()-1, srng); //random, < p
for (BigInteger f: factors) { //check cond on g for each f
z = pMinusOne.divide(f);
lnr = g.modPow(z, p);
if (lnr.equals(ONE)) {
isGen = false;
break;
}
}
} while (!isGen);
// Now g is a generator mod p
} // end of makeSafePrimeAndGenerator() method
public static HashSet<BigInteger> primeFact(BigInteger n) {
BigInteger nn = new BigInteger(n.toByteArray()); //clone n
HashSet<BigInteger> factors = new HashSet<BigInteger>();
BigInteger dvsr
= TWO,
dvsrSq = dvsr.multiply(dvsr);
while (dvsrSq.compareTo(nn) <= 0) {
//divisor <= sqrt of n
if (nn.mod(dvsr).equals(ZERO)) {
//found a factor (must be prime):
factors.add(dvsr);
//add it to set
while (nn.mod(dvsr).equals(ZERO)) //divide it out from n completely
nn = nn.divide(dvsr);
//(ensures later factors are prime)
}
dvsr = dvsr.add(ONE);
//next possible divisor
dvsrSq = dvsr.multiply(dvsr);
}
//if nn's largest prime factor had multiplicity >= 2, nn will now be 1;
//if the multimplicity is only 1, the loop will have been exited leaving
//nn == this prime factor;
if (nn.compareTo(ONE) > 0)
factors.add(nn);
return factors;
}
public BigInteger getP() { return p; }
public BigInteger getG() { return g; }
//Test whether p is prime, not p|g, and g is a generator mod p.
public static boolean isGenerator(BigInteger p, BigInteger g, int crtty) {
System.err.printf("Testing p = %s,\ng = %s\n",
p.toString(16), g.toString(16));
if (!p.isProbablePrime(crtty)) {
System.err.println("p is not prime.");
return false;
}
if (g.mod(p).equals(ZERO)) {
System.err.println("p divides g.");
return false;
}
//See note below on generator test
BigInteger pMinusOne = p.subtract(ONE), z;
System.err.println("Finding prime factors of p-1 ...");

14

//Warning: a large prime factor will take a long time to find!


HashSet<BigInteger> factors = primeFact(pMinusOne);
boolean isGen = true;
for (BigInteger f: factors) { //check cond on g for each f
z = pMinusOne.divide(f);
if (g.modPow(z, p).equals(ONE)) {
isGen = false;
System.err.println("g is not a generator mod p.");
break;
}
}
return isGen;
}

//Test driver
public static void main(String[] args) {
int bitLen = 512;
if (args.length > 0) {
try {
bitLen = Integer.parseInt(args[0]);
} catch(NumberFormatException ex) {
bitLen = 512;
}
}
GeneratorFactory fact = new GeneratorFactory(bitLen);
BigInteger p = fact.getP(), g = fact.getG();
System.out.println("p probable prime: "
+ (p.isProbablePrime(300)?"yes":"no"));
if (g.compareTo(p) < 0)
System.out.println("g < p");
else
System.out.println("p divides g: " + (g.mod(p).equals(ZERO)?"yes":"no"));
}

/* Note on generator test:


* If p is prime and not a factor of g then |g|_p is in general a divisor of
* p-1 (see eg Bishop prop 37 or Apostol thm 10.1). So if g is NOT
* a generator mod p then |g|_p is a PROPER divisor of p-1.
* In that case, if F is the set of all prime factors of p-1 then some
* (p-1)/f, f in F, will be a multiple of |g|_p and hence fail the test (3).
*/
--------------------------------------------------------import java.math.BigInteger;
public class GenTest {
public static void main(String[] args) {
boolean isGen = GeneratorFactory.isGenerator(
new BigInteger(args[0], 16), new BigInteger(args[1], 16), 300);
System.err.println(args[1] + (isGen?" is ":" is not ")
+ "a generator mod "+ args[0]);
}
}
--------------------------------------------------------import java.math.BigInteger;
import java.security.SecureRandom;
import java.io.*;
public class MyElGamal {
private BigInteger p, g, a, r, pMinus2;

15

private
private
private
private
ZERO
TWO

SecureRandom srng;
static final int CRTTY = 300;
static final String configPath = "MyElGamalConfig.txt";
static final BigInteger
= BigInteger.ZERO,
ONE = BigInteger.ONE,
= ONE.add(ONE),
THREE = TWO.add(ONE);

//Two constructors //Option 1: generate a random system using specified key size; save config:
public MyElGamal(int kSz) { //Random system with at least (kSz) bits in p
srng = new SecureRandom();
GeneratorFactory fact = new GeneratorFactory(kSz, CRTTY, srng);
p = fact.getP(); pMinus2 = p.subtract(TWO);
g = fact.getG();
//a should be a random integer in range 1 < a < p-1
BigInteger pmt = p.subtract(THREE);
a = (new BigInteger(p.bitLength(), srng)).mod(pmt).add(TWO);
r = g.modPow(a, p);
saveConfig();
}
//Option 2 (default) -- construct a system from saved values:
public MyElGamal() {
srng = new SecureRandom();
try {
BufferedReader in = new BufferedReader(new FileReader(configPath));
p = new BigInteger(in.readLine(), 16);
g = new BigInteger(in.readLine(), 16);
a = new BigInteger(in.readLine(), 16);
in.close();
} catch (NumberFormatException ex) {
System.err.println("Invalid data in config file - " + ex);
System.exit(1);
} catch (EOFException ex) {
System.err.println("Unexpected end of config file");
System.exit(1);
} catch (IOException ex) {
System.err.println("Trouble reading config file");
System.exit(1);
} catch (NullPointerException ex) {
System.err.println("Trouble reading string from config file - " +ex);
System.exit(1);
}
if (!p.isProbablePrime(CRTTY)) {
System.err.println(p.toString(16) + " is not prime. Terminating.");
System.exit(1);
}
if (g.mod(p).equals(ZERO)) {
System.err.println(p.toString(16) + " divides " + g.toString(16) +
". Terminating.");
System.exit(1);
}
//That g is truely a generator mod p will take inordinately long to
//check if p-1 has a large prime factor.
//Notwithstading this, we have a system if we have go to here.
pMinus2 = p.subtract(TWO);
r = g.modPow(a, p);
}
public String toString() {
String dspStg = "p = " + p.toString(16);

16

String
dspStg
dspStg
dspStg
return

nl = dspStg.length() > 80? "\n\n": "\n";


+= nl + "g = " + g.toString(16);
+= nl + "a = " + a.toString(16);
+= nl + "r = " + r.toString(16);
dspStg;

public void saveConfig() {


try {
PrintWriter out = new PrintWriter(new FileWriter(configPath));
out.println(p.toString(16));
out.println(g.toString(16));
out.println(a.toString(16));
out.close();
} catch (IOException ex) {
System.err.println("Could not save the config");
}
}
public BigInteger getP() { return p; }
public BigInteger getG() { return g; }
public BigInteger getR() { return r; }
//A message block is considered a BigInteger.
//Returns pair of BigIntegers comprising the ElGamal cipher-"text"
public BigInteger[] encrypt(BigInteger m) {
BigInteger k = new BigInteger(p.bitLength(), srng);
k = k.mod(pMinus2).add(ONE);
BigInteger[] cipher = new BigInteger[2];
cipher[0] = g.modPow(k, p);
cipher[1] = r.modPow(k, p).multiply(m).mod(p);
return cipher;
}
public BigInteger decrypt(BigInteger c0, BigInteger c1) {
BigInteger c = c0.modPow(a, p).modInverse(p); //c0^-a mod p
return c.multiply(c1).mod(p);
}
public MyElGamalEncrypter getEncrypter() {
return new MyElGamalEncrypter(p, g, r);
}
public MyElGamalDecrypter getDecrypter() {
return new MyElGamalDecrypter(p, a);
}
/* Test bed
* Run with single integer argument, kSz, to generate a system and save it.
* Run with no arguments to initialise a system from a saved config,
* generate a random BigInteger < p, encrypt it and decrypt it again.
*/
public static void main(String[] args) {
if (args.length > 0) {
new MyElGamal(Integer.parseInt(args[0]));
System.err.println("Config saved in " + configPath);
} else {
MyElGamal sys = new MyElGamal();
SecureRandom sr = new SecureRandom();
BigInteger p = sys.getP(),
msg = (new BigInteger(p.bitLength(), sr)).mod(p);
System.out.println("Message = " + msg.toString(16));

17

BigInteger[] c = sys.encrypt(msg);
System.out.println("Cipher: c0 = " + c[0].toString(16));
System.out.println("Cipher: c1 = " + c[1].toString(16));
BigInteger d = sys.decrypt(c[0], c[1]);
System.out.println("Decrypted = " + d.toString(16));

}
}
} // end MyElGamal class
--------------------------------------------------------import java.math.BigInteger;
import java.io.*;

public class MyElGamalTst {


public static void main(String[] args) throws IOException {
if (args.length < 2) {
System.out.println("Usage: java MyElGamalTst [e|d] <path>");
return;
}
MyElGamal pkc = new MyElGamal();
MyTransformer trfmr;
if (args[0].charAt(0) == 'e')
trfmr = pkc.getEncrypter();
else if (args[0].charAt(0) == 'd')
trfmr = pkc.getDecrypter();
else {
System.out.println(pkc);
System.out.println("No encrypt/decrypt option specified");
return;
}
File inFile = new File(args[1]);
int inputLength = (int)(inFile.length()/100 + 1)*100;
BufferedInputStream inStrm = new BufferedInputStream(
new FileInputStream(inFile));
BufferedOutputStream outStrm = new BufferedOutputStream(
new FileOutputStream(args[1]+".out"));
System.err.println("Buffer size = " + inputLength);
byte[] buf = new byte[inputLength];
int nBytes = inStrm.read(buf);
System.out.println("\n" + nBytes + " bytes read");
byte[] msg = new byte[nBytes];
System.arraycopy(buf, 0, msg, 0, nBytes);
byte[] tmsg = trfmr.transform(msg);
System.out.println("" + tmsg.length + " bytes produced");
outStrm.write(tmsg);
inStrm.close();
outStrm.close();
}
}
--------------------------------------------------------public class MyTransformer
same as RSAs
---------------------------------------------------------

18

import java.math.BigInteger;
import java.security.SecureRandom;
public class MyElGamalEncrypter extends MyTransformer {
private BigInteger p, g, r, pMinus2;
private SecureRandom srng;
private static final BigInteger ONE = BigInteger.ONE, TWO = ONE.add(ONE);
//Assume p is prime, g is a gen mod p, r = g^a mod p (a = pvt key)
public MyElGamalEncrypter(BigInteger p, BigInteger g, BigInteger r) {
srng = new SecureRandom();
this.p = p; this.g = g; this.r = r;
pMinus2 = p.subtract(TWO);
System.out.println("Encryption key:");
System.out.println("p = " + p.toString(16));
System.out.println("g = " + g.toString(16));
System.out.println("r = " + r.toString(16));
}
public byte[] transform(byte[] msg) {
long startTm = System.currentTimeMillis();
int blkSz = (p.bitLength() - 1)/8;
byte[][] ba = block(pad(msg, blkSz), blkSz);
byte[][] ba2 = new byte[2*ba.length][];
BigInteger m, c0, c1, k;
System.err.println("" + ba.length + " blocks");
for (int i=0; i<ba.length; i++) {
m = new BigInteger(1, ba[i]);
//make +ve BigInt out of current blk
k = new BigInteger(p.bitLength(), srng);
k = k.mod(pMinus2).add(ONE);
//rndm k, 0 < k < p-1
c0 = g.modPow(k, p);
//compute ElGamal transform
c1 = r.modPow(k, p).multiply(m).mod(p);
ba2[2*i]
= getBytes(c0);
ba2[2*i+1] = getBytes(c1);

// convert to bytes

if (i%10 == 0) System.err.print("\rBlock " + i);


}
System.err.println("\rEncryption took " +
(System.currentTimeMillis()-startTm) + " ms");
return unblock(ba2, blkSz+1);

}
} //end class MyElGamalEncrypter
--------------------------------------------------------import java.math.BigInteger;
public class MyElGamalDecrypter extends MyTransformer {
private BigInteger p, a;
//Assume p is prime
public MyElGamalDecrypter(BigInteger p, BigInteger a) {
this.p = p; this.a = a;
System.out.println("Decryption key:");
System.out.println("p = " + p.toString(16));
System.out.println("a = " + a.toString(16));
}
public byte[] transform(byte[] msg) {

19

long startTm = System.currentTimeMillis();


int blkSz = (p.bitLength()-1)/8 + 1;
byte[][] ba2 = block(msg, blkSz);
byte[][] ba = new byte[ba2.length/2][];
BigInteger m, c0, c1, c;
System.err.println("" + ba.length + " blocks");
for (int i=0; i<ba.length; i++) {
c0 = new BigInteger(1, ba2[2*i]);
//make +ve BigInts out of
c1 = new BigInteger(1, ba2[2*i+1]); //current blocks
c = c0.modPow(a, p).modInverse(p);
m = c.multiply(c1).mod(p);

//c0^-a mod p
//recover plain "text"

ba[i] = getBytes(m);
//convert to bytes
if (i%10 == 0) System.err.print("\rBlock " + i);
}
System.err.println("\nDecryption took " +
(System.currentTimeMillis()-startTm) + " ms");
return unpad(unblock(ba, blkSz-1), blkSz-1);
}

20

//end class MyElGamalDecrypter

Potrebbero piacerti anche