Bouncy Castle
Bouncy Castle supports DSA, RSA, and ECC (Elliptic Curve Cryptography) digital signature algorithms. My code example only shows how to use the RSA signature.
To generate an RSA key pair, BC requires the developer to pick a public exponent. A common pick is 65537 (Hex 10001) for strong security and fast performance:
Listing 1. Bouncy Castle RSA key generation in CryptoEngine
public void generateRSAKeyPair () throws Exception {
SecureRandom sr = new SecureRandom();
BigInteger pubExp = new BigInteger("10001", 16);
RSAKeyGenerationParameters RSAKeyGenPara =
new RSAKeyGenerationParameters(pubExp, sr, 1024, 80);
RSAKeyPairGenerator RSAKeyPairGen = new RSAKeyPairGenerator();
RSAKeyPairGen.init(RSAKeyGenPara);
AsymmetricCipherKeyPair keyPair = RSAKeyPairGen.generateKeyPair();
RSAprivKey = (RSAPrivateCrtKeyParameters) keyPair.getPrivate();
RSApubKey = (RSAKeyParameters) keyPair.getPublic();
}
Bouncy Castle's lightweight package lacks a good key serialization API. To serialize keys for transportation or future use, we must export key parameters one by one:
Listing 2. Bouncy Castle RSA key serialization in keygensrc/GenerateAllKeys.java
BigInteger mod = RSAprivKey.getModulus();
out = new FileOutputStream(outdir + "RSAmod.dat");
out.write(mod.toByteArray());
out.flush();
out.close();
BigInteger privExp = RSAprivKey.getExponent();
out = new FileOutputStream(outdir + "RSAprivExp.dat");
out.write(privExp.toByteArray());
out.flush();
out.close();
pubExp = RSAprivKey.getPublicExponent();
if ( !pubExp.equals(new BigInteger("10001", 16)) )
throw new Exception("public exponent does not match");
out = new FileOutputStream(outdir + "RSApubExp.dat");
out.write(pubExp.toByteArray());
out.flush();
out.close();
BigInteger dp = RSAprivKey.getDP();
out = new FileOutputStream(outdir + "RSAdp.dat");
out.write(dp.toByteArray());
out.flush();
out.close();
BigInteger dq = RSAprivKey.getDQ();
out = new FileOutputStream(outdir + "RSAdq.dat");
out.write(dq.toByteArray());
out.flush();
out.close();
BigInteger p = RSAprivKey.getP();
out = new FileOutputStream(outdir + "RSAp.dat");
out.write(p.toByteArray());
out.flush();
out.close();
BigInteger q = RSAprivKey.getQ();
out = new FileOutputStream(outdir + "RSAq.dat");
out.write(q.toByteArray());
out.flush();
out.close();
BigInteger qInv = RSAprivKey.getQInv();
out = new FileOutputStream(outdir + "RSAqInv.dat");
out.write(qInv.toByteArray());
out.flush();
out.close();
Among those parameters, mod and pubExp are needed for reconstructing the public key. To reconstruct public and private keys from serialized parameters, we use the following code in CryptoEngine's constructor:
Listing 3. Reconstruct key pair in CryptoEngine's constructor
is = c.getResourceAsStream("/keys/RSAmod.dat");
BigInteger RSAmod = new BigInteger(readFromStream(is));
is.close();
is = c.getResourceAsStream("/keys/RSAprivExp.dat");
BigInteger RSAprivExp = new BigInteger(readFromStream(is));
is.close();
is = c.getResourceAsStream("/keys/RSApubExp.dat");
BigInteger RSApubExp = new BigInteger(readFromStream(is));
is.close();
is = c.getResourceAsStream("/keys/RSAdp.dat");
BigInteger RSAdp = new BigInteger(readFromStream(is));
is.close();
is = c.getResourceAsStream("/keys/RSAdq.dat");
BigInteger RSAdq = new BigInteger(readFromStream(is));
is.close();
is = c.getResourceAsStream("/keys/RSAp.dat");
BigInteger RSAp = new BigInteger(readFromStream(is));
is.close();
is = c.getResourceAsStream("/keys/RSAq.dat");
BigInteger RSAq = new BigInteger(readFromStream(is));
is.close();
is = c.getResourceAsStream("/keys/RSAqInv.dat");
BigInteger RSAqInv = new BigInteger(readFromStream(is));
is.close();
RSAprivKey = new RSAPrivateCrtKeyParameters(RSAmod, RSApubExp,
RSAprivExp, RSAp, RSAq, RSAdp, RSAdq, RSAqInv);
RSApubKey = new RSAKeyParameters(false, RSAmod, RSApubExp);
Once you have keys, signing the digest is simple. Here we use SHA-1 digest: