网上很多资料讲rsa的js实现与java实现,当数据量较小时基本都没问题,但加密的数据块过大时js与java加密的结果始终不一样,究其原因问题还是出在java代码上。对于大数据块java端要分块加密(这部分网上也有很多资料提及但加密结果与js加密的并不一样),这里说下我自己亲身实践的结果。对要加密的字符串按每62个字符做分割,对分割的每组数据分别加密然后用空格连接,这样加密的结果就和js端加密的结果一样了。
JAVA RSA加密工具类
/**
* @author Administrator
*
*/
public class RSAUtil
{
public static final String NOPADDING = "RSA/NONE/NoPadding";
public static final String PKCS1PADDING = "RSA/ECB/PKCS1Padding";
public static String encrypt(String e, String n, String s) throws Exception
{
return encrypt(e, n, s, PKCS1PADDING);
}
public static String encrypt(String e, String n, String s, String transformation) throws Exception
{
RSAPublicKey key = (RSAPublicKey) getPublicKey(n, e);
// return encryptByPublicKey(s, key);
Cipher cipher = Cipher.getInstance(transformation, new BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] result = cipher.doFinal(s.getBytes());
return HttpUtils.convertToHexString(result);
}
public static PublicKey getPublicKey(String modulus, String publicExponent) throws NoSuchAlgorithmException, InvalidKeySpecException
{
BigInteger bigIntModulus = new BigInteger(modulus, 16);
BigInteger bigIntPrivateExponent = new BigInteger(publicExponent, 16);
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA", new BouncyCastleProvider());
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
}
/**
* * 加密 *
*
* @param key
* 加密的密钥 *
* @param data
* 待加密的明文数据 *
* @return 加密后的数据 *
* @throws Exception
*/
public static String encryptWithSize(String e, String n, String text) throws Exception
{
try
{
RSAPublicKey pk = (RSAPublicKey) getPublicKey(n, e);
byte[] data = text.getBytes();
Cipher cipher = Cipher.getInstance("RSA/NONE/NoPadding", new BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, pk);
int blockSize = cipher.getBlockSize();// 获得加密块大小,如:加密前数据为128个byte,而key_size=1024
// 加密块大小为127
// byte,加密后为128个byte;因此共有2个加密块,第一个127
// byte第二个为1个byte
int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
int leavedSize = data.length % blockSize;
int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize;
byte[] raw = new byte[outputSize * blocksSize];
int i = 0;
while (data.length - i * blockSize > 0)
{
if (data.length - i * blockSize > blockSize)
cipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize);
else
cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize);
// 这里面doUpdate方法不可用,查看源代码后发现每次doUpdate后并没有什么实际动作除了把 byte[]放到
// ByteArrayOutputStream中,而最后doFinal的时候才将所有的byte[]进行加密,可是到了此时加密块大小很可能已经超出了
// OutputSize所以只好用dofinal方法。
i++;
}
return HttpUtils.convertToHexString(raw);
}
catch (Exception exp)
{
throw new Exception(exp.getMessage());
}
}
/**
* 公钥加密
*
* @param data
* @param publicKey
* @return
* @throws Exception
*/
public static String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception
{
Cipher cipher = Cipher.getInstance("RSA/NONE/NoPadding", new BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// 模长
int key_len = publicKey.getModulus().bitLength() / 8;
// 加密数据长度 <= 模长-11
String[] datas = splitString(data, key_len - 11);
String mi = "";
// 如果明文长度大于模长-11则要分组加密
for (String s : datas)
{
mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;
}
/**
* 拆分字符串
*/
public static String[] splitString(String string, int len)
{
int x = string.length() / len;
int y = string.length() % len;
int z = 0;
if (y != 0)
{
z = 1;
}
String[] strings = new String[x + z];
String str = "";
for (int i = 0; i < x + z; i++)
{
if (i == x + z - 1 && y != 0)
{
str = string.substring(i * len, i * len + y);
}
else
{
str = string.substring(i * len, i * len + len);
}
strings[i] = str;
}
return strings;
}
/**
* BCD转字符串
*/
public static String bcd2Str(byte[] bytes)
{
char temp[] = new char[bytes.length * 2], val;
for (int i = 0; i < bytes.length; i++)
{
val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
val = (char) (bytes[i] & 0x0f);
temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
}
return new String(temp);
}
}
对加密块的处理
/**
* 通过指定长度分割字符串
*
* @param str
* @param len
* @return
*/
private List<String> splitStr(String str, int len) {
List<String> list = new ArrayList<String>();
String value = str;
while (value.length() > len) {
String sub = value.substring(0, len);
list.add(sub);
value = value.substring(len);
}
list.add(value);
return list;
}
/**
* 分组加密数据
*
* @param strList
* @return
* @throws Exception
*/
private String encryParams(List<String> strList) throws Exception {
StringBuffer sb;
String result;
StringBuffer retStr = new StringBuffer();
for (int i = 0; i < strList.size(); i++) {
sb = new StringBuffer(strList.get(i));
result = RSAUtil.encrypt("10001", rsakey, sb.reverse().toString(), RSAUtil.NOPADDING);
retStr.append(result).append(" ");
}
return retStr.toString().trim();
}