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

news/2024/7/3 13:28:05 标签: 开源, java, rsa, 加密, 签名, 分段加密, 解密

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的加解密实现

  • 解密核心逻辑

    java">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实现及其应用 中有介绍),上述核心逻辑是无法商用的,可商用的逻辑如下:

    java">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生成秘钥即转换实现

  • 秘钥生成逻辑
    java">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);
        }
    }
    
  • 公钥、私钥反向生成逻辑
    java">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私钥的判定逻辑:
    java">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;
    }
    
  • 签名和验签判定逻辑:
    java">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加密批量验证逻辑
    java">@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.niftyadmin.cn/n/367913.html

相关文章

Oracle数据库表空间数据删除以及数据库重启

-删除空的表空间&#xff0c;但是不包含物理文件 drop tablespace tablespace_name; –删除非空表空间&#xff0c;但是不包含物理文件 drop tablespace tablespace_name including contents; –删除空表空间&#xff0c;包含物理文件 drop tablespace tablespace_name includi…

ChatGPT简介

零、ChatGPT-更新日志 创建 创建 2023年2月9日 2023年2月9日 介绍 介绍 2023年2月9日 2023年2月9日 优化 优化 2023年3月26-28日 2023年3月26-28日 新增 新增 2023年3月29日 2023年3月29日 新增 新增 2023年3月29日 2023年3月29日 优化 优化 2023年3月30日 2023年3月30日 新增 …

有序表2:跳表

跳表是一个随机化的数据结构&#xff0c;可以被看做二叉树的一个变种&#xff0c;它在性能上和红黑树&#xff0c;AVL树不相上下&#xff0c;但是跳表的原理非常简单&#xff0c;目前在Redis和LeveIDB中都有用到。 它采用随机技术决定链表中哪些节点应增加向前指针以及在该节点…

SaaS CRM系统的优势,与本地部署相比哪个更方便?

CRM系统主要有两种部署方式&#xff0c;分别是云部署和本地部署。那么&#xff0c;本地部署CRM软件真的比SaaS CRM好吗&#xff1f;本文将为您分析两种部署方式的区别&#xff0c;来为您答疑解惑。 云部署CRM的含义&#xff1a; 云部署CRM系统是指将CRM系统部署在云端&#x…

Linux-0.11 文件系统super.c详解

Linux-0.11 文件系统super.c详解 模块简介 该模块主要包含了对超级块的一些读写操作。 函数详解 lock_super static void lock_super(struct super_block * sb)该函数的作用是锁定bh块。 cli();//关中断while (sb->s_lock)//如果已经被锁定sleep_on(&(sb->s_wai…

Centos7中mysql安装配置

前提&#xff1a;先关闭防火墙或开启tcp的3306端口 1、查看服务器上是否有现成的安装包 yum list mysql* 2、去mysql官网的yum资源库找到对应的rpm文件的下载链接 确定系统版本 cat /etc/redhat-release 到mysql官网复制对应版本的资源下载链接 MySQL :: Download MySQL Yum…

【Netty】Promise 源码分析(十七)

文章目录 前言一、Promise 接口二、Netty 的 DefaultPromise2.1、设置任务的成功或失败2.2、获取 Future 任务执行结果和添加监听事件 三、Netty 的 DefaultChannelPromise总结 前言 回顾Netty系列文章&#xff1a; Netty 概述&#xff08;一&#xff09;Netty 架构设计&…

【MySQL学习1:单表】

之前做的笔记都在有道云&#xff0c;之后会一点点将以前的笔记分享出来~ MySQL学习1&#xff1a;单表查询 1. 变量起别名2. 去重3. 空值NULL4. 使用着重号5. 查询常数6. 显示表结果信息 describe 或 desc7. 运算符&#xff08;1&#xff09;安全等于运算符 <>&#xff08…