Search code examples
c#node.jscryptographybouncycastle

Node encrypt with forge-node and bouncyCastle decryp


I currently generate a key pair and then encrypt the private key with a paraphase as you can see in the following code in node with the node-forge library.

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' });

However, when I try to do client-side decryption in C# with the BouncyCastle library I get the following error.

      public static string DecryptPrivateKey(string privateKey, string passphrase)
      {
        // Utiliza Bouncy Castle para descifrar la clave privada
        AsymmetricCipherKeyPair keyPair;
        using (TextReader reader = new StringReader(privateKey))
        {
          PemReader pemReader = new PemReader(reader, new PasswordFinder(passphrase));
          keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
        }
    
        // Extrae la clave privada en formato 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();
    }
  }

"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"

I have looked for information so that what Forge generates is compatible with Bouncycastle, but I still cannot achieve it. I would be grateful if you could give me a clue or perhaps a node -> c# implementation that has worked for you. Thank you

C#: .net 6.0, BouncyCastle.NetCore 2.2.1 Node: v16.16.0, node-forge ^1.3.1

privateKey:

-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n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\r\n4ipuuNdc4dxqGyUw/2ttDqulBw==\r\n-----END ENCRYPTED PRIVATE KEY-----\r\n

passphrase: "my-new-secret-key"


Solution

  • The posted key is a PEM encoded, encrypted private RSA key in PKCS#8 format. The C# code, on the other hand, is written for the import of a PEM encoded, encrypted private RSA key in PKCS#1 format, which is why the import fails.

    So that the import of the posted key works, the DecryptPrivateKey() method must be changed as follows:

    using Org.BouncyCastle.Crypto.Parameters;
    ...
    public static string DecryptPrivateKey(string privateKey, string passphrase)
    {
        RsaPrivateCrtKeyParameters privKey;
        using (TextReader reader = new StringReader(privateKey))
        {
            PemReader pemReader = new PemReader(reader, new PasswordFinder(passphrase));
            privKey = (RsaPrivateCrtKeyParameters)pemReader.ReadObject();
        }
        StringWriter stringWriter = new StringWriter();
        PemWriter pemWriter = new PemWriter(stringWriter);
        pemWriter.WriteObject(privKey);
        pemWriter.Writer.Flush();
        return stringWriter.ToString();
    }
    

    Note the use of the RsaPrivateCrtKeyParameters type instead of the AsymmetricCipherKeyPair type.

    For completeness: Be aware that the key is exported as PEM encoded private RSA key in PKCS#1 format.


    However, in my environment I cannot reproduce the error message you observed, instead I get the (more plausible) message:

    System.InvalidCastException: Unable to cast object of type 'Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters' to type 'Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair'.
    

    Note that as of .NET 5 there is a built-in support for importing a PEM encoded, encrypted private RSA key in PKCS#8 format, namely RSA.ImportFromEncryptedPem(), as well as for PEM encoding, namely PemEncoding, so BouncyCastle is actually not needed for this.

    using System.Security.Cryptography;
    ...
    RSA rsa = RSA.Create();
    rsa.ImportFromEncryptedPem(privateKey, Encoding.UTF8.GetBytes(passphrase));
    Console.WriteLine(new string(PemEncoding.Write("PRIVATE RSA KEY", rsa.ExportRSAPrivateKey())));