C# BouncyCastle - RSA Encryption with Public/Private keys

37,657

Solution 1

I was using an incorrect Public Key.. and the test that proved the Private and Public keys matched was using the correct Public Key.

The above code works perfectly as is, as long as you get the keys right!

Solution 2

There are some errors in OP's code. I made few changes. Here is what I got running.

public class TFRSAEncryption
{
    public string RsaEncryptWithPublic(string clearText, string publicKey)
    {
        var bytesToEncrypt = Encoding.UTF8.GetBytes(clearText);

        var encryptEngine = new Pkcs1Encoding(new RsaEngine());

        using (var txtreader = new StringReader(publicKey))
        {
            var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();

            encryptEngine.Init(true, keyParameter);
        }

        var encrypted = Convert.ToBase64String(encryptEngine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length));
        return encrypted;

    }

    public string RsaEncryptWithPrivate(string clearText, string privateKey)
    {
        var bytesToEncrypt = Encoding.UTF8.GetBytes(clearText);

        var encryptEngine = new Pkcs1Encoding(new RsaEngine());

        using (var txtreader = new StringReader(privateKey))
        {
            var keyPair = (AsymmetricCipherKeyPair)new PemReader(txtreader).ReadObject();

            encryptEngine.Init(true, keyPair.Private);
        }

        var encrypted = Convert.ToBase64String(encryptEngine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length));
        return encrypted;
    }


    // Decryption:

    public string RsaDecryptWithPrivate(string base64Input, string privateKey)
    {
        var bytesToDecrypt = Convert.FromBase64String(base64Input);

        AsymmetricCipherKeyPair keyPair;
        var decryptEngine = new Pkcs1Encoding(new RsaEngine());

        using (var txtreader = new StringReader(privateKey))
        {
            keyPair = (AsymmetricCipherKeyPair)new PemReader(txtreader).ReadObject();

            decryptEngine.Init(false, keyPair.Private);
        }

        var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
        return decrypted;
    }

    public string RsaDecryptWithPublic(string base64Input, string publicKey)
    {
        var bytesToDecrypt = Convert.FromBase64String(base64Input);

        var decryptEngine = new Pkcs1Encoding(new RsaEngine());

        using (var txtreader = new StringReader(publicKey))
        {
            var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();

            decryptEngine.Init(false, keyParameter);
        }

        var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
        return decrypted;
    }
}

class Program
{

    static void Main(string[] args)
    {
        // Set up 
        var input = "Perceived determine departure explained no forfeited";
        var enc = new TFRSAEncryption();
        var publicKey = "-----BEGIN PUBLIC KEY----- // Base64 string omitted // -----END PUBLIC KEY-----";
        var privateKey = "-----BEGIN PRIVATE KEY----- // Base64 string omitted// -----END PRIVATE KEY-----";

        // Encrypt it
        var encryptedWithPublic = enc.RsaEncryptWithPublic(input, publicKey);

        var encryptedWithPrivate = enc.RsaEncryptWithPrivate(input, privateKey);

        // Decrypt
        var output1 = enc.RsaDecryptWithPrivate(encryptedWithPublic, privateKey);

        var output2 = enc.RsaDecryptWithPublic(encryptedWithPrivate, publicKey);

        Console.WriteLine(output1 == output2 && output2 == input);

        Console.Read();
    }
}

Solution 3

I tried @Morio's solution but was getting some exceptions. The first one was

-----END PUBLIC KEY not found

Which I was able to fix by formatting both keys properly

publicKey = $"-----BEGIN PUBLIC KEY-----\n{publicKey}\n-----END PUBLIC KEY-----\n";

The second error was while trying to decrypt the text. Unable to cast object of type 'Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters' to type 'Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair'.
Which I resolved by casting to the appropriate type.

var keyPair = (RsaPrivateCrtKeyParameters)new PemReader(txtreader).ReadObject();
Share:
37,657

Related videos on Youtube

scudsucker
Author by

scudsucker

Updated on August 04, 2021

Comments

  • scudsucker
    scudsucker almost 3 years

    I need to encrypt data in C# in order to pass it to Java. The Java code belongs to a 3rd party but I have been given the relevant source, so I decided that as the Java uses the Bouncy Castle libs, I will use the C# port.

    Decryption works fine. However, decryption works only when I use the encrypt using the private key, and not with the public key. When using the public key, decryption fails with unknown block type.

    Obviously the encryption inside the RsaEncryptWithPrivate uses the public key when encrypting, so I do not get why the two encryption methods are not functionally identical:

    using Org.BouncyCastle.Crypto;
    using Org.BouncyCastle.Crypto.Encodings;
    using Org.BouncyCastle.Crypto.Engines;
    using Org.BouncyCastle.OpenSsl;
    
    public class EncryptionClass
    {       
        public string RsaEncryptWithPublic(string clearText
            , string publicKey)
        {
            var bytesToEncrypt = Encoding.UTF8.GetBytes(clearText);
    
            var encryptEngine = new Pkcs1Encoding(new RsaEngine());
    
            using (var txtreader = new StringReader(publicKey))
            {
                var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();
    
                encryptEngine.Init(true, keyParameter);
            }
    
            var encrypted = Convert.ToBase64String(encryptEngine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length));
            return encrypted;
    
        }
    
        public string RsaEncryptWithPrivate(string clearText
            , string privateKey)
        {
            var bytesToEncrypt = Encoding.UTF8.GetBytes(clearText);
    
            var encryptEngine = new Pkcs1Encoding(new RsaEngine());
    
            using (var txtreader = new StringReader(privateKey))
            {
                var keyPair = (AsymmetricCipherKeyPair)new PemReader(txtreader).ReadObject();
    
                encryptEngine.Init(true, keyPair.Public);
            }
    
            var encrypted= Convert.ToBase64String(encryptEngine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length));
            return encrypted;
        }
    
    
        // Decryption:
    
        public string RsaDecrypt(string base64Input
            , string privateKey)
        {
            var bytesToDecrypt = Convert.FromBase64String(base64Input);
    
            //get a stream from the string
            AsymmetricCipherKeyPair keyPair;
            var decryptEngine = new Pkcs1Encoding(new RsaEngine());
    
            using ( var txtreader = new StringReader(privateKey) )
            {
                keyPair = (AsymmetricCipherKeyPair) new PemReader(txtreader).ReadObject();
    
                decryptEngine.Init(false, keyPair.Private);
            }
    
            var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
            return decrypted;
        }
    }
    
    // In my test project   
    
        [Test()]
        public void EncryptTest()
        {
            // Set up 
            var input = "Perceived determine departure explained no forfeited";
            var enc = new EncryptionClass();
            var publicKey = "-----BEGIN PUBLIC KEY----- // SNIPPED // -----END PUBLIC KEY-----";
            var privateKey = "-----BEGIN PRIVATE KEY----- // SNIPPED // -----END PRIVATE KEY-----";
    
            // Encrypt it
            var encryptedWithPublic = enc.RsaEncryptWithPublic(input, publicKey); 
    
            var encryptedWithPrivate = enc.RsaEncryptWithPrivate(input, privateKey);
    
            // Decrypt
            var outputWithPublic = payUEnc.RsaDecrypt(encryptedWithPrivate, privateKey); 
            // Throws error: "unknown block type"
    
            var outputWithPrivate = payUEnc.RsaDecrypt(encryptedWithPrivate, _privateKey); 
            // returns the correct decrypted text, "Perceived determine departure explained no forfeited"
    
            // Assertion
            Assert.AreEqual(outputWithPrivate, input); // This is true
        }
    

    Incidentally the Java decryption exhibits the same issue - when encrypted with the public key only, it fails.

    I'm very new to cryptography, so I'm sure I'm doing something very simple wrong in the RsaEncryptWithPublic method.

    EDIT:

    I've also added a unit test which proves that the public key is equal to the public key that is extracted from the private key:

        [Test()]
        public void EncryptCompareTest()
        {
            AsymmetricKeyParameter keyParameterFromPub;
            AsymmetricKeyParameter keyParameterFromPriv;
            AsymmetricCipherKeyPair keyPair;
    
            using (var txtreader = new StringReader(_publicKey))
            {
                keyParameterFromPub = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();
            }
    
            using (var txtreader = new StringReader(_privateKey))
            {
                keyPair = (AsymmetricCipherKeyPair)new PemReader(txtreader).ReadObject();
                keyParameterFromPriv = keyPair.Public;
            }
    
            Assert.AreEqual(keyParameterFromPub, keyParameterFromPriv); // returns true;
    
        } 
    
    • scudsucker
      scudsucker over 9 years
      I was using an incorrect Public Key.. and the test that proved the Private and Public keys matched was using the correct Public Key. The a\bove code works perfectly as is, as long as you get the keys right!
  • Hugo Medina
    Hugo Medina almost 7 years
    Thank you! I appreciate your help
  • Ruslan
    Ruslan over 5 years
    Did you have an error 'illegal object in GetInstance: Org.BouncyCastle.Asn1.DerSequence' with this code? I am trying to test the BouncyCastle to see how it works and when I run the code above I get this error message.
  • Morio
    Morio over 5 years
    @Ruslan I think it could be caused by a version change. You could try a version close to my post time and trace back to see what's the API change.
  • sacha barber
    sacha barber about 4 years
    This is awesome, just the ticket, clearest example ever by far