package io.conducive.client.crypt;
import com.google.common.base.Joiner;
import com.google.gwt.core.client.Callback;
import com.googlecode.gwt.crypto.bouncycastle.AsymmetricBlockCipher;
import com.googlecode.gwt.crypto.bouncycastle.AsymmetricCipherKeyPair;
import com.googlecode.gwt.crypto.bouncycastle.InvalidCipherTextException;
import com.googlecode.gwt.crypto.bouncycastle.encodings.PKCS1Encoding;
import com.googlecode.gwt.crypto.bouncycastle.engines.RSAEngine;
import com.googlecode.gwt.crypto.bouncycastle.generators.RSAKeyPairGenerator;
import com.googlecode.gwt.crypto.bouncycastle.params.RSAKeyGenerationParameters;
import com.googlecode.gwt.crypto.bouncycastle.params.RSAKeyParameters;
import com.googlecode.gwt.crypto.bouncycastle.params.RSAPrivateCrtKeyParameters;
import com.googlecode.gwt.crypto.util.SecureRandom;
import java.math.BigInteger;
/**
* @author Reuben Firmin
*/
public class RSA {
protected final static String SPLITTER = "|";
public enum Strength {
ONE(1024),
TWO(2048),
FOUR(4096),
EIGHT(8192);
int strength;
private Strength(int strength) {
this.strength = strength;
}
public int getStrength() {
return strength;
}
public static Strength from(int i) {
for (Strength strength : values()) {
if (strength.getStrength() == i) {
return strength;
}
}
return null;
}
}
/**
* This version uses Forge. You must import either forge.bundle.js or forge.min.js.
*
* @param strength
* @return
*/
public static void makeKeyPair(int numWorkers, Strength strength, Callback<AsymmetricCipherKeyPair, String> callback) {
new Forge(numWorkers, strength, callback).generateKeys();
}
/**
* Note, we'd like to migrate to this, but it takes too long given https://code.google.com/p/google-web-toolkit/issues/detail?id=8310
* @param strength
* @return
*/
public static AsymmetricCipherKeyPair makeKeypairSlow(Strength strength) {
RSAKeyPairGenerator generator = new RSAKeyPairGenerator();
// see https://code.google.com/p/gwt-crypto/issues/detail?id=25
SecureRandom random = SecureRandom.getInstance(null);
BigInteger exponent = BigInteger.valueOf(65537);
RSAKeyGenerationParameters params = new RSAKeyGenerationParameters(
exponent,
random,
strength.strength,
80);
generator.init(params);
// see https://code.google.com/p/gwt-crypto/issues/detail?id=26
return generator.generateKeyPair();
}
public static String serialize(RSAPrivateCrtKeyParameters privateKey) {
return Joiner.on(SPLITTER).join(
privateKey.getModulus().toString(),
privateKey.getPublicExponent().toString(),
privateKey.getExponent().toString(),
privateKey.getP().toString(),
privateKey.getQ().toString(),
privateKey.getDP().toString(),
privateKey.getDQ().toString(),
privateKey.getQInv().toString());
}
public static RSAPrivateCrtKeyParameters deserialize(String serialized) {
String[] components = serialized.split("\\|");
if (components.length != 8) {
throw new IllegalArgumentException("Serialized key is malformed");
}
// TODO evaluate, ensure adequate
// "not really deprecated, just untested"
return new RSAPrivateCrtKeyParameters(
new BigInteger(new String(components[0])),
new BigInteger(new String(components[1])),
new BigInteger(new String(components[2])),
new BigInteger(new String(components[3])),
new BigInteger(new String(components[4])),
new BigInteger(new String(components[5])),
new BigInteger(new String(components[6])),
new BigInteger(new String(components[7]))
);
}
public static RSAPrivateCrtKeyParameters deserialize(String modulus, String publicExponent, String privateExponent,
String p, String q, String dP, String dQ, String qInv)
{
// "not really deprecated, just untested"
return new RSAPrivateCrtKeyParameters(new BigInteger(modulus),
new BigInteger(publicExponent),
new BigInteger(privateExponent),
new BigInteger(p),
new BigInteger(q),
new BigInteger(dP),
new BigInteger(dQ),
new BigInteger(qInv));
}
/**
* Note, per bouncycastle.org (/Schneir):
* "you should encrypt the data using a randomly generated key and a symmetric cipher, after that you should encrypt
* the randomly generated key using RSA, and then send the encrypted data and the encrypted random key to the other
* end where they can reverse the process (ie. decrypt the random key using their RSA private key and then decrypt
* the data)."
*
* @param publicKey
* @param raw
* @return
* @throws InvalidCipherTextException
*/
public static byte[] encrypt(RSAKeyParameters publicKey, String raw) throws InvalidCipherTextException {
AsymmetricBlockCipher eng = new PKCS1Encoding(new RSAEngine());
eng.init(true, publicKey);
byte[] input = raw.getBytes();
return eng.processBlock(input, 0, input.length);
}
public static String decrypt(RSAPrivateCrtKeyParameters privateKey, byte[] encrypted) throws InvalidCipherTextException {
AsymmetricBlockCipher eng = new PKCS1Encoding(new RSAEngine());
eng.init(false, privateKey);
return new String(eng.processBlock(encrypted, 0, encrypted.length));
}
}