You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NodeJS中使用RSA私钥解密Base64字符串报错排查

解决Node.js中RSA-OAEP解密的PEM格式错误问题

问题背景

我并非NodeJS开发者,在使用给定私钥解密Base64编码字符串时遇到困难。该字符串是通过Java的RSA算法,采用RSA/ECB/OAEPWithSHA-1AndMGF1Padding方式加密的。

我编写了如下代码:

import forge from "node-forge";

function decryptWithPrivateKey(encryptedBase64String: string, privateKeyString: string) {
  try {

    // Remove spaces and newlines from the privateKeyString
    privateKeyString = privateKeyString.replace(/\s/g, '');

    console.log('encryptedBase64String: ' + encryptedBase64String);
    console.log('privateKeyString: ' + privateKeyString);

    const privateKey = forge.pki.privateKeyFromPem(privateKeyString);
    
    const encryptedBuffer = Buffer.from(encryptedBase64String, 'base64');
    const encryptedBytes = forge.util.createBuffer(encryptedBuffer.toString('binary'));

    const decryptedBytes = privateKey.decrypt(encryptedBytes.bytes(), 'RSA-OAEP', {
        md: forge.md.sha1.create(),
        mgf1: {
            md: forge.md.sha1.create()
        }
    });

    return decryptedBytes.toString();
  } catch (error: any) {
    console.log(error.message);
    //throw new Error('Error: ' + error.message);
  }
}

const decryptedString = decryptWithPrivateKey(encryptedPassword, privateKey);
console.log(decryptedString);

运行后始终报错:Invalid PEM formatted message.

我确认私钥和加密字符串都是正确的,在Java环境中可以正常使用,请问我的代码哪里出现了问题?

待解密字符串:
FfKZ2sKQO6sCndb4orGYVIPGBNNsh/eWHu8Ay4MOFfess3B7IBaxBIqq6CUbmgwCbl2D0I38h7OEIO4VI1aC0lH4wMESzxPF5N6724e1SAXPEi0f/F3SA8sMy+mApZtWoUUwmeMPmDap7JII0DxuOngWwiRzwBK7KXc1ww2JLv9aPisBeTWme6QxAMgBpHREQJ9ymxVxRdxa3OyiwsrtgUDORp+z8+cvSLBQwEnjc228YJ0P1n/ggX1O6U/ytzogtVjVniw+TbSHNnZhcjHzPHs6sjYTOZFx3yoYfbh08hiUk3l7jRDXeGYNEgiBUAWY/zhn97VEgcuEYS9TQIEePw==

私钥:
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDNZQq9bWwqyFKnheZOhGabLpPDWKbJeIz6YoJNDF8VlzjVnPIgr5ft5iEanvEl7qYBojq1GKzwa7vTxrLt6AHZXCB7ITfhGp4iMtVVtA+vJgBurSKkPBhVoRpewv1YbAsdEKMZ2wkopHEEuRVi5ngUEV6klccuqGiYZHapzbrw06Wpx7+dmMkB56RuWm18kKx9sMclEBk84LyGuVvkR8W86axy3MWTBPq1wHw8YDG77vBFKgOTdwfp+QsYDoZ+Eph0s9RB6Re1kyX97nXthaLJPi44knnA02VmwwDEGjX7cli2TllRwhs9SOdBheA8iNltU7erXGo9MkSLJwu3Xae5AgMBAAECggEBAKMV2KpCKQHrX52+tvifLm2HVTJlwMD2JFJnR3jwGsuk8C7E2IhVhHgUIzLlAysQS+W+L4k914tqr4/fFJHMhzNUcEH0RU9KeEdFP5k2SMHUlcFLXdc0FEphifO2Nb0wxL5GSIsuHR6VO1v5rd6CitO8Wv6qIqaMd8iQ9JutTAMNTjNG/FWCLkjkWBdtulUOrprlNAmJ4KTKG2ZXVMdjuYFq7++w5Bhouo038jZIDfCksan4CNUAccg03cW9OKl3Vkw6jPCBKWxk/fZChzReb3kot1YQEq8+DDlSTT8xEss9FuF6caX6PRFHgNaeHOq3Eza616dgS87uJbZ8uycbU8UCgYEA5ylrb3GVVALsqI3i+yLF+SaKCxTXwZfpbZes9sUiCaDI0GP3EsJAolrBcA3/io08FTUXpqxLEH7VojJa7oPKTSmo75T5amjp3Xx4NbweFVN4R4oxfR9tgJGnnGCOF2EaLVr3ofWMQK5ObU4VqMXbaKe0dX6vvKrYAHETPuBHlBcCgYEA43bYYICjNN51ez7rHe/RsqYbRbgj8eT504OgOQdkqWAcHEe5YQu1k6bAGXfC9bmVw8ZxITLoQmjJ2ph1lJbw/1UD6VvToXwdC0U/sf4+ICdrA7Al3BS74D2caQBBAy9AL5My7PtECCBBcNCPDMg9PDPLtbv6JAiyugfkdLdzdK8CgYAy1mQfTT6HDGGZrCKoEnyxj606RKoylEMs6/eJCc+ziF3Bnsp+oKPwUL9L8ngps2fsThHWgPu9M/etxR9EZ019bNUPY5b6tPoB0NbrEmUXUZUZPqYlm1zMsrvG42E8eQl1whTF1Ke0reDnnw2aYXEgcgl48ZSajIQWjMerw01anwKBgCsag0hOOB81pKeVNbK3llI3PeXJTi5xiJH3WG5WI1IhX1JQmSV1tZPkPnTVCAHOiD3QJfH3lqG2/8FHsMhWjhakgpg/CWyJe2t+VOKi3hzzu0i4oYQB/dQxeKlBHhC/bHdXeogD2VF840nAi8sNSuVUn16x32XirBOqmTUw6lULAoGBAK41Wu99ZlLRpqRuWTSqyuF2H46Vq3fa0spm4OsULMMZZgVYVev11sFtakQUp4AUw/QsgnEjtZdzg05dUZDDOBsYvyUVYUi1AMGDYI3Zw8UsmzqqH9dNjweO1zAGgVAvh1/HIgi8pwpIyaDOPx9NJnXhOO+pUU3C0z1kSBo9uiLM

问题原因

报错的核心原因是你传入的私钥不符合PEM格式规范:

  • Java输出的原始私钥是Base64编码的裸内容,缺少PEM格式必须的头部-----BEGIN RSA PRIVATE KEY-----和尾部-----END RSA PRIVATE KEY-----
  • 你代码中用replace(/\s/g, '')移除了所有空白字符,但标准PEM格式允许(甚至推荐)将Base64内容分成每行64字符的分段,不过即使不分段,只要有正确的头尾,forge也能识别。

修正方案

  1. 给私钥添加正确的PEM头尾
  2. 不需要移除所有空白,反而要确保头尾和内容的格式正确

修正后的代码

import forge from "node-forge";

function formatPrivateKey(rawKey: string): string {
  // 添加PEM头尾,并按64字符分段(可选,但符合标准格式)
  const header = "-----BEGIN RSA PRIVATE KEY-----";
  const footer = "-----END RSA PRIVATE KEY-----";
  // 先移除原始可能的空白,再分段
  const cleanKey = rawKey.replace(/\s/g, '');
  // 每64字符插入换行
  const chunkedKey = cleanKey.match(/.{1,64}/g)?.join('\n') || cleanKey;
  return `${header}\n${chunkedKey}\n${footer}`;
}

function decryptWithPrivateKey(encryptedBase64String: string, privateKeyString: string) {
  try {
    // 格式化私钥为标准PEM格式
    const formattedPrivateKey = formatPrivateKey(privateKeyString);
    
    console.log('encryptedBase64String: ' + encryptedBase64String);
    console.log('formattedPrivateKey: ' + formattedPrivateKey);

    const privateKey = forge.pki.privateKeyFromPem(formattedPrivateKey);
    
    const encryptedBuffer = Buffer.from(encryptedBase64String, 'base64');
    const encryptedBytes = forge.util.createBuffer(encryptedBuffer.toString('binary'));

    const decryptedBytes = privateKey.decrypt(encryptedBytes.bytes(), 'RSA-OAEP', {
        md: forge.md.sha1.create(),
        mgf1: {
            md: forge.md.sha1.create()
        }
    });

    return decryptedBytes.toString();
  } catch (error: any) {
    console.log(error.message);
    throw new Error('解密失败: ' + error.message);
  }
}

// 替换为你的实际变量
const encryptedPassword = "FfKZ2sKQO6sCndb4orGYVIPGBNNsh/eWHu8Ay4MOFfess3B7IBaxBIqq6CUbmgwCbl2D0I38h7OEIO4VI1aC0lH4wMESzxPF5N6724e1SAXPEi0f/F3SA8sMy+mApZtWoUUwmeMPmDap7JII0DxuOngWwiRzwBK7KXc1ww2JLv9aPisBeTWme6QxAMgBpHREQJ9ymxVxRdxa3OyiwsrtgUDORp+z8+cvSLBQwEnjc228YJ0P1n/ggX1O6U/ytzogtVjVniw+TbSHNnZhcjHzPHs6sjYTOZFx3yoYfbh08hiUk3l7jRDXeGYNEgiBUAWY/zhn97VEgcuEYS9TQIEePw==";
const privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDNZQq9bWwqyFKnheZOhGabLpPDWKbJeIz6YoJNDF8VlzjVnPIgr5ft5iEanvEl7qYBojq1GKzwa7vTxrLt6AHZXCB7ITfhGp4iMtVVtA+vJgBurSKkPBhVoRpewv1YbAsdEKMZ2wkopHEEuRVi5ngUEV6klccuqGiYZHapzbrw06Wpx7+dmMkB56RuWm18kKx9sMclEBk84LyGuVvkR8W86axy3MWTBPq1wHw8YDG77vBFKgOTdwfp+QsYDoZ+Eph0s9RB6Re1kyX97nXthaLJPi44knnA02VmwwDEGjX7cli2TllRwhs9SOdBheA8iNltU7erXGo9MkSLJwu3Xae5AgMBAAECggEBAKMV2KpCKQHrX52+tvifLm2HVTJlwMD2JFJnR3jwGsuk8C7E2IhVhHgUIzLlAysQS+W+L4k914tqr4/fFJHMhzNUcEH0RU9KeEdFP5k2SMHUlcFLXdc0FEphifO2Nb0wxL5GSIsuHR6VO1v5rd6CitO8Wv6qIqaMd8iQ9JutTAMNTjNG/FWCLkjkWBdtulUOrprlNAmJ4KTKG2ZXVMdjuYFq7++w5Bhouo038jZIDfCksan4CNUAccg03cW9OKl3Vkw6jPCBKWxk/fZChzReb3kot1YQEq8+DDlSTT8xEss9FuF6caX6PRFHgNaeHOq3Eza616dgS87uJbZ8uycbU8UCgYEA5ylrb3GVVALsqI3i+yLF+SaKCxTXwZfpbZes9sUiCaDI0GP3EsJAolrBcA3/io08FTUXpqxLEH7VojJa7oPKTSmo75T5amjp3Xx4NbweFVN4R4oxfR9tgJGnnGCOF2EaLVr3ofWMQK5ObU4VqMXbaKe0dX6vvKrYAHETPuBHlBcCgYEA43bYYICjNN51ez7rHe/RsqYbRbgj8eT504OgOQdkqWAcHEe5YQu1k6bAGXfC9bmVw8ZxITLoQmjJ2ph1lJbw/1UD6VvToXwdC0U/sf4+ICdrA7Al3BS74D2caQBBAy9AL5My7PtECCBBcNCPDMg9PDPLtbv6JAiyugfkdLdzdK8CgYAy1mQfTT6HDGGZrCKoEnyxj606RKoylEMs6/eJCc+ziF3Bnsp+oKPwUL9L8ngps2fsThHWgPu9M/etxR9EZ019bNUPY5b6tPoB0NbrEmUXUZUZPqYlm1zMsrvG42E8eQl1whTF1Ke0reDnnw2aYXEgcgl48ZSajIQWjMerw01anwKBgCsag0hOOB81pKeVNbK3llI3PeXJTi5xiJH3WG5WI1IhX1JQmSV1tZPkPnTVCAHOiD3QJfH3lqG2/8FHsMhWjhakgpg/CWyJe2t+VOKi3hzzu0i4oYQB/dQxeKlBHhC/bHdXeogD2VF840nAi8sNSuVUn16x32XirBOqmTUw6lULAoGBAK41Wu99ZlLRpqRuWTSqyuF2H46Vq3fa0spm4OsULMMZZgVYVev11sFtakQUp4AUw/QsgnEjtZdzg05dUZDDOBsYvyUVYUi1AMGDYI3Zw8UsmzqqH9dNjweO1zAGgVAvh1/HIgi8pwpIyaDOPx9NJnXhOO+pUU3C0z1kSBo9uiLM";

const decryptedString = decryptWithPrivateKey(encryptedPassword, privateKey);
console.log('解密结果:', decryptedString);

验证说明

  • 修正后的代码会将原始裸Base64私钥转换为标准PEM格式,解决Invalid PEM formatted message.错误
  • 你原来的解密逻辑(RSA-OAEP参数配置)是正确的,和Java的RSA/ECB/OAEPWithSHA-1AndMGF1Padding完全匹配,所以格式修正后就能正常解密。

内容的提问来源于stack exchange,提问作者Vinicius Garcia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 12:24:51