如何在 Java 中解密 PEM 格式的密码保护私钥

发布时间 - 2026-01-22 00:00:00    点击率:

本文详解如何正确解析并解密 aws 导出的、以 pem 封装的密码加密私钥(begin encrypted private key),避免因直接使用 base64 原文导致的 der 解析异常(如 `ioexception: extra data given to dervalue constructor`)。

在使用 AWS ACM 导出证书时,私钥默认以 PEM 格式导出,并采用 PKCS#8 加密标准(即 -----BEGIN ENCRYPTED PRIVATE KEY----- 开头),其内容为 Base64 编码的 DER 结构。关键错误在于:直接对原始 PEM 字符串调用 getBytes() 并传入 EncryptedPrivateKeyInfo 构造器——这会将 PEM 头尾和换行符一并作为 DER 输入,导致 ASN.1 解析失败(报错 extra data given to DerValue constructor)。

正确做法是先剥离 PEM 封装,提取纯 DER 字节内容

。推荐使用 Bouncy Castle 的 PemReader(需引入 bcprov-jdk15on 和 bcpkix-jdk15on 依赖)完成解析:



    org.bouncycastle
    bcprov-jdk15on
    1.70


    org.bouncycastle
    bcpkix-jdk15on
    1.70

以下是完整、健壮的解密方法实现:

import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.pkcs.*;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.io.*;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.KeySpec;

public PrivateKey readPrivateKey(String privateKeyPem, String passphrase) throws Exception {
    // 1. 使用 PemReader 提取 PEM 内容(自动跳过头尾与换行)
    try (PemReader pemReader = new PemReader(new StringReader(privateKeyPem))) {
        PemObject pemObject = pemReader.readPemObject();
        if (pemObject == null || !"ENCRYPTED PRIVATE KEY".equals(pemObject.getType())) {
            throw new IllegalArgumentException("Invalid PEM type: expected 'ENCRYPTED PRIVATE KEY'");
        }

        // 2. 获取原始 DER 字节(已解码 Base64)
        byte[] derBytes = pemObject.getContent();

        // 3. 构建 EncryptedPrivateKeyInfo(此时输入为纯净 DER)
        EncryptedPrivateKeyInfo encryptedInfo = new EncryptedPrivateKeyInfo(derBytes);

        // 4. 初始化解密 Cipher
        String algName = encryptedInfo.getAlgName();
        Cipher cipher = Cipher.getInstance(algName);
        PBEKeySpec keySpec = new PBEKeySpec(passphrase.toCharArray());
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algName);
        cipher.init(Cipher.DECRYPT_MODE,
                keyFactory.generateSecret(keySpec),
                encryptedInfo.getAlgParameters());

        // 5. 解密并生成 PrivateKey
        KeySpec pkcs8KeySpec = encryptedInfo.getKeySpec(cipher);
        KeyFactory kf = KeyFactory.getInstance("RSA"); // 若为 EC 私钥,改为 "EC"
        return kf.generatePrivate(pkcs8KeySpec);
    }
}

⚠️ 注意事项:

  • 密钥类型适配:若导出的是 ECC 证书(如 secp256r1),需将 KeyFactory.getInstance("RSA") 替换为 KeyFactory.getInstance("EC");Bouncy Castle 也支持 "ECDSA" 别名。
  • 密码编码:确保 passphrase 与 AWS 导出时输入的密码完全一致(区分大小写、空格、特殊字符)。
  • 异常处理:生产环境应捕获具体异常(如 InvalidKeySpecException, BadPaddingException)并提供有意义的错误提示,而非静默吞掉异常。
  • Bouncy Castle 注册(可选但推荐):若使用较新 JDK,建议显式注册 BC 提供者:
    Security.addProvider(new BouncyCastleProvider());

该方案通过标准 PEM 解析流程,严格遵循 PKCS#8 加密私钥规范,彻底规避了原始代码中因 PEM/DER 混淆引发的 ASN.1 解析错误,适用于所有符合 RFC 7468 的加密私钥导入场景。


# java  # 编码  # 字节  # ssl  # crypto 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 详解Nginx + Tomcat 反向代理 负载均衡 集群 部署指南  Laravel怎么多语言本地化设置_Laravel语言包翻译与Locale动态切换【手册】  Laravel如何生成和使用数据填充?(Seeder和Factory示例)  如何在IIS7中新建站点?详细步骤解析  Laravel如何保护应用免受CSRF攻击?(原理和示例)  今日头条微视频如何找选题 今日头条微视频找选题技巧【指南】  教你用AI将一段旋律扩展成一首完整的曲子  制作网站软件推荐手机版,如何制作属于自己的手机网站app应用?  如何快速登录WAP自助建站平台?  html5如何设置样式_HTML5样式设置方法与CSS应用技巧【教程】  Python文件流缓冲机制_IO性能解析【教程】  音乐网站服务器如何优化API响应速度?  iOS发送验证码倒计时应用  如何为不同团队 ID 动态生成多个独立按钮  JavaScript如何实现类型判断_typeof和instanceof有什么区别  如何用PHP快速搭建高效网站?分步指南  PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)  微信小程序 HTTPS报错整理常见问题及解决方案  高防服务器租用如何选择配置与防御等级?  HTML 中动态设置元素 name 属性的正确语法详解  昵图网官方站入口 昵图网素材图库官网入口  如何在IIS管理器中快速创建并配置网站?  Android中AutoCompleteTextView自动提示  javascript基于原型链的继承及call和apply函数用法分析  奇安信“盘古石”团队突破 iOS 26.1 提权  使用C语言编写圣诞表白程序  Windows10怎样连接蓝牙设备_Windows10蓝牙连接步骤【教程】  详解Huffman编码算法之Java实现  制作旅游网站html,怎样注册旅游网站?  百度浏览器网页无法复制文字怎么办 百度浏览器复制修复  Python高阶函数应用_函数作为参数说明【指导】  网站建设要注意的标准 促进网站用户好感度!  Laravel怎么处理异常_Laravel自定义异常处理与错误页面教程  Win11怎么设置默认图片查看器_Windows11照片应用关联设置  如何在万网开始建站?分步指南解析  Android中Textview和图片同行显示(文字超出用省略号,图片自动靠右边)  Laravel数据库迁移怎么用_Laravel Migration管理数据库结构的正确姿势  太平洋网站制作公司,网络用语太平洋是什么意思?  Laravel如何实现数据导出到CSV文件_Laravel原生流式输出大数据量CSV【方案】  Laravel distinct去重查询_Laravel Eloquent去重方法  Laravel怎么判断请求类型_Laravel Request isMethod用法  千库网官网入口推荐 千库网设计创意平台入口  如何在景安云服务器上绑定域名并配置虚拟主机?  Laravel如何实现数据导出到PDF_Laravel使用snappy生成网页快照PDF【方案】  猎豹浏览器开发者工具怎么打开 猎豹浏览器F12调试工具使用【前端必备】  无锡营销型网站制作公司,无锡网选车牌流程?  高端智能建站公司优选:品牌定制与SEO优化一站式服务  Android实现代码画虚线边框背景效果  如何在IIS中新建站点并配置端口与物理路径?  深圳网站制作平台,深圳市做网站好的公司有哪些?