An Implementation of RSA and ElGamal PKCs Using Java BigInteger Class
An Implementation of RSA and ElGamal PKCs Using Java BigInteger Class
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.
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));
}
//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;
}
}
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
11
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;
13
14
//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"));
}
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
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.*;
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
}
} //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
//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