5.开源非对称加密算法RSA实现

news/2025/2/13 21:16:10/

5.开源非对称加密算法RSA实现

前期内容导读:

  1. 开源加解密RSA/AES/SHA1/PGP/SM2/SM3/SM4介绍
  2. 开源AES/SM4/3DES对称加密算法介绍及其实现
  3. 开源AES/SM4/3DES对称加密算法的验证实现
  4. 开源非对称加密算法RSA/SM2实现及其应用

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

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

1.1 RSA的加解密实现

  • 加解密核心逻辑

    public byte[] doCipher(byte[] data, byte[] key, int cipherMode)
    {try{//1.获取秘钥对象Key algKey = toKey(key);//2.根据填充类型获取加密对象Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", BouncyCastleProvider.PROVIDER_NAME);//3.初始化加密对象cipher.init(cipherMode, algKey);byte[] partData = cipher.doFinal(data, 0, data.length);return partData;}catch (Exception e){throw new EncryptionException("do rsa encrypt/decrypt error.", e);}
    }
    

    说明:

    1. 上面的代码阐述了加解密的核心流程:根据二进制生成秘钥,再基于加密对象填充数据获得结果;
    2. 通过上述核心代码逻辑验证每种填充算法最大可加密多少明文byte;
    3. 通过秘钥二进制反向生成秘钥对象是一个有意思且有点复杂的事情,后面再单独说明;
  • 受RSA算法的加密长度、填充算法、明文长度、BouncyCastle不支持加分段加密的影响(在开源非对称加密算法RSA/SM2实现及其应用 中有介绍),上述核心逻辑是无法商用的,可商用的逻辑如下:

    public byte[] doCipher(byte[] data, byte[] key, int cipherMode)
    {ByteArrayOutputStream out = new ByteArrayOutputStream();try{//1.获取秘钥对象Key algKey = toKey(key);//2.根据填充类型获取加密对象Cipher cipher;if (null == this.getPaddingMode()){cipher = Cipher.getInstance(this.getAlgorithm());}else{cipher = Cipher.getInstance(this.getPaddingMode(), this.getProvider());}//3.初始化加密对象cipher.init(cipherMode, algKey);//4.根据RSA类型获取每次处理报文的最大字节数int maxLen = this.rsaType.getDecryptLen(this.getPaddingMode());if (cipherMode == Cipher.DECRYPT_MODE){maxLen = this.rsaType.getEncryptLen();}//5.分段加解密int start = 0;while (start < data.length){//5.1获取每次的起始位置int limit = start + maxLen;limit = Math.min(limit, data.length);//5.2分段加解密后,把该段报文写入缓存byte[] partData = cipher.doFinal(data, start, limit - start);out.write(partData, 0, partData.length);//5.3把分段的起始位置挪至上一次的结束位置start = limit;}return out.toByteArray();}catch (Exception e){throw new EncryptionException("do rsa encrypt/decrypt error.", e);}finally{IOUtils.closeQuietly(out);}
    }
    

    说明:

    1. 在加密的核心逻辑上,加了秘钥长度和填充长度的关系处理;
    2. 在单次加密正常后,还迭代对超长的明文做了循环截取加密;

1.2 RSA生成秘钥即转换实现

  • 秘钥生成逻辑
    public KeyPair createKey(byte[] initKey)
    {try{KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(this.getAlgorithm(), this.getProvider());if (null != initKey){SecureRandom random = this.createRandom(initKey);keyGenerator.initialize(this.getEncryptLen(), random);}else{keyGenerator.initialize(this.getEncryptLen());}return keyGenerator.generateKeyPair();}catch (Exception e){throw new EncryptionException("create rsa key pair error.", e);}
    }
    
  • 公钥、私钥反向生成逻辑
    public PublicKey toPubKey(byte[] pubKey)
    {try{X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKey);KeyFactory keyFactory = KeyFactory.getInstance(this.getAlgorithm());return keyFactory.generatePublic(keySpec);}catch (Exception e){throw new EncryptionException("get rsa public key error.", e);}
    }public PrivateKey toPriKey(byte[] priKey)
    {try{PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(priKey);KeyFactory keyFactory = KeyFactory.getInstance(this.getAlgorithm());return keyFactory.generatePrivate(keySpec);}catch (Exception e){throw new EncryptionException("get rsa private key error.", e);}
    }
    

    说明:

    1. 上述几段秘钥相关的代码可以把秘钥转成二进制,也可以把秘钥二进制反向转成秘钥对象,但是是怎么知道秘钥二进制是私钥或是公钥呢?
  • 公钥or私钥的判定逻辑:
    private Key toKey(byte[] key)
    {Key rsaKey;if (this.rsaType.isPriKey(key)){rsaKey = toPriKey(key);}else{rsaKey = toPubKey(key);}return rsaKey;
    }/*** 是否是私钥* <p>* 经统计,规则如下:* 1.私钥长度介于加密算法长度的(1/2-1)* 2.公钥介于加密算法长度的(1/8-1/2)** @param key 秘钥二进制* @return true表示私钥*/
    public boolean isPriKey(byte[] key)
    {if (null != key && key.length > 0){int keyLen = key.length;int maxKeyLen = this.getLen();int minKeyLen = maxKeyLen / PRI_RATIO;return (keyLen < maxKeyLen && keyLen > minKeyLen);}return false;
    }
    
  • 签名和验签判定逻辑:
    public byte[] sign(byte[] data, byte[] key)
    {try{PrivateKey priKey = this.toPriKey(key);Signature signature = Signature.getInstance(this.getSignatureAlg(), this.getProvider());signature.initSign(priKey);signature.update(data);return signature.sign();}catch (Exception e){throw new EncryptionException("failed to signature.", e);}
    }public boolean verify(byte[] data, byte[] key, byte[] sign)
    {try{PublicKey pubKey = this.toPubKey(key);Signature signature = Signature.getInstance(this.getSignatureAlg(), this.getProvider());signature.initVerify(pubKey);signature.update(data);return signature.verify(sign);}catch (Exception e){throw new EncryptionException("failed to verify signature.", e);}
    }
    
  • RSA加密批量验证逻辑
    @Test
    public void encrypt()
    {int[] encLengths = {1024, 2048, 3072, 4096};List<String> paddings = new ArrayList<>();paddings.add("RSA/NONE/NoPadding");paddings.add("RSA/ECB/OAEPPadding");paddings.add("RSA/ECB/PKCS1Padding");paddings.add("RSA/ECB/NoPadding");//公钥加密super.encrypt(encLengths, paddings);//私钥加密super.encrypt(encLengths, paddings, false);
    }@Test
    public void testEncryptAndSign()
    {String initKey = UUID.randomUUID() + new String(RandomUtils.nextBytes(5000), StandardCharsets.UTF_8);int[] encLengths = {1024, 2048, 3072, 4096};List<String> paddings = new ArrayList<>();paddings.add("RSA/ECB/OAEPPadding");paddings.add("RSA/ECB/PKCS1Padding");BaseSingleSignature encryption = new RsaEncryption();for (String padding : paddings){encryption.setPaddingMode(padding);for (int encLen : encLengths){encryption.setEncryptLen(encLen);KeyPair keyPair = encryption.createKey(initKey.getBytes(StandardCharsets.UTF_8));super.testEncryptAndSign(encryption, keyPair.getPrivate().getEncoded(),keyPair.getPublic().getEncoded());}}
    }    
    

    说明:

    1. 上述验证代码中,一旦设置成RSA/NONE/NoPadding或者RSA/ECB/NoPadding,就有大概率会报错,排除掉NoPadding则一切正常;

2. 总结:

  1. BouncyCastle代码整体设计比较优雅,非常容易做到RSA的多种加密长度的兼容。本开源加密组件初期仅支持1024/2048,后面很快就扩展支持了3072/4096加密长度、OAEPPadding填充模式;
  2. NoPadding在较长数据加密时,基本上都会出现异常,初步怀疑是BouncyCastle的bug,但是该模式不安全、也没人使用,就不去跟进解决了;
  3. RSA加密长度3072/4096生成秘钥非常慢;但是各种加密长度下,整体加密耗时约在100ms+(以1000byte字节为例),解密在5ms以内;

http://www.ppmy.cn/news/97498.html

相关文章

盘点一个Python列表的基础题目

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注 回复“书籍”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 随意春芳歇&#xff0c;王孙自可留。 大家好&#xff0c;我是皮皮。 一、前言 前几天在Python最强王者群【eric】问了一个Python列表基础的问题&#x…

Ubuntu安装RabbitMQ server - 在ubuntu+cpolar+rabbitMQ环境下,实现mq服务端远程访问

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 转载自cpolar内网穿透的文章&#xff1a;无公网IP&…

Linux上安装jdk Tomcat mysql redis

1.安装JDk 1.1这里使用xshell中xfxp进行文件的上传&#xff0c;将jdk二进制包上传到Linux服务器上 下载地址&#xff1a;Java Downloads | Oracle 或者这里有下载好的安装包&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1ZSJxBDzDaTwCH2IG-d2Gig 提取码&#xff1a;…

Patroni集群中添加一个PG备库

Patroni集群中添加一个PG备库 当要向Patroni集群中添加一个PG备库时&#xff0c;需要执行以下详细步骤&#xff1a; 编辑Patroni配置文件&#xff1a;打开Patroni配置文件&#xff0c;通常是patroni.yml或postgresql.yml&#xff0c;并找到bootstrap部分。 设置引导方法&…

深入了解云计算:发展历程、服务与部署模型、未来趋势与挑战

开篇博主 bluetata 的观点&#xff1a;PaaS 服务必将是未来10年云计算权重最高的趋势&#xff08;05/02/2023 15:32&#xff09; 文章目录 一、前言二、认识了解云计算2.1 什么是云计算2.1.1 维基百科上的云计算定义2.1.2 NIST 标准云计算定义2.1.3 如果被面试如何解释云计算 2…

【Redis】持久化机制详解:从RDB到AOF,你需要知道的一切

本文目录 RDB&#xff08;默认&#xff09; 自动触发 &#x1f19a; 手动触发 优点 缺点 何时会触发RDB快照 AOF 启用 AOF 配置路径 AOF 文件&#x1f4c3; AOF 的写回策略 AOF 的重写机制 优点 缺点 RDB & AOF 优先级 终极方案&#xff1a;RDB AOF 混合方…

Breakdance评测:改变游戏规则的WordPress网站生成器

您是否厌倦了与复杂的网站生成器打交道&#xff1f;这些网站生成器需要很长时间才能创建您想要的网站&#xff0c;Breakdance Builder改变了网站建设的游戏规则&#xff0c;彻底改变了网站的创建方式。 使用 Breakdance&#xff0c;您可以忘记必须学习复杂的编码语言和花费数小…

leetCode. Populating Next Right Pointers in Each Node

参考资料&#xff1a;《程序员代码面试指南》第二版——二叉树的按层打印与ZigZag打印 Populating Next Right Pointers in Each Node You are given a perfect binary tree where all leaves are on the same level, and every parent has two children. The binary tree has…