0


JAVA 解密被密码保护的pem私钥文件

0、说明:只能读取PKCS8格式的加密私钥

1、解密使用的依赖:

        <!-- RAS私钥解密相关 -->
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.68</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpkix-jdk15on</artifactId>
            <version>1.68</version>
        </dependency>
        <!-- 工具 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.22</version>
        </dependency>

JAVA版本:1.8.0_261

SpringBoot版本:2.6.4

2、代码:

读取PKCS8格式的秘钥


    private static final String PRIVATE_KEY_FILE = "prikey.pem";

    /**
     * 读取带密码保护的私钥文件
     * @param passwd 保护密码
     * @return 私钥的byte数组,可以通过Base64编码转为字符串
     */
    public static byte[] readPrivateKeyFromPem(String passwd) throws Exception {
        File file = ResourceUtils.getFile("classpath:".concat(PRIVATE_KEY_FILE));
        String encrypted = FileUtil.readString(file, StandardCharsets.UTF_8);
        // 读取带密码保护的私钥:https://cloud.tencent.com/developer/article/2186682?from=15425
        PrivateKeyInfo pki;
        try (PEMParser pemParser = new PEMParser(new StringReader(encrypted))) {
            Object o = pemParser.readObject();
            PKCS8EncryptedPrivateKeyInfo epki = (PKCS8EncryptedPrivateKeyInfo) o;
            // 手动添加BC的安全模式:https://blog.csdn.net/Roy_70/article/details/70842322
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            JcePKCSPBEInputDecryptorProviderBuilder builder =
                    new JcePKCSPBEInputDecryptorProviderBuilder().setProvider("BC");
            InputDecryptorProvider idp = builder.build(passwd.toCharArray());
            pki = epki.decryptPrivateKeyInfo(idp);
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
            return converter.getPrivateKey(pki).getEncoded();
        }
     }

秘钥文件放置在模块的resource目录下,也可以自行更改文件读取方式

3、另外附上自己写的完整的RSA分段加解密

import cn.hutool.core.codec.Base64Decoder;
import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;

import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder;
import org.springframework.util.ResourceUtils;

import java.io.File;
import java.io.StringReader;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Arrays;

public class RsaBigDataUtil {

    private static final String PUBLIC_KEY_FILE = "pubkey.pem";
    private static final String PRIVATE_KEY_FILE = "prikey.pem";

    /**
     * 私钥分段解密
     *
     * @param privateKey 私钥
     * @param context    密文
     * @param len        公钥长度
     * @return 解密后的明文比特流
     */
    public static byte[] decrypt(byte[] privateKey, byte[] context, int len) {
        RSA rsa = new RSA(privateKey, null);
        ByteBuffer srcByteBuffer = ByteBuffer.wrap(context);
        int splitCount = (int) Math.ceil((double) context.length / (double) (len * 2));        // 向上取整得分段数
        ByteBuffer resByteBuffer = ByteBuffer.allocate(splitCount * len);
        int splitIndex = 1;
        int length = 0;
        while (splitIndex <= splitCount) {
            // 当本次被解密数据的最后一位长度扩张后会超过缓冲区长度的时候减小数据容器的长度
            byte[] temp_mi = new byte[srcByteBuffer.limit() < splitIndex * len * 2 ? len : len * 2];
            srcByteBuffer.get(temp_mi, 0, temp_mi.length);
            byte[] temp_min = rsa.decrypt(temp_mi, KeyType.PrivateKey);
            resByteBuffer.put(temp_min, 0, temp_min.length);
            length += temp_min.length;
            splitIndex++;
        }
        return Arrays.copyOfRange(resByteBuffer.array(), 0, length);
    }

    /**
     * 公钥分段加密
     *
     * @param publicKey 公钥
     * @param context   明文
     * @param len       公钥长度
     * @return 加密后的密文比特数组
     */
    public static byte[] encrypt(byte[] publicKey, byte[] context, int len) {
        RSA rsa = new RSA(null, publicKey);
        ByteBuffer srcByteBuffer = ByteBuffer.wrap(context);
        int splitCount = (int) Math.ceil((double) context.length / (double) len);
        // 由于加密后密文会膨胀,但最大大小是切片数*两倍的块大小
        ByteBuffer resByteBuffer = ByteBuffer.allocate(splitCount * len * 2);
        int splitIndex = 1;
        int length = 0;
        while (splitIndex <= splitCount) {
            byte[] temp_min = new byte[splitCount == splitIndex ? (context.length - (splitCount - 1) * len) : len];
            srcByteBuffer.get(temp_min, 0, temp_min.length);
            byte[] temp_mi = rsa.encrypt(temp_min, KeyType.PublicKey);
            resByteBuffer.put(temp_mi, 0, temp_mi.length);
            length += temp_mi.length;
            splitIndex++;
        }
        return Arrays.copyOfRange(resByteBuffer.array(), 0, length);
    }

    /**
     * 读取公钥文件
     * @return 公钥的byte数组,可以通过Base64编码转为字符串
     * */
    public static byte[] readPublicKeyFromPem() throws Exception {
        File file = ResourceUtils.getFile("classpath:".concat(PUBLIC_KEY_FILE));
        String encrypted = FileUtil.readString(file, StandardCharsets.UTF_8);
        encrypted = encrypted.replace("-----BEGIN PUBLIC KEY-----","");
        encrypted = encrypted.replace("-----END PUBLIC KEY-----","");
        return Base64Decoder.decode(encrypted);
    }

    /**
     * 读取带密码保护的私钥文件
     * @param passwd 保护密码
     * @return 私钥的byte数组,可以通过Base64编码转为字符串
     */
    public static byte[] readPrivateKeyFromPem(String passwd) throws Exception {
        File file = ResourceUtils.getFile("classpath:".concat(PRIVATE_KEY_FILE));
        String encrypted = FileUtil.readString(file, StandardCharsets.UTF_8);
        // 读取带密码保护的私钥:https://cloud.tencent.com/developer/article/2186682?from=15425
        PrivateKeyInfo pki;
        try (PEMParser pemParser = new PEMParser(new StringReader(encrypted))) {
            Object o = pemParser.readObject();
            PKCS8EncryptedPrivateKeyInfo epki = (PKCS8EncryptedPrivateKeyInfo) o;
            // 手动添加BC的安全模式:https://blog.csdn.net/Roy_70/article/details/70842322
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            JcePKCSPBEInputDecryptorProviderBuilder builder =
                    new JcePKCSPBEInputDecryptorProviderBuilder().setProvider("BC");
            InputDecryptorProvider idp = builder.build(passwd.toCharArray());
            pki = epki.decryptPrivateKeyInfo(idp);
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
            return converter.getPrivateKey(pki).getEncoded();
        }
    }
}

代码只实现了基础功能,还有很多不足,欢迎各位大佬指正

4、附上一个基础的使用案例

    void encrypt() throws Exception {
        String content = "114514";
        System.out.println("原文:"+content);
        byte[] mi = RsaBigDataUtil.encrypt(RsaBigDataUtil.readPublicKeyFromPem(), content.getBytes(StandardCharsets.UTF_8), 256);
        String base64 = Base64Encoder.encode(mi);
        System.out.println("加密:"+base64);
        byte[] min = RsaBigDataUtil.decrypt(RsaBigDataUtil.readPrivateKeyFromPem("yourPasswd"),mi,256);
        System.out.println("解密:"+new String(min, StandardCharsets.UTF_8));
    }

测试用的密钥对可以在RSA加密/解密 - 在线工具 (try8.cn)在线生成

下载好的秘钥放置在项目的resource目录下(或者自己重写文件读取方法)

记得将代码中的文件名替换为自己的

标签: java 安全 springboot

本文转载自: https://blog.csdn.net/qq_26747159/article/details/128576347
版权归原作者 清风略过北极光 所有, 如有侵权,请联系我们删除。

“JAVA 解密被密码保护的pem私钥文件”的评论:

还没有评论