Nov
16
RSA: Java bouncy castle 与 PHP openssl_public_encrypt 兼容的那点事儿
与某供应商对接的时候,要求用他们的RSA公钥加密,抛过来一个 RSAUtil.java ,核心代码大概是这样的:
RSA什么的,用php来做不要太简单,顺手就能写出来:
结果果然不行,发过去以后,对方表示无法解密,而我没有对方的私钥,不好验证,没办法,只能自己动手,造一对rsa密钥:
试了下,确实和 java 的结果不互通。不过在测试过程中发现一个现象:java生成的加密串总是一样的,而php生成的加密串总是不一样的。google搜了一下"php openssl_public_encrypt different everytime", Stack Overflow 的解释是,PHP的 openssl_public_encrypt 默认使用 PKCS#1 算法,引入随机数,用于防止流量探测(频率分析、密文匹配什么的,我就不懂了)。
所以很显然,Bouncy Castle 没有使用 PKCS#1 算法,放狗搜到官方文档说,Cipher.getInstance("RSA", "BC") ,第一个参数 RSA 相当于 "RSA/NONE/NoPadding" (当然也可以指定 RSA/NONE/PKCS1Padding )。
看了下 php的openssl_public_encrypt文档,可以给第四个参数“padding”指定不同的值,例如 OPENSSL_NO_PADDING ,但是试了下,发现直接失败了,只好再放狗,竟然搜到了 php的bugreport,还好第一个回复就说明了原因:需要手动用 ASCII 0 填充到 blocksize 才行(当然rsa并不禁止使用其他value,主要是加解密双方要约定好)。
验证了一下,用 OPENSSL_NO_PADDING 能够正常解密 java 生成的密文,并且在明文前面填充了若干 ASCII 0 ,补全到128字节,就此解决问题:
转载请注明出自 ,如是转载文则注明原出处,谢谢:)
RSS订阅地址: https://www.felix021.com/blog/feed.php 。
public byte[] encrypt(byte[] data, PublicKey pk) throws Exception {
Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, pk);
byte[] raw = new byte[128]; //cipher.getBlockSize() = 128
cipher.doFinal(data, 0, data.length, raw, 0);
return raw;
}
Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, pk);
byte[] raw = new byte[128]; //cipher.getBlockSize() = 128
cipher.doFinal(data, 0, data.length, raw, 0);
return raw;
}
RSA什么的,用php来做不要太简单,顺手就能写出来:
function rsa_encrypt($plain_text, $public_key_path)
{
$public_key = openssl_pkey_get_public(file_get_contents($public_key_path));
openssl_public_encrypt($plain_text, $encrypted, $public_key);
return bin2hex($encrypted);
}
function rsa_decrypt($encrypted, $private_key_path)
{
$private_key = openssl_pkey_get_private(file_get_contents($private_key_path));
openssl_private_decrypt(hex2bin($encrypted), $plain_text, $private_key);
return $plain_text;
}
{
$public_key = openssl_pkey_get_public(file_get_contents($public_key_path));
openssl_public_encrypt($plain_text, $encrypted, $public_key);
return bin2hex($encrypted);
}
function rsa_decrypt($encrypted, $private_key_path)
{
$private_key = openssl_pkey_get_private(file_get_contents($private_key_path));
openssl_private_decrypt(hex2bin($encrypted), $plain_text, $private_key);
return $plain_text;
}
结果果然不行,发过去以后,对方表示无法解密,而我没有对方的私钥,不好验证,没办法,只能自己动手,造一对rsa密钥:
引用
$ openssl genrsa -out test.pem 1024
$ openssl rsa -in test.pem -pubout -out test_public.pem
$ openssl rsa -in test.pem -pubout -out test_public.pem
试了下,确实和 java 的结果不互通。不过在测试过程中发现一个现象:java生成的加密串总是一样的,而php生成的加密串总是不一样的。google搜了一下"php openssl_public_encrypt different everytime", Stack Overflow 的解释是,PHP的 openssl_public_encrypt 默认使用 PKCS#1 算法,引入随机数,用于防止流量探测(频率分析、密文匹配什么的,我就不懂了)。
所以很显然,Bouncy Castle 没有使用 PKCS#1 算法,放狗搜到官方文档说,Cipher.getInstance("RSA", "BC") ,第一个参数 RSA 相当于 "RSA/NONE/NoPadding" (当然也可以指定 RSA/NONE/PKCS1Padding )。
看了下 php的openssl_public_encrypt文档,可以给第四个参数“padding”指定不同的值,例如 OPENSSL_NO_PADDING ,但是试了下,发现直接失败了,只好再放狗,竟然搜到了 php的bugreport,还好第一个回复就说明了原因:需要手动用 ASCII 0 填充到 blocksize 才行(当然rsa并不禁止使用其他value,主要是加解密双方要约定好)。
验证了一下,用 OPENSSL_NO_PADDING 能够正常解密 java 生成的密文,并且在明文前面填充了若干 ASCII 0 ,补全到128字节,就此解决问题:
function rsa_encrypt($plain_text, $public_key_path)
{
$public_key = openssl_pkey_get_public(file_get_contents($public_key_path));
openssl_public_encrypt(str_pad($plain_text, 128, "\0", STR_PAD_LEFT), $encrypted, $public_key, OPENSSL_NO_PADDING);
return bin2hex($encrypted);
}
function rsa_decrypt($encrypted, $private_key_path)
{
$private_key = openssl_pkey_get_private(file_get_contents($private_key_path));
openssl_private_decrypt(hex2bin($encrypted), $plain_text, $private_key, OPENSSL_NO_PADDING);
return ltrim($plain_text, "\0");
}
{
$public_key = openssl_pkey_get_public(file_get_contents($public_key_path));
openssl_public_encrypt(str_pad($plain_text, 128, "\0", STR_PAD_LEFT), $encrypted, $public_key, OPENSSL_NO_PADDING);
return bin2hex($encrypted);
}
function rsa_decrypt($encrypted, $private_key_path)
{
$private_key = openssl_pkey_get_private(file_get_contents($private_key_path));
openssl_private_decrypt(hex2bin($encrypted), $plain_text, $private_key, OPENSSL_NO_PADDING);
return ltrim($plain_text, "\0");
}
欢迎扫码关注:
转载请注明出自 ,如是转载文则注明原出处,谢谢:)
RSS订阅地址: https://www.felix021.com/blog/feed.php 。
火蜥蜴
2018-5-25 15:30
mark一下,也碰到了这个问题
无名先生
2018-3-6 16:50
我花了几天研究但一看到了这文件所有都迎刃而解了谢谢你
柚子同学
2017-7-19 14:44
哈哈哈 大神 花了好长时间看了你的所有资料
peaceful
2017-5-17 18:58
good
分页: 1/1 1