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----- MIIJnzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIi0Ogm5Zz728CAggA MB0GCWCGSAFlAwQBKgQQRulEtj6AcqptqFMV4Ar9TQSCCVCbfxCwfm6QGcG8o4LI 7Tay7K2K/8ZBRLjGupJiVc+D1+r4XxOilqJcnoNCjd9gB/7ugwYfQ13ceK2p1HaT DezDAgMFoeEPSNVkmZW6ixk6VtZtA4h/6Za8PMCQUHUztzsrcTvoGKtF3Z4UfNAK 0EVQ1ktQvOm/jHkMXXS/Jdqb6s1zcIxD+BGmqJc51DKtABmUbdrGnhkvhcGysDa4 BqmCOgbPbSuh14ZdzR55Wxg4znYZ/G2HnXKssU7GeYSiu1h/6KEelABG3rCjkuqb RE2szVGeR/DHrwuIzIQyy6Q7Dpidk7ddRO3rt7veRbLVrrggwrmn/u3OpjDHHOh6 IxpUxePpPAWRDIWksbmGhezeCmu5BVv69kYPKvAxTbVh3fHIx8RyU09mOvZNtfpi AbCHamuJLFoavxYVLXiEnpgTv1ilanMMnj9DamKiqk+0A9G16mtRMmsiZpQYl6Uc +81fb5swU1aBbo4r1efIQqb7kCcnbrFrxEvwsNZ6KiNddWTw4qVvIfjflBNdhHg3 oJLglgibJS3j5FhWpiNkhVDRnqDR5ZRcSV8GlCf/Ui68a8m08pNp4wY2Qbc4xAYm Wz1oDnlgfBN5F6Z+fLeWPpwnJa9tfV4iCKODANwdak155v1XHVgponMwnsTtEuvi OnHNPdaWc+SII8EGay4oPKkwaqqBG8zLZ3x0lPPUDvmGr4eXHN2A0ydjwEbCEyxq 7cACL3Xof4LdMWqA7JYwud9FgRurSmVfXPfyiOpBGFq+w9K0+WmPDsbTBeIct6YQ rJP7/gwNqU2c7DZMGhtHEnliJkS/Cb1H9xFxWd2RvCvvcRRk/G+kNepBDTm3pusH hXNbtoMow5FXDJw7p+SOTYUvXn3knkbFQrZWe0DATlyLpuemA7aoXu//py/Fzflb tbaAUj9qZnM46lkqsvvhQSgC7n4+18+VctFNVZ9G3PcKgglGv2HKPzqf9H6Qe4K9 AcGC3UDEh5PWWg7ssPtkyPDTEmc+Gbyl9kFAOzDATPsgL9oEqCdvwyfAFNKB1jCM rv65LP3R/e7VvP1CD3K7cJbldxDia+bKibqQcgq9xhZ/ed/Q0pWKFrJazZMYQm7n ULZZ0PX4SK6DSUPfpDgcMB79VASdw0Ac48Zmr6cHXRSSs+cgIP8E1aQ5PAyRibLO hbQl0Y6CCV2e1k+zgDdd06/ZUVmRD4Sl3iKzCI0Gfs40Eu60QyhqnLh3dBEGa0il PhbmnddABc2FZxldUheM0m7zugdnROHPKsaB49QBnXe9qgddb4M8sbdzebW5oun5 SWV3WZL6YW3/Gdf8BXE9dQzrGkUlWCvOwvqdg1AHQM/vgVKXl7N2PgGeGh1r9TYX Bw4IFJCMVA9p47aRM/EAoUFgDmt70T1qD09KInvu+4hof/wFLr+jPnyZfh+Q8Dtl fsXA5NeVIzVbJZNndIF55scjxDVBhQxsySuWjxHWrwGaE3yaGxkeLXR2dAIUPIEF w6BipotaYaSVDVazPNI4hmFhgl42qFd/rBjqScgP6+A8w3eO+TQSC6MpsJMUuPcl cp1BKraJnq+KH8jGAo65QecFx8i4B6d+DBi4ADuiljuj3JOd54iqOO3IUaz/CArX mocSmzXy3GOBuXj8l/r1qHQfSapTXDxCQsKcqJZL66cU3ByAxA9s0LmzUZbuPvPF ErGzi48E8w6J87Irm044m0buJvXla9FFKZJRGA5KSqzqFPvb8Bdw1n9OPePt2rFO nPtwXurhXQ8/cJZ41CMJ+uH1/7VyEk38Q6mXB5+gkV3vLAWgan80OjMy2l1FLnk/ QdGLHaXijobSVs+OFzJ40ZSFSyPd5RUq3ivtQvY3CYugj+ZaEMlRkZAX8tzOuqKp TcK6DVEunJhRIPMA+I3J+9dyjGh1DW1aYYd/EVhiHTWtRvQqpC5swuqx8cS6ClfK OT5vYR7spl+pX8aYCRw470DRYs7+oHUqs31XxFK493HDp1Mb+g4AiupYCuWymbTY dH2r/Lng3eWrUHTqQaAyV8/Cz/RDoF6XguJpJ4jMfe71bDuaMq6y/EX7MNoJsRwq 2D/EwqDZzipaj243/w6+/k2PEFEcsmNJ6ihC2fa8S5En5q16UwHpnDK+4RN0RvYg JViC2ayQ6Tqx8Xiw1RHcqBzWRCYU7ZWtPiij6YG17K5Eo75Rbd9OJeH+zYB0L+jc oGQ1gXb9duvk+uuNGmiCiU7PqlaYPMMRjFNAG/LMbmET2NmvTIuRb7BEhU9FvjaT PexD0+FNE3vZkiCY3VHPxFEP2z+JToNw/35Ewc/MmxrEotNDYaIe6uIGRtAXWYCi Xb4vSVZvVzw6vKqzIwhbwqaKttyp/TaN4V4Pu6D0fzdE6W12RFZ7PYy1XEb0syIf 95WmAAhHgs7740u5tASLBtjA61Z3w3iGOojB+hCS3Na7dfrSpmOTI00yu56RWPFP UNlmlHg46xlrEwLmfS8AJS4vmC//sYXapuobSVv7ilUUUWkH9H8PvQvutSuprDVh 22FMzqhquzfrHeIIpjXKDsjmF9gxgqSxWTGjgCQ07izaQaRz7oncCPQI3h9/8F5P Ztx9xigHbVfZFEsXPIZLrkeJRvk7h8Lk0AAG1VXvb15WN1cjht9ENikKs9ezfX7o rNsNpeLjKwh+aatPE+6enYYQabLvsu1xrY/rsTJiA0XkqtZODkiPAaZr5csiTwdU tGGGUMyOVoi0S50ICRKbObCAfYZ4PkRxib9MvMKu9HtelC6yXb4NTcSvtRcJpsuP GFkAtgqJ55UpYT8L7VeukJ4Wqi2VmIa3G9cljPbBAYqSbaw/1xzWzKloHbufiEnK CxoMVBWYu4tSF+9cHRAR2LPAKSV+kLhnoHZBqljHSR3Z6l9uE4hDdCDTm1C45g7P mzNi/Azf/T7jIhDRFSeE3wWeMDqiJreublEyIl1FGkuYsLN7VxUmQMSdDJk1Cv7R 2L/FIQxPW+228HQmjobRdyKjRbSGXVJ+cVsavUuy8JN+f5hgSz/+u9djJTsOU2ch tnwRa2d2eA++TwIgw9VK0JzsHlzlPbdP5bHQq1lKTHxIJktARoYB9Fl2pretqSRf O99Rof/i2cp/fPqNDgUx1kgmSjHH/t8HtVYcMpZTqgXnJhWi188AdxUiHMCfdAm+ 4ipuuNdc4dxqGyUw/2ttDqulBw== -----END ENCRYPTED PRIVATE KEY-----
密码:my-new-secret-key
排查与解决方案
核心问题
报错指向Base64格式无效,大概率是私钥字符串在传输或处理过程中引入了非法字符(比如多余的空格、换行符格式不统一、不可见字符),或者node-forge生成的PEM格式细节与BouncyCastle的解析逻辑存在差异。
修正步骤
清理私钥字符串:在C#解密前,先对输入的私钥字符串做格式清理,移除多余空白字符,统一换行符为LF:
var cleanedPrivateKey = Regex.Replace(privateKey, @"\s+", "\n") .Trim() .Replace("\r\n", "\n");这一步可以过滤掉所有非必要的空白,确保Base64部分没有混入非法字符。
修正后的完整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(); } }额外验证建议:
- Node.js端传递私钥时,直接传递原始PEM字符串,避免JSON序列化等操作引入转义字符;
- 用Base64验证工具检查加密私钥的Base64部分是否有效,确认没有非法字符;
- 确认node-forge的加密参数:
encryptRsaPrivateKey默认使用PBKDF2密钥派生,迭代次数10000,哈希算法SHA-256,这些参数BouncyCastle的PemReader默认支持,无需额外配置。
内容的提问来源于stack exchange,提问作者user3324336
相关产品推荐
相关产品推荐

