Rsa加解密 + 签名验签
Rsa加解密
- 概述
- 聚合算法名称(用于创建加密器)
- 基本概念
- 填充方式
- 分块加密
- 基本使用
- 生成密钥
- 加解密
- 创建加密器
- 设置模式(加密)、公钥
- 对明文加密,并对结果进行Base64编码
- 对以上结果,进行解密 设置模式(解密)、私钥
- 签名 + 验签
- 创建签名器
- 初始化签名器,加载签名明文
- 生成签名
- 初始化验签参数,加载签名明文
- 验签
- 关于初始化加密器和签名器的重载方法
概述
RSA 是一种非对称加密算法,它使用一对密钥:公钥和私钥。公钥用于加密数据,私钥用于解密数据,或者私钥用于签名,公钥用于验证签名。
聚合算法名称(用于创建加密器)
JDK官方文档
常用名称:
- 加密算法名
- RSA/ECB/NoPadding
- RSA/None/NoPadding
- RSA/ECB/PKCS1Padding
- RSA/ECB/OAEPWithSHA-1AndMGF1Padding
- RSA/ECB/OAEPWithSHA-256AndMGF1Padding
- 签名算法
- SHA1withRSA(早期使用,现不推荐)
- SHA256withRSA(满足日常需要,一般情况用得最多)
- SHA384withRSA(安全性要求更高,计算成本随着变高)
- SHA512withRSA(安全性要求极高)
- MD5withRSA(目前已不推荐使用,MD5被证明存在安全漏洞)
基本概念
RSA需要一对密钥对,通常需要代码或工具生成。
原始密钥数据是字节数组,因此如果需要分发或者存储,通常也会进行Base64编码。
填充方式
RSA对明文同样有要求,明文块不能大于密钥长度。因此,通常情况下,也需要进行明文填充,如果选择无填充,那么明文长度如果不是密钥长度倍数,那么可能会报错。
- PKCS1Padding
这是最常用的填充方式,基本满足日常使用 - OAEP(OAEPWithSHA-1AndMGF1Padding 和 OAEPWithSHA-256AndMGF1Padding)
OAEP 是一种更安全的填充方式,主要用于防止选择明文攻击(CPA)等高级攻击方式。它基于哈希函数和掩码生成函数来构建填充结构。多用于安全要求较高的场景,如金融等场景。虽然安全性更高,但是计算成本也更高。 - NoPadding
不进行填充,此时对明文有要求,长度须是密钥整数倍。
分块加密
RSA算法要求:单次加密明文长度不能超出密钥长度;如果明文可能超出密钥长度,要么使用ECB分块模式加密,要么手动分块后逐个块加密。
分块模式:ECB(按密钥长度分块)、NONE(不分块)
基本使用
生成密钥
支持密钥长度:512及以下(早期使用,现在少用)、1024(用得较多)、2048(目前使用最多)、3072 位和 4096 位(对安全要求极高的场景,但会增加计算成本)
- 生成密钥对象
KeyPairGenerator pairGenerator = KeyPairGenerator.getInstance("RSA");
pairGenerator.initialize(2048);
KeyPair keyPair = pairGenerator.genKeyPair();
- 解析出公私钥(可以从密钥对象中获取公私钥)
// 私钥
PrivateKey privateKey = keyPair.getPrivate();
// 公钥
PublicKey publicKey = keyPair.getPublic();
- 此时的公私钥是以字节数组存在,如果要进行存储和分发。通常使用Base64编码
// 私钥字符串
String privateEncode = Base64.getEncoder().encodeToString(privateKey.getEncoded());
// 公钥字符串
String publicEncode = Base64.getEncoder().encodeToString(publicKey.getEncoded())
加解密
创建加密器
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
设置模式(加密)、公钥
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
对明文加密,并对结果进行Base64编码
byte[] bytes = cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));
System.out.println("加密后的内容:" + Base64.getEncoder().encodeToString(bytes));
对以上结果,进行解密 设置模式(解密)、私钥
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
byte[] bytes1 = cipher.doFinal(bytes);
System.out.println("解密后的内容:" + new String(bytes1));
签名 + 验签
我们通常说的RSA签名实际上是一种简略的说法。
RSA签名实际是:签名 + 加密 2个过程。
其中散列算法(或者叫摘要算法,签名算法等等)常用的有:
- SHA256(大部分场景使用,平衡安全和性能)
- SHA384
- SHA512
- MD5(MD5被证明存在漏洞,不推荐使用)
- SHA1(早期使用,目前已不推荐使用)
这些散列算法,是独立于RSA的,RSA签名算法名称就是在它们后面增加 withRSA 而已。
所谓RSA签名,只是对散列算法得到的散列值进行RSA加密而已。这一点要明确。
接着上面创建RSA密钥后,开始进行签名与验签
创建签名器
与加解密不同,签名器由Signature这个类提供支持
Signature signature = Signature.getInstance("SHA256withRSA");
初始化签名器,加载签名明文
signature.initSign(keyPair.getPrivate());
signature.update(str.getBytes(StandardCharsets.UTF_8));
生成签名
byte[] byteSign = signature.sign();
System.out.println("签名:" + Base64.getEncoder().encodeToString(byteSign));
初始化验签参数,加载签名明文
signature.initVerify(keyPair.getPublic());
signature.update(str.getBytes(StandardCharsets.UTF_8));
验签
boolean verify = signature.verify(byteSign);
System.out.println("验签结果:" + verify);
关于初始化加密器和签名器的重载方法
在RSA加密场景,除了模式,公钥外,还有个参数SecureRandom random,它用于在一些填充模式(OAEP)下,生成一个随机掩码,提高加密安全性。