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

Node.js forge加密私钥后,C# BouncyCastle解密失败求助

Node.js node-forge加密RSA私钥与C# BouncyCastle跨平台兼容问题解决

问题场景

在Node.js(v16.16.0,node-forge ^1.3.1)中使用node-forge生成RSA密钥对,并通过AES256算法加密私钥,代码如下:

const keypair = forge.pki.rsa.generateKeyPair({ bits: 4096 });
const publicKey = forge.pki.publicKeyToPem(keypair.publicKey);
const privateKey = forge.pki.encryptRsaPrivateKey(keypair.privateKey, passphrase, { algorithm: 'aes256' });

但在C#(.NET 6.0,BouncyCastle.NetCore 2.2.1)端用BouncyCastle解密时,抛出错误:

The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters

C#解密代码如下:

public static string DecryptPrivateKey(string privateKey, string passphrase)
{
    // 使用Bouncy Castle解密私钥
    AsymmetricCipherKeyPair keyPair;
    using (TextReader reader = new StringReader(privateKey))
    {
        PemReader pemReader = new PemReader(reader, new PasswordFinder(passphrase));
        keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
    }

    // 提取PEM格式的私钥
    StringWriter stringWriter = new StringWriter();
    PemWriter pemWriter = new PemWriter(stringWriter);
    pemWriter.WriteObject(keyPair.Private);
    pemWriter.Writer.Flush();

    return stringWriter.ToString();
}

private class PasswordFinder : IPasswordFinder
{
    private readonly string password;

    public PasswordFinder(string password)
    {
        this.password = password;
    }

    public char[] GetPassword()
    {
        return password.ToCharArray();
    }
}

加密后的私钥:

-----BEGIN ENCRYPTED PRIVATE KEY-----
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
4ipuuNdc4dxqGyUw/2ttDqulBw==
-----END ENCRYPTED PRIVATE KEY-----

密码:my-new-secret-key

排查与解决方案

核心问题

报错指向Base64格式无效,大概率是私钥字符串在传输或处理过程中引入了非法字符(比如多余的空格、换行符格式不统一、不可见字符),或者node-forge生成的PEM格式细节与BouncyCastle的解析逻辑存在差异。

修正步骤

  1. 清理私钥字符串:在C#解密前,先对输入的私钥字符串做格式清理,移除多余空白字符,统一换行符为LF:

    var cleanedPrivateKey = Regex.Replace(privateKey, @"\s+", "\n")
        .Trim()
        .Replace("\r\n", "\n");
    

    这一步可以过滤掉所有非必要的空白,确保Base64部分没有混入非法字符。

  2. 修正后的完整C#代码:

    using System.IO;
    using System.Text.RegularExpressions;
    using Org.BouncyCastle.Crypto;
    using Org.BouncyCastle.OpenSsl;
    
    public static string DecryptPrivateKey(string privateKey, string passphrase)
    {
        // 清理私钥格式,移除多余空白并统一换行符
        var cleanedPrivateKey = Regex.Replace(privateKey, @"\s+", "\n")
            .Trim()
            .Replace("\r\n", "\n");
    
        AsymmetricCipherKeyPair keyPair;
        using (TextReader reader = new StringReader(cleanedPrivateKey))
        {
            PemReader pemReader = new PemReader(reader, new PasswordFinder(passphrase));
            keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
        }
    
        // 导出未加密的PEM格式私钥
        using (StringWriter stringWriter = new StringWriter())
        {
            PemWriter pemWriter = new PemWriter(stringWriter);
            pemWriter.WriteObject(keyPair.Private);
            pemWriter.Writer.Flush();
            return stringWriter.ToString();
        }
    }
    
    private class PasswordFinder : IPasswordFinder
    {
        private readonly string _password;
    
        public PasswordFinder(string password)
        {
            _password = password;
        }
    
        public char[] GetPassword()
        {
            return _password.ToCharArray();
        }
    }
    
  3. 额外验证建议:

    • Node.js端传递私钥时,直接传递原始PEM字符串,避免JSON序列化等操作引入转义字符;
    • 用Base64验证工具检查加密私钥的Base64部分是否有效,确认没有非法字符;
    • 确认node-forge的加密参数:encryptRsaPrivateKey默认使用PBKDF2密钥派生,迭代次数10000,哈希算法SHA-256,这些参数BouncyCastle的PemReader默认支持,无需额外配置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 14:02:32