RSA using BouncyCastle

Trying to do RSA using BouncyCastle, but struggling to find your way around the API? In a previous post (see here) I pondered why the RSA implementation in System.Security.Cryptography is restricted to only the most common usage scenarios. I mentioned BouncyCastle as an alternative for those who wanted a more flexible API, but never got around to providing examples where BouncyCastle was used. By request, this post provides usage examples by building a crude and simple, but efficient set of methods for RSA key generation, encryption, and decryption, all built on top of BouncyCastle.

NOTE: The general cryptographical security of the presented method is beyond the scope of the article. The code presented is not cryptographically secure for large data sets. If you’re here looking for a way to do cryptographically secure RSA in the general case, you should look into more complicated approaches including padding, blinding, and more sophisticated block cipher modes. Cryptography is a topic undergoing constant research, so stay up to date and be sure to evaluate the strength of your solution for the scenarios in which you apply it.

BouncyCastle provides flexibility and control over your encryption approach, which comes at a cost. The BouncyCastle API might be a bit hard to cope with at first, but if you know encryption in general you should be able to find your way around the API without too much effort. This post will be focusing on RSA, since that was my original need, but it should be mentioned that BouncyCastle provides many other asymmetric (and symmetric) algorithms for which the usage is similar to what you find below.

Creating RSA keys

Creating RSA keys is a simple task. The method below lets you specify the key size in bits, and creates a key pair for you.


public AsymmetricCipherKeyPair GenerateKeys(int keySizeInBits)
{
  RsaKeyPairGenerator r = new RsaKeyPairGenerator();
  r.Init(new KeyGenerationParameters(new SecureRandom(),
    keySizeInBits));
  AsymmetricCipherKeyPair keys = r.GenerateKeyPair();
  return keys;
}

That’s all there is to it.

Encryption

Now that we have a key pair, we are ready to encrypt and decrypt using RSA. In the example below, we use a key (public or private) to encrypt a byte sequence. To encrypt a string, simply convert the string to a byte array using Encoding.GetBytes.


public byte[] Encrypt(byte[] data, AsymmetricKeyParameter key)
{
  RsaEngine e = new RsaEngine();
  e.Init(true, key);</p>

<p>int blockSize = e.GetInputBlockSize();</p>

<p>List<byte> output = new List<byte>();</p>

<p>for (int chunkPosition = 0; chunkPosition &lt; data.Length; 
    chunkPosition += blockSize)
  {
    int chunkSize = Math.Min(blockSize, data.Length - 
      (chunkPosition * blockSize));
    output.AddRange(e.ProcessBlock(data, chunkPosition,
      chunkSize));
  }
  return output.ToArray();
}

The approach above uses a list to gather output for the sake of simplicity. Note that the RSA engine can only process a limited block size at a time (block size depends on the key size). The approach above processes a data set of an arbitrary size.

The above method does not impose constraints on which key you use for encryption. Use the public key or the private key as you see fit for your solution.

Decryption

The Decrypt method is very similar to the Encrypt method:


public byte[] Decrypt(byte[] data, AsymmetricKeyParameter key)
{
  RsaEngine e = new RsaEngine();
  e.Init(false, key);</p>

<p>int blockSize = e.GetInputBlockSize();</p>

<p>List<byte> output = new List<byte>();</p>

<p>for (int chunkPosition = 0; chunkPosition &lt; data.Length;
    chunkPosition += blockSize)
  {
    int chunkSize = Math.Min(blockSize, data.Length - 
      (chunkPosition * blockSize));
    output.AddRange(e.ProcessBlock(data, chunkPosition,
      chunkSize));
  }
  return output.ToArray();
}

Again, it’s up to you which key you choose to use. If you want to use the common approach, encrypt using a symmetric cipher, hash the data, and sign the hash with your private key using the above Encrypt method. If you want to use another approach like encrypting the actual data using your private key, you are free to do so.

I hope this post helps those of you who want to apply RSA (or any other asymmetric cipher) to more subtle cases than those supported by the .NET framework.

  • Not Working!

    hi! first of all thanks a lot! there is no documentation on the bouncy castle web page, or any where for that matter, on C# coding. I wanted to modify RSA signature for using blinding. but the above code dosen’t work.. i’m unable to makeout what’s wrong. i pasted the code in Class1.cs and called the functions as: Class1 rdc=new Class1(); AsymmetricCipherKeyPair keys = rdc.GenerateKeys(960); byte[] plainbytes = System.Text.Encoding.UTF8.GetBytes(txt1.Text); byte[] cipherbytes = rdc.Encrypt(plainbytes, keys.Private); byte[] plainbytes_de = rdc.Decrypt(cipherbytes, keys.Public); txt2.Text = Convert.ToBase64String(cipherbytes); txt3.Text = Convert.ToBase64String(plainbytes_de);

    please tell me what i’m doing wrong… also there is a RsaBlindingEngine in Bouncy Castle… How is it usefull?

  • lalit

    i’m sorry my name is lalit not Not Working!

  • http://ox.no Håvard

    Hi Lalit.

    You haven’t stated what you are trying to achieve, so I’ll assume you expect txt3.Text to contain the plain text from txt1.Text to verify that encryption and decryption works as intended. If that is the case, then you should convert plainbytes_de to a string using Encoding.UTF8.GetString, not Convert.ToBase64String.

    The RSA blinding engine in BouncyCastle should work fine. Just generate keys, use RSABlindingFactorGenerator to generate a blinding factor for you, and use RSABlindingEngine for encryption/decryption.

    Good luck!

  • lalit

    Thanks a million! That solved the problem (silly mistake on my side)! But I’m stuck again, the constructor of RSABlindingFactorGenerator requires an object of RsaKeyParameters type. Do you know how to make this from the key pair or only the public key?

  • lalit

    A simple type-casting solved the problem Again my silly mistake, sorry…

  • Kombajn

    Is http://www.discryptor.net/ using it right?

  • http://www.telemo.com.ve Anibal

    Hi Harvard! Thanks for your example! I’ve been trying to implement it into my J2ME script for decryption, and “almost” work!

    This is my “decryption part” code, and here, you can note some differences with your code:

    public String RSADecrypt(byte[] data,RSAKeyParameters key) { RSAEngine e = new RSAEngine(); e.init(false, key); int blockSize = e.getInputBlockSize(); StringBuffer strOut=new StringBuffer(); for (int chunkPosition = 0; chunkPosition <data.length; chunkPosition += blockSize) { int chunkSize = Math.min(blockSize, Math.abs(data.length - (chunkPosition * blockSize))); byte[] pb=e.processBlock(data, chunkPosition, chunkSize); String strBuf=new String(pb); System.out.println(“Got:”+strBuf); strOut.append(strBuf); } return strOut.toString(); }

    First, I’ve changed the AsymmetricKeyParameter with RSAKeyParameters, because I’ve already the modulus and private exponent for the private key. It works fine.

    Also, where you wrote: int chunkSize = Math.min(blockSize, data.length – (chunkPosition * blockSize));

    I wrote: int chunkSize = Math.min(blockSize, Math.abs(data.length – (chunkPosition * blockSize)));

    That’s because if not, I got: java.lang.NegativeArraySizeException.

    Well, once i did this, and set a crypto message for a decoded “Hello World!” text, I get:

    “Got:!dlroW olleH”

    If I try to decrypt larger texts, for example:

    “I hope this post helps those of you who want to apply RSA (or any other asymmetric cipher) to more subtle cases than those supported by the .NET framework.”

    The result is:

    Got:op siht epoh I Got:esoht spleh ts Got:aw ohw uoy fo Got:SR ylppa ot tn Got:ehto yna ro( A Got:c cirtemmysa r Got:erom ot )rehpi Got: sesac eltbus Got:pus esoht naht Got: eht yb detrop Got:krowemarf TEN. Got:.

    For a final string like: “op siht epoh Iesoht spleh tsaw ohw uoy fo SR ylppa ot tnehto yna ro( Ac cirtemmysa rerom ot )rehpi sesac eltbus pus esoht naht eht yb detropkrowemarf TEN..”

    Any idea?

    Thanks in advance!

  • Brandon

    I’ve copied this into a VB.NET application running framework 2.0.

    I can’t seem to get it to decrypt more than one block of data. This code in the decrypt method

    While chunkPosition < data.Length

    Dim chunkSize As Integer = System.Math.Min(blockSize, data.Length – (chunkPosition * blockSize)) output.AddRange(e.ProcessBlock(data, chunkPosition, chunkSize)) chunkPosition += blockSize

    End While

    Only ever adds one range of bytes to the List, because the e.ProcessBlock line never gives back more than that. Also, the calculation that figures out chunksize returns a huge negative number on the second iteration. I’ve changed this number in the debugger to the same size block as the initial iteration to no avail. Any help?

  • Pingback: The Dangers of Copy/Paste Coding | Design Limbo

  • Uzeyir

    Hi, I want to change cryptoprovider(f/e: “Microsoft Enhanced Cryptographic Provider v1.0″) as keylength when generate pair key with AsymmetricCipherKeyPair class. Can anyone helps with it?

  • Sekhar

    In Encryption List class are used by you. From jar this List is class is coming, this is an User Defined class or it is coming from any jar file, please mention those details.

  • http://www.technogumbo.com Charles

    Sekhar,

    The above code is in c# although slightly modified it works great for the Java implementation of bouncycastle!

    The list you are reffering to is actualy a data-type in c#. You can see examples of using a list here: http://dotnetperls.com/list

    Thanks so much for posting something on this Havard! As other people have said; I could find no examples or documentation on this anywhere. I am using the Java implementation, but your code example is more then enough to get started.

    Thanks again!

  • http://ox.no Håvard

    Charles: Glad to help, thanks for sharing!

  • gary

    Hello all I have a question concerning rsa key pairs. I am currently working for a client to simulate large volumes of encrypted traffic. the issue I am having with the keys is, I already have my private key given to me but its in a file with the certificate and its format is something like this “56 H3 75 …..” there are 10 sets like this. how would I use this to generate the private key or how would I import it.

  • http://www.quovadisglobal.com Craig Martin

    Hi There,

    This doesn’t exactly apply to this post, but I thought I would ask it here anyway as there is very little on using BouncyCastle in C# (as has been noted by previous posts here!).

    Is there anyway to create an AsymmetricKeyParameter using a previously generated private key? I am generating a key pair, storing the private key in an encrypted form and then when it is time to create a P12 for the end user to retrieve their certificate, I am decrypting the previously stored private key, but now have no idea how to actually make it into an AsymmetricKeyParameter so as to create the P12 using BouncyCastle.

    Is what I am trying to do even possible?

    Any help is greatly appreciated.

    Cheers, Craig Martin

  • zack

    I have the same question as Craig, what if you already have the private key and want to use it to decrypt. Trying to get this working with AWS :)

  • http://ox.no Håvard

    @Craig @zack The AsymmetricKeyParameter itself is a base class for all asymmetric keys; it does not (and cannot) know what the actual key is. So, you need to construct a key parameter matching the key you want to use. If you’re using an RSA private key, construct an RSAKeyParameters instance. If you’re using El Gamal, construct an ElGamalKeyParameters instance, and so on. All these concrete key parameter classes inherit AsymmetricKeyParameter.

  • Sinisa

    Brandon’s got a point. Any help for that?

  • Sinisa

    Nvm, got it. As chunkPosition gets enlarged by blockSize, it is enough to put

    int chunkSize = Math.Min(blockSize, data.Length – chunkPosition );

    instead of

    int chunkSize = Math.Min(blockSize, data.Length – (chunkPosition * blockSize));

  • John

    Hi, I am looking for an infrastructure like JCE in java so I can “install” encryption providers. The one I am going to put in now is Bouncy Castle provider and use “PBEWITHMD5AND256BITAES-CBC-OPENSSL” but I want to be able to change the providers and algorithms just by configuration change. I have that in Java with JCE. Is there something like that in C# and are there docs / examples?

    Thanks.

  • http://glenakinsays.blogspot.com/ Glen

    Hi Harvard,

    Thanks for this. I was actually able to use your suggestions in blinding messages. However, I can’t seem to go through the whole process of blinding a message, signing it, unblinding the signed message, and verifying the signature. I’ve written these codes: http://www.daniweb.com/forums/post1315685.html#post1315685

    Please, if you could have a look, perhaps you could tell me what I’m doing wrong.

  • Hector Huerta

    Hi! I am using the new version of BouncyCastle for C# , and now it’s working, but I can not load my previously generated PrivateKey could you give us and example of loading one with the RSAKeyParameters instance?

    Thank you very much

  • Hashim Malin

    This code is dangerously wrong. There is a REASON that the API for RSA limits the size of block you can encrypt directly to a function of the key size, and when you use this naive approach to apply the same key to many chunks of data, you leak enough information to allow the factoring of your private keys by adversaries. I repeat: if you use this code as given above, you are doing absolutely nothing but giving yourself a false sense of security.

    To encrypt large volumes of data using RSA, it is CRITICAL to use a padding scheme — a system called RSA-OAEP is one example. Well designed padding schemes used in conjunction with RSA prevent dictionary attacks, chosen plaintext attacks via ciphertext mutability based on the multiplicative homomorphism feature of RSA, and defend against recovery of cleartext which is otherwise possible when sending the same message to multiple public keys.

    Honestly, just a quick perusal of wikipedia’s article on RSA should be enough to convince anyone that the code offered in this article is deeply flawed and fundamentally insecure.

  • http://ox.no Håvard

    @Hashim The code itself is most definitely not “dangerously wrong”, but it can of course be used in scenarios where it would be unsafe.

    The article neither suggests that it is a good idea to encrypt large data using the scheme it implements, nor discusses the safety of this particular RSA scheme versus other schemes. In fact, it does not concern itself with security issues at all. This is deliberate because it is a different matter, way beyond the scope of the article. The implementation is a perfectly valid use of RSA, which for small data sets (size depends on key size and is another matter beyond the scope of the article) is safe and sound.

    Saying that the code is unsafe is like saying that code which allows using RSA with a small key size is unsafe. It obviously is unsafe in the general case, but that doesn’t mean it’s wrong.

    Moral: You must never uncritically copy/paste code off the internet without being sure it is valid, sound, safe, and correct for your usage scenario.

  • Hashim Malin

    …your article says “processes a data set of an arbitrary size”. This is wrong. The fact that you’ve wrapped the RSA code in a loop at all is wrong. If you have enough data that you feel the need to make a loop like that, you — by definition — have enough data that you need to use one of the cryptographically secure schemes that have been developed to allow block ciphers to do this.

    Wikipedia is a good start: https://secure.wikimedia.org/wikipedia/en/wiki/Block_cipher_modes_of_operation

    What you’re doing in your post is ECB mode. ECB mode has long been known by the security community to not provide serious message confidentiality. At the very least your original post should mention this and provide people a link to an elementary discussion of block cipher modes.

  • http://ox.no Håvard

    @Hashim The article is correct. We’re not discussing correctness. What you are discussing is cryptographical strength.

    Block modes are actually only one of the weaknesses it has from a cryptanalytic perspective – others include lack of blinding, making it susceptible to timing attacks, and other shortcomings which are covered in the Wikipedia article cited by you. Also, more recent research suggest that even the sign/encrypt approach has flaws (read Don Davis’ paper “Defective Sign & Encrypt in SMIME, PKCS#7, MOSS, PEM, PGP and XML” from 2001 for an in-depth analysis). Even if there are weaknesses to all the various approaches, they are strong for certain applications. The key is knowing what to apply under which circumstances.

    This topic is far beyond the scope of the article. Scaring people with statements is just as bad as ignorance. What needs to be done is to raise awareness, and crying “wolf wolf” never helped that. I think we agree on that, and I agree that the reader could benefit from a notice. It has been added.

  • praveen

    Hi all, thanks for all the help you have given here on this post..this really helped me to understand the Bouncy API. i am stucked with a problem and i want to post here to take your suggestions.. my requirement is to store the public key and private key in the database (Sql server) as a string (nvarchar(max)) and retrieve those keys while encrypting or decrypting. can you please help me to convert a string to cipher key parameter. i want to use any of the asymetric engines for this purpose. the code i have written for this is : BufferedBlockCipher bufferedCipher = new BufferedBlockCipher(desedeEngine); System.Text.UTF8Encoding UTFEncode = new UTF8Encoding(); byte[] keyByte = UTFEncode.GetBytes(PublicKey);

            // Create the KeyParameter for the DES3 key generated. 
            KeyParameter keyparam = ParameterUtilities.CreateKeyParameter("DES", keyByte);
    

    iam able to execute the projec but if if i verify the decypted file its actually not decrypted. can you please suggest.

  • http://www.keelio.com Matt Olson

    Here is a more verbose example wtih padding and the encryption / decryption routines don’t have bugs in them that cause long data to fail.

    private void btnGenerateKeys_Click(object sender, EventArgs e) { //ECDSASample(384); AsymmetricCipherKeyPair keys = GenerateRSAKeys(1024);

            /Option 1 to write RSA Private key, doesn't work with public however/
            PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keys.Private);
            byte[] serializedPrivateKey = privateKeyInfo.ToAsn1Object().GetDerEncoded();
            System.IO.File.WriteAllBytes("C:\RSAPrivate.key",serializedPrivateKey);

        /*Option 2 to write RSA Private Key, works same way with public*/
        StringBuilder sb = new StringBuilder();
        PemWriter pemwrit = new PemWriter(new StringWriter(sb));
        //char[] password = "test123".ToCharArray();
        pemwrit.WriteObject(keys.Private);
        pemwrit.Writer.Flush();
    
        System.IO.File.WriteAllText("C:\\RSAPrivate.Key",sb.ToString());
    
        sb = new StringBuilder();
        pemwrit = new PemWriter(new StringWriter(sb));
        pemwrit.WriteObject(keys.Public);
        pemwrit.Writer.Flush();
    
        System.IO.File.WriteAllText("C:\\RSAPublic.Key", sb.ToString());
    
        /*Example to read it back*/
        StringBuilder sbr = new StringBuilder();
        PemReader reader = new PemReader(new StringReader(pemwrit.Writer.ToString()));
        AsymmetricKeyParameter pubKey = (AsymmetricKeyParameter)reader.ReadObject();
        //RsaPublicKeyStructure key = new RsaPublicKeyStructure(reader.ReadObject();
    }
    

    public byte[] RSAEncryptV3(byte[] data, AsymmetricKeyParameter key) { SecureRandom rand = new SecureRandom(); IBufferedCipher cipher = CipherUtilities.GetCipher("RSA/NONE/OAEPWithSHA1AndMGF1Padding");

            cipher.Init(true, new ParametersWithRandom(key, rand));

        byte[] cipherTextBlock = null; 
        int outputsize = cipher.GetOutputSize(data.Length); //Array size of ciphered data (encrypted data)
        int blockSize = cipher.GetBlockSize();  //Amount of data we can process at one time (-2 -2*hlen)
        List&lt;byte&gt; output = new List&lt;byte&gt;();
        int outputLen = 0;
        byte[] dataToProcess = null;
        for (int chunkPosition = 0; chunkPosition &lt; data.Length; chunkPosition += blockSize)
        {
            dataToProcess = new byte[blockSize];
            int chunkSize = (data.Length - chunkPosition) &lt; blockSize ? (data.Length - chunkPosition) : blockSize; //Math.Min(blockSize, data.Length - (chunkPosition * blockSize));
            Buffer.BlockCopy(data, chunkPosition, dataToProcess, 0, chunkSize);
    
            cipherTextBlock = new byte[outputsize];
    
            outputLen = cipher.ProcessBytes(dataToProcess, 0, chunkSize, cipherTextBlock, 0);
            cipher.DoFinal(cipherTextBlock, outputLen);
            //output.AddRange(e.ProcessBytes(data, chunkPosition,
            //  chunkSize));
            output.AddRange(cipherTextBlock);
        }
    
        return output.ToArray();
    }
    
    public byte[] RSADecryptV3(byte[] data, AsymmetricKeyParameter key)
    {
    
        SecureRandom rand = new SecureRandom();
        IBufferedCipher cipher = CipherUtilities.GetCipher("RSA/NONE/OAEPWithSHA1AndMGF1Padding");
    
        cipher.Init(false, new ParametersWithRandom(key, rand));
    
        byte[] cipherTextBlock = null;
        int outputsize = cipher.GetOutputSize(data.Length); //Array size of ciphered data (encrypted data)
        int blockSize = cipher.GetBlockSize();  //Amount of data we can process at one time (-2 -2*hlen)
        List&lt;byte&gt; output = new List&lt;byte&gt;();
        int outputLen = 0;
        byte[] dataToProcess = null;
    
        for (int chunkPosition = 0; chunkPosition &lt; data.Length; chunkPosition += blockSize)
        {
            dataToProcess = new byte[blockSize];
            int chunkSize = (data.Length - chunkPosition) &lt; blockSize ? (data.Length - chunkPosition) : blockSize; //Math.Min(blockSize, data.Length - (chunkPosition * blockSize));
            Buffer.BlockCopy(data, chunkPosition, dataToProcess, 0, chunkSize);
    
            cipherTextBlock = new byte[outputsize];
    
            outputLen = cipher.ProcessBytes(dataToProcess, 0, chunkSize, cipherTextBlock, 0);
            cipher.DoFinal(cipherTextBlock, outputLen);
            //output.AddRange(e.ProcessBytes(data, chunkPosition,
            //  chunkSize));
            output.AddRange(cipherTextBlock);
        }
    
        return output.ToArray();
    }
    

  • Venu

    Hi Matt

    thanks a lot for the code i tried your code but i am getting error “attempt to process message to long for cipher” while decrypting the encrypted file.

    Please can you suggest me where i might have gone wrong.

    Thanks for helping me