Search code examples
javascriptc#encryptionrsa

How to decrypt RSA encrypted string from JavaScript in C#?


I have an asymmetric RSA key pair stored in two separate files. I want to generate a new symmetric key and encrypt it with public RSA key in my postbuild.js GULP script, so the user cannot access it. Then I want to send it to the C# server, where it would be decrypted and used.

I use the following JavaScript code in Node.js for encryption:

const generateAndEncryptKey = () => {
    const symmetricKey = crypto.randomBytes(32);
    const publicKey = fs.readFileSync("pubkey.pem", "utf8");
    const encryptedSymmetricKey = crypto.publicEncrypt({
        key: publicKey,
        padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
        oaepHash: "sha256",
    }, Buffer.from(symmetricKey)).toString("base64");
    return encryptedSymmetricKey;
}

The above code somehow works and generates a base64 string that I later send to the server. I'm not sure if this is the correct way to do this.

But I'm unable to find a way to decrypt this string in C#. I tried to use the BouncyCastle library and the following code:

public string DecryptKey(string encryptedKey) {
    var privateKey = @"-----BEGIN RSA PRIVATE KEY-----
...shortened...
-----END RSA PRIVATE KEY-----";

    var bytesToDecrypt = Convert.FromBase64String(encryptedKey);
    var decryptEngine = new Pkcs1Encoding(new RsaEngine());
    using (var txtreader = new StringReader(privateKey)) {
        AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)new PemReader(txtreader).ReadObject();
        decryptEngine.Init(false, keyPair.Private);
    }
    var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
    return decrypted;
}

But the ProcessBlock method always throws an InvalidCipherTextException "unknown block type".

Can someone help me to find out what am I doing wrong or point me to another better way of achieving this?


Solution

  • Decryption with the C# code fails because in the NodeJS code OAEP/SHA256 is used as padding and in the C# code PKCS#1 v1.5 padding. For decryption to work, both paddings must be identical. The padding in the C# code can be adapted to that of the NodeJS code as follows:

    var decryptEngine = new OaepEncoding(new RsaEngine(), new Sha256Digest());
    

    Also, the decrypted key must not be UTF-8 decoded as this corrupts the data. Either it is returned as byte[], or if conversion to a string is desired, a suitable binary-to-text encoding such as Base64 or hex must be used.

    With these changes decryption works in the C# code.