4.Java开源RSA/SM2非对称加密算法对比介绍
- 支持RSA(1024/2048/3072/4096)/AES(128/192/256)/SHA-1/SHA-256/SHA-512/SHA-3/MD5/PGP/HMAC-SHA256/HMAC-SHA512等国际通用的加密算法;- 支持SM2/SM3/SM4/HMAC-SM3等国密算法;- 还支持国密和国际加密算法的统一抽象与封装,并封装了国际/国密组合使用的一些实践;- 本加解密组件基于Bo
·
4.Java开源RSA/SM2非对称加密算法介绍
前期内容导读:
非对称加密
主要是指秘钥对
是非对称的(相对于对称加密
而言),简单理解就是加密秘钥和解密秘钥不同,一般叫做公钥和私钥。公钥是需要给出去的,私钥需要自己保存,属于非常重要的隐私数据;非对称加密
在加解密能力的基础上,还衍生出了签名和验证的能力,用于应对内容是否被篡改,就像古代的蜡封一样;- 由于非对称加密算法比较复杂,加解密效率并不高,加之当下服务都在分布式化,运行分布式服务的机器配置(包括VM)都不高,所以实际很少使用其加解密特性,大多都是使用其签名和验签能力,比如:加密机、PGP加密等;
- 国际上非对称加密算法主要为RSA,与之对应的国密非对称加密算法为SM2,当下SM2的应用还相对较少;
1. 开源组件 非对称秘钥加密介绍
- 加密组件引入方法:
<dependency> <groupId>com.biuqu</groupId> <artifactId>bq-encryptor</artifactId> <version>1.0.5</version> </dependency>
1.1 非对称秘钥加密算法列表如下:
名称 | 全称 | 加密长度 | 常用模式 | 填充模式 | 签名算法 | 常用算法 | 加密特点 | 签名特点 |
---|---|---|---|---|---|---|---|---|
RSA | 3人名缩写 | 2048 3072 4096 | ECB | NoPadding PKCS1Padding OAEPPadding | SHA512WITHRSA SHA256WITHRSA … | RSA/ECB/PKCS1Padding | 支持公钥加密,私钥解密 支持私钥加密,公钥解密 不支持分段加密 | 支持私钥签名,公钥验签 |
SM2 | SM2椭圆曲线公钥密码算法 | 256 | - | - | SM3WithSM2 | - | 国密算法,安全性优于RSA 2048,可用于替换RSA; 支持公钥加密,私钥解密 支持分段加密 | 支持私钥签名,公钥验签 |
说明:
加密长度
: 在非加密算法中通常是指一次性可加密的密文块的长度。注意:加密长度
不等于秘钥长度,在RSA/SM2中秘钥长度与加密长度是存在一定关系的;- 在非对称加密算法中,RSA加密时,PKCS1Padding/OAEPPadding/NoPadding等多种填充算法,填充长度是不同的,这会直接影响加密的明文的长度;
RSA
虽然支持私钥加密公钥解密,但是这是不符合实际的应用场景的,因为公钥通常是要给出去的,有时不止给1个客户方,这样所有的客户方都可以解密了,框架中虽然可以支持这种方式,但是不推荐使用;- 可以看下
BouncyCastle
源码org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.java
,里面有各种填充模式;并且里面做了全大写转换匹配,所以不会有大小写问题;
1.2 非对称秘钥加密算法的特点如下:
- 加密长度、秘钥和明文的关系表如下:
加密算法 | 秘钥初始值 (byte) | 加密长度 (bit) | 私钥长度 (byte) | 公钥长度 (byte) | 生成耗时 (ms) |
---|---|---|---|---|---|
RSA | 1000 | 1024 | 633-636 | 162 | 79 |
RSA | 1000 | 2048 | 1216-1218 | 294 | 612 |
RSA | 1000 | 3072 | 1792-1795 | 422 | 2038 |
RSA | 1000 | 4096 | 2372-2376 | 550 | 8577 |
SM2 | 1000 | 256 | 150 | 91 | 15 |
说明:
- RSA私钥长度是个范围,公钥长度是固定值;
- SM2公私钥长度是固定值;
- 自验发现SM2生成秘钥的效率要远远高于RSA;RSA加密长度提升时,其生成耗时也增长了好几倍(限于本人机器性能和精力有限,仅做了20+次简单验证);
- 明文长度、加密长度、填充算法与密文长度关系表:
加密算法 | 加密长度 (bit) | 填充算法 | 填充长度 (byte) | 明文长度 (byte) | 密文长度 (byte) | 加密耗时 (ms) |
---|---|---|---|---|---|---|
RSA | 1024/2048/3072/4096 | NoPadding | 0 | 1-${EncLen} | ${EncLen} | 400-500 |
RSA | 1024/2048/3072/4096 | PKCS1Padding | 11 | 1-(${EncLen} -11) | ${EncLen} | 400-500 |
RSA | 1024/2048/3072/4096 | OAEPPadding | 42 | 1-(${EncLen} -42) | ${EncLen} | 400-500 |
SM2 | 256 | - | - | 1 | 98 | 15 |
SM2 | 256 | - | - | 2 | 99 | 15 |
SM2 | 256 | - | - | … | … | 15 |
SM2 | 256 | - | - | 65 | 162 | 15 |
说明:
${EncLen}
表示加密长度,RSA每次可加密的明文长度=${EncLen}
-填充长度,明文超过部分需要做二次加密调用,但BouncyCastle RSA不支持二次调用;- RSA加密时,PKCS1Padding填充符占用11byte,NoPadding占0byte,
OAEPPadding
填充符占用42byte;在C/C++资料中有PKCS1OAEPPadding
填充算法,可能和Java中的OAEPPadding
是同一种填充算法,待验证;尤其说明下,好多线上资料都说OAEPPadding
填充占41byte,大家也可以自行验证下;- RSA加密时,选择
NoPadding
时,在批量加密时,经常报错,但因为这种模式本来就不安全,本身也是不推荐使用的;- SM2 密文长度不是定长的,密文最低是98byte,每增加1byte的数据,密文也相应增加1byte;
- BouncyCastle SM2支持分段加密;
2. 开源组件 非对称秘钥加密实现
2.1 对称秘钥加密代码设计
bq-encryptor非对称秘钥加密代码设计 | |||||
---|---|---|---|---|---|
算法名称 | 算法实现类 | 抽象类 | 是否安全 | 补充说明 | |
RSA | RsaEncryption | BaseSingleSignature | ✔ | 2048及以上是安全的 | |
SM2 | Sm2Encryption | ✔ | RSA的国内替代算法 |
2.2 非对称秘钥加密核心逻辑
-
非对称加密RSA与SM2实现的原理完全不同,所以抽象类
BaseSingleSignature
只做了公共接口定义,比如:生成秘钥、加密、解密、签名、验签等,具体的实现分别在算法实现类里面; -
RSA与SM2的设计与实现会单独讲解;
3. 开源组件 非对称加密使用
以SM2算法为例,可以有如下3种使用方式:
- 使用方式1:直接创建SM2加密对象
BaseSingleSignature encryption = new Sm2Encryption();
- 使用方式2:通过算法工厂创建SM2加密对象
BaseSingleSignature encryption = EncryptionFactory.SM2.createAlgorithm();
4.RSA算法的的应用
4.1 RSA在JwtToken的应用
- 在
spring-security-oauth2-authorization-server
自定义Jwt认证服务的秘钥:/** * 注入秘钥管理服务 * * @param JWK 秘钥对象 * @return 秘钥管理服务 */ @Bean public JWKSource<SecurityContext> jwkSource(JWK jwk) { JWKSet jwkSet = new JWKSet(jwk); return (jwkSelector, context) -> jwkSelector.select(jwkSet); } ... /** * 生成JWK对象 * * @param priKey 私钥(非必传时,表示仅需公钥验证) * @param pubKey 公钥 * @param kid 秘钥id(可重新设置,重启后对所有客户端生效) * @return JWK秘钥对象 */ private static JWK genRsaKey(byte[] priKey, byte[] pubKey, String kid) { BaseSingleSignature encryption = EncryptionFactory.RSA.createAlgorithm(); RSAPublicKey rsaKey = (RSAPublicKey)encryption.toPubKey(pubKey); RSAKey.Builder builder = new RSAKey.Builder(rsaKey); if (null != priKey) { PrivateKey rsaPriKey = encryption.toPriKey(priKey); builder.privateKey(rsaPriKey); } if (null == kid) { kid = UUID.randomUUID().toString(); } return builder.keyID(kid).build(); }
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
已为社区贡献3条内容
所有评论(0)