无法解密Node.js中使用RSA公钥加密的C#中的字符串



我已经用c#创建了xml密钥:

RSACryptoServiceProvider RSA = new RSACryptoServiceProvider (2048);
string str = RSA.ToXmlString (true);
System.IO.File.WriteAllText (@"c:pathtokeypair.xml", str);

然后使用这个在线工具,我将XML密钥转换为pem私钥。然后使用开放SSL,我从私钥中提取了公钥:

openssl rsa -in ./private.pem -pubout -out public.pem 

使用public.pem我正在加密Node中的秘密字符串:

const encryptWithRsa = (secretStringToEncrypt) => {
const publicKeyPath = path.join(appRoot, `${process.env.PUBLIC_KEY_PATH}`);
const publicKeyString = fs.readFileSync(publicKeyPath, 'utf8');
const buffer = Buffer.from(secretStringToEncrypt, 'utf8');
const encrypted = crypto.publicEncrypt({
key: publicKeyString,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256",
}, buffer);
const encryptedString = encrypted.toString('base64');
console.log("encryptWithRsa -> encryptedString", encryptedString);
return encryptedString;
};

然后我试图解密c#中的字符串:

public string RSADecrypt (string encryptedStringFromNode) {
string keyPair = System.IO.File.ReadAllText (@"c:pathtokeypair.xml");
using var csp = new RSACryptoServiceProvider ();
ImportKey (csp, keyPair);
var decrypted = csp.Decrypt (Convert.FromBase64String (encryptedStringFromNode), true);
return Encoding.UTF8.GetString (
decrypted
);
}
public void ImportKey (RSA rsa, string xmlKey) {
var parameters = new RSAParameters ();
var xmlDoc = new XmlDocument ();
xmlDoc.LoadXml (xmlKey);
if (xmlDoc.DocumentElement.Name.Equals ("RSAKeyValue")) {
foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes) {
var value = ToByteArray (node.InnerText);
switch (node.Name) {
case nameof (parameters.Modulus):
parameters.Modulus = value;
break;
case nameof (parameters.Exponent):
parameters.Exponent = value;
break;
case nameof (parameters.P):
parameters.P = value;
break;
case nameof (parameters.Q):
parameters.Q = value;
break;
case nameof (parameters.DP):
parameters.DP = value;
break;
case nameof (parameters.DQ):
parameters.DQ = value;
break;
case nameof (parameters.InverseQ):
parameters.InverseQ = value;
break;
case nameof (parameters.D):
parameters.D = value;
break;
}
}
} else {
throw new Exception ("");
}
rsa.ImportParameters (parameters);
}
public byte[] ToByteArray (string input) {
return string.IsNullOrEmpty (input) ? null : Convert.FromBase64String (input);
}

但当我试图解密Node.js加密的字符串时,我得到了以下错误:

Exception has occurred: CLR/Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException
An exception of type 'Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException' occurred in 
System.Security.Cryptography.Csp.dll but was not handled in user code: 'The parameter is incorrect.'

然而,如果我尝试在这个在线工具上用相同的公钥加密,然后用我的C#代码解密,它会起作用,我会得到原始的未加密字符串。

我在Node.js中用于RSA加密的Padding有问题吗?

首先,很抱歉您太懒了,无法分析代码以找出失败的真正原因。大多数情况下,平台之间的填充不匹配,定义参数总是一种很好的编程技术即使它们被称为";标准";或";默认";因为它们将来可能会改变。

在我的示例中,我使用PKCS1填充RSA加密,这在两个平台上都可用。

第二:我想使用在线编译器进行演示,所以我使用固定/严格的公共和私有RSA密钥。请注意,我使用的是一个512位密钥长度的*UNSECURERSA密钥对(只是为了缩短密钥串(,是的,密钥是示例密钥。

第三:这两种解决方案都是出于教育目的,因为它们没有任何异常处理。

第四:两种解决方案都提供了";全圆形";意味着同时进行加密和解密,C#程序执行使用来自Node.JS-Solution.的加密字符串进行第二次解密

这是Node.JS-program的输出(在此处运行:https://repl.it/@javacrypto/RSAEncryptionNodeJstoC(:

enc: QPCE4WtNPLFGjWvwpN/JTITdr2k9IhGrsohuW0yPue4dQ2Cv63i+LlohmjsSUcnaiB/zbeItGDIx3s11ayBIUA==
dec: The quick brown fox jumps over the lazy dog

如果你运行C#版本,你会得到这个输出(在这里运行:https://repl.it/@javacrypto/RSAecryptionCfromNodeJs-1(

encryptedData: jnAyBvjvjG/NXc/HUmfrTks3SWgeYz3HEksPvk9D9eH/DF0PK6nZu36wTAeZ/fHr3v8dWYwXtT8nXZgvokJJYg==
decryptedData: The quick brown fox jumps over the lazy dog
original Data: VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==
*** enc string from NodeJs ***
decryptedData: The quick brown fox jumps over the lazy dog

Node.JS代码:

const crypto = require('crypto')
// SECURITY WARNING: these sample keys are UNSECURE 512 bit long RSA keys
const publicKey = `-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJn4LYaoLyuT+pD9NKI8wOvPrRjMIxGn
HbqIxUrpGsyj162fEOV4836oCZg0N8HFnt4Vivdjt8/7ZgLjeOygNGUCAwEAAQ==
-----END PUBLIC KEY-----`;
const privateKey = `-----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAmfgthqgvK5P6kP00
ojzA68+tGMwjEacduojFSukazKPXrZ8Q5XjzfqgJmDQ3wcWe3hWK92O3z/tmAuN4
7KA0ZQIDAQABAkEAmCpGy/rxS08e5iXn26LRQvvm5UfyLKMNZWmAGk2QF8cRGFB7
dds/SI9wGTC9xyOoF4N2kWzYdLx+dYbR9lqwbQIhAPFQkWqNIWZiL+YFRhS6jg/+
5+k0CpXpAPelNUFc4pijAiEAo1bchWA5ddDd59FED37QcrakoTXNoxRspFtsLDKE
p1cCIQC6AXRVQTvBg2WQU/hU+geO5Nk1tFuEefm46atHGqW/KQIgXoGggC9Hr8pU
yo9DIPAP7X+Ny5TU0Vm87m/TK9Ni+2sCIGKjhxD/HYAr8+UPJ4aUIHayn3ZZo5DQ
BcShe496ZtPS
-----END PRIVATE KEY-----`;
function encrypt(toEncrypt, relativeOrAbsolutePathToPublicKey) {
//const absolutePath = path.resolve(relativeOrAbsolutePathToPublicKey)
//const publicKey = fs.readFileSync(absolutePath, 'utf8')
const buffer = Buffer.from(toEncrypt, 'utf8')
var constants = require("constants");
const encrypted = crypto.publicEncrypt(
{"key":publicKey, padding:constants.RSA_PKCS1_PADDING}, buffer)
return encrypted.toString('base64')
}
function decrypt(toDecrypt, relativeOrAbsolutePathtoPrivateKey) {
//const absolutePath = path.resolve(relativeOrAbsolutePathtoPrivateKey)
//const privateKey = fs.readFileSync(absolutePath, 'utf8')
const buffer = Buffer.from(toDecrypt, 'base64')
var constants = require("constants");
const decrypted = crypto.privateDecrypt(
{
key: privateKey.toString(),
passphrase: '',
padding:constants.RSA_PKCS1_PADDING,
},
buffer,
)
return decrypted.toString('utf8')
}
const enc = encrypt('The quick brown fox jumps over the lazy dog')
console.log('enc:', enc)
const dec = decrypt(enc)
console.log('dec:', dec)

C#代码:

using System;
using System.Security.Cryptography;
using System.Text;
using System.Collections.Generic;
class RSACSPSample {
static void Main() {
try {
ASCIIEncoding ByteConverter = new ASCIIEncoding();
string dataString = "The quick brown fox jumps over the lazy dog";
// Create byte arrays to hold original, encrypted, and decrypted data.
byte[] originalData = ByteConverter.GetBytes(dataString);
byte[] encryptedData;
byte[] decryptedData;
// SECURITY WARNING: these sample keys are UNSECURE 512 bit long RSA keys
// get private and public key
var publicKey = "<RSAKeyValue><Modulus>mfgthqgvK5P6kP00ojzA68+tGMwjEacduojFSukazKPXrZ8Q5XjzfqgJmDQ3wcWe3hWK92O3z/tmAuN47KA0ZQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
var privateKey = "<RSAKeyValue><Modulus>mfgthqgvK5P6kP00ojzA68+tGMwjEacduojFSukazKPXrZ8Q5XjzfqgJmDQ3wcWe3hWK92O3z/tmAuN47KA0ZQ==</Modulus><Exponent>AQAB</Exponent><P>8VCRao0hZmIv5gVGFLqOD/7n6TQKlekA96U1QVzimKM=</P><Q>o1bchWA5ddDd59FED37QcrakoTXNoxRspFtsLDKEp1c=</Q><DP>ugF0VUE7wYNlkFP4VPoHjuTZNbRbhHn5uOmrRxqlvyk=</DP><DQ>XoGggC9Hr8pUyo9DIPAP7X+Ny5TU0Vm87m/TK9Ni+2s=</DQ><InverseQ>YqOHEP8dgCvz5Q8nhpQgdrKfdlmjkNAFxKF7j3pm09I=</InverseQ><D>mCpGy/rxS08e5iXn26LRQvvm5UfyLKMNZWmAGk2QF8cRGFB7dds/SI9wGTC9xyOoF4N2kWzYdLx+dYbR9lqwbQ==</D></RSAKeyValue>";
RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider(512); // SECURITY WARNING: 512 is UNSECURE
RSAalg.PersistKeyInCsp = false;
// encrypt with xml-public key
RSAalg.FromXmlString(publicKey);
encryptedData = RSAalg.Encrypt(originalData, false);
string encryptedDataBase64 = Convert.ToBase64String(encryptedData);
Console.WriteLine("encryptedData: " + encryptedDataBase64);
// decrypt with xml-private key
RSAalg.FromXmlString(privateKey);
decryptedData = RSAalg.Decrypt(encryptedData, false);
Console.WriteLine("decryptedData: " + ByteArrayToString(decryptedData));
Console.WriteLine("original Data: " + Convert.ToBase64String(originalData));
Console.WriteLine("*** enc string from NodeJs ***");
string b64string = "QPCE4WtNPLFGjWvwpN/JTITdr2k9IhGrsohuW0yPue4dQ2Cv63i+LlohmjsSUcnaiB/zbeItGDIx3s11ayBIUA==";
encryptedData = Convert.FromBase64String(b64string);
decryptedData = RSAalg.Decrypt(encryptedData, false);
Console.WriteLine("decryptedData: " + ByteArrayToString(decryptedData));
}
catch(ArgumentNullException) {
Console.WriteLine("error in data en-/decryption");
}
}

private static string ByteArrayToString(byte[] arr)
{
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
return enc.GetString(arr);
}
}

最新更新