开源非对称加密算法RSA/SM2实现及其应用

开源非对称加密算法RSA/SM2实现及其应用

前期内容导读:

  1. 开源加解密RSA/AES/SHA1/PGP/SM2/SM3/SM4介绍
  2. 开源AES/SM4/3DES对称加密算法介绍及其实现
  3. 开源AES/SM4/3DES对称加密算法的验证实现
  • 非对称加密主要是指秘钥对是非对称的(相对于对称加密而言),简单理解就是加密秘钥和解密秘钥不同,一般叫做公钥和私钥。公钥是需要给出去的,私钥需要自己保存,属于非常重要的隐私数据;
  • 非对称加密在加解密能力的基础上,还衍生出了签名和验证的能力,用于应对内容是否被篡改,就像古代的蜡封一样;
  • 由于非对称加密算法比较复杂,加解密效率并不高,加之当下服务都在分布式化,运行分布式服务的机器配置(包括VM)都不高,所以实际很少使用其加解密特性,大多都是使用其签名和验签能力,比如:加密机、PGP加密等;
  • 国际上非对称加密算法主要为RSA,与之对应的国密非对称加密算法为SM2,当下SM2的应用还相对较少;

1. 开源组件 非对称秘钥加密介绍

  • 加密组件引入方法:
    <dependency>
        <groupId>com.biuqugroupId>
        <artifactId>bq-encryptorartifactId>
        <version>1.0.1version>
    dependency>
    

1.1 非对称秘钥加密算法列表如下:

名称 全称 加密长度 常用模式 填充模式 签名算法 常用算法 加密特点 签名特点
RSA 3人名缩写 1024
2048
3072
4096
NONE
ECB
NoPadding
PKCS1Padding
OAEPPadding
SHA512WITHRSA
SHA256WITHRSA
RSA/NONE/NoPadding
RSA/ECB/PKCS1Padding
支持公钥加密,私钥解密
支持私钥加密,公钥解密
不支持分段加密
支持私钥签名,公钥验签
SM2 SM2椭圆曲线公钥密码算法 256 - - SM3WithSM2 - 国密算法,安全性优于RSA 2048,可用于替换RSA;
支持公钥加密,私钥解密
支持分段加密
支持私钥签名,公钥验签

说明:

  1. 加密长度: 在非加密算法中通常是指一次性可加密的密文块的长度。注意:加密长度不等于秘钥长度,在RSA/SM2中秘钥长度与加密长度是存在一定关系的;
  2. 在非对称加密算法中,RSA加密时,PKCS1Padding/OAEPPadding/NoPadding等多种填充算法,填充长度是不同的,这会直接影响加密的明文的长度;
  3. RSA虽然支持私钥加密公钥解密,但是这是不符合实际的应用场景的,因为公钥通常是要给出去的,有时不止给1个客户方,这样所有的客户方都可以解密了,框架中虽然可以支持这种方式,但是不推荐使用;
  4. 可以看下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

说明:

  1. RSA私钥长度是个范围,公钥长度是固定值;
  2. SM2公私钥长度是固定值;
  3. 自验发现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

说明:

  1. ${EncLen}表示加密长度,RSA每次可加密的明文长度=${EncLen}-填充长度,明文超过部分需要做二次加密调用,但BouncyCastle RSA不支持二次调用;
  2. RSA加密时,PKCS1Padding填充符占用11byte,NoPadding占0byte,OAEPPadding填充符占用42byte;在C/C++资料中有PKCS1OAEPPadding填充算法,可能和Java中的OAEPPadding 是同一种填充算法,待验证;尤其说明下,好多线上资料都说OAEPPadding填充占41byte,大家也可以自行验证下;
  3. RSA加密时,选择NoPadding时,在批量加密时,经常报错,但因为这种模式本来就不安全,本身也是不推荐使用的;
  4. SM2 密文长度不是定长的,密文最低是98byte,每增加1byte的数据,密文也相应增加1byte;
  5. 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();
    }
    

你可能感兴趣的:(biuqu项目,加解密组件,java,RSA,SM2,开源,加密)