我们需要用shell生成加密原文数据,存入mysql中,然后用java取出mysql中的加密数据并解密出原文。
这个任务中遇到的最大问题是,用shell进行base64编码后的加密数据,无法被java的解密程序解密,会报错。
Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:370)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:282)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:370)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:404)
at javax.crypto.Cipher.doFinal(Cipher.java:2226)
at cn.chinaunicom.paas.management.utils.RSAHelper.decryptByPrivateKey(RSAHelper.java:107)
at cn.chinaunicom.paas.management.utils.解密云池密码.main(解密云池密码.java:55)
深入到代码里面看,实际上是由于解密程序运行到unpadV15这个函数里面时,它的第一个字节是2,而不是0,导致了bp返回true,程序就报错终止。
众所周知,第一个字节是有代表意义的,它必须是特定值,不同的填充方式有不同的值。默认是填充0。
究其原因,还是使用shell输出被base64编码的数据的方式不对,没有用对语句消除换行符的影响。
正确的加密解密过程如下,为了方便理解,省略链接mysql的过程。
shell端
首先生成公钥私钥
(1)进入opensll交互
opensll
(2)生成私钥,1024为私钥长度
genrsa -out private_key.pem 1024
(3)将私钥以pkcs8的格式存储
pkcs8 -topk8 -inform PEM -in private_key.pem -outform PEM -nocrypt -out pkcs8
(4)生成公钥
rsa -in private_key.pem -pubout -out public_key.pem
(5)编写shell加密脚本
# 初始化原始需要加密数据
data_string="1q2w3e4r5t"
# 把数据保存到文件中。openssl似乎只能从文件中读取数据
echo $data_string > data_string
# 对数据进行base64编码
#data_base64=$(echo -n "$data_string" | base64)
# 把数据保存到文件中,方便openssl进行读取。
#echo $data_base64 > data_base64
# 使用openssl rsautl加密,保存数据到encrypted_data文件
openssl rsautl -encrypt -in data_string -inkey public_key.pem -pubin -out encrypted_data
#正确的输出方式
echo -n $(<encrypted_data) |base64
#错误的输出方式
echo $(<encrypted_data) |base64
#后面假设将加密后的数据存入了数据库,代码省略
很明显,输出的结果里是不同的。
编写java解密脚本。
public class 解密云池密码
{
public static void main(String[] args) throws Exception {
#假设已经从数据库里获取到了base64数据data
data="JgEbpDVWGgjeXHFGryRTmCvopamerBXQHCOiyD+JrrTTHXBzRRxFqfYZbkIqWqF+5R7weFhPpidL" +
"qpJ5dL9HXZQ8gqN9SGKwP0QorVGX6n+0Z/nM7RmjN4Hb4bEQ//Eu9vqwLVS5TEB7Zz13zb0I8CyK" +
"ErXkvb5I+AzCZaW+41U=";
String strPrivKey = ""//过长,省略。这里填写是pkcs8
String s = RSAHelper.decryptByPrivateKey(data,strPrivKey);
System.out.println(s);
}
}
//下面是RSAHelper中的decryptByPrivateKey函数的代码
public static String decryptByPrivateKey(String encryptedDataString, String privateKey)
throws Exception {
byte[] encryptedData = Base64Utils.decode(encryptedDataString.getBytes());
byte[] keyBytes = Base64Utils.decode(privateKey.getBytes());
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORM);
cipher.init(Cipher.DECRYPT_MODE, privateK);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
return new String(decryptedData);
}
输出结果就是我们加密的数据:1q2w3e4r5t