Serializing/Deserializing RSA Public/Private keys generated using Bouncy Castle library

.Net provides a good Cryptographic framework in the System.Security.Cryptography namepsace. But I have sometimes found it lacking for my needs (and non-intuitive at some other times).

Yesterday I needed to generate a RSA Public-Private key pair in a client-server communication, where the server generates the key pair and sends off the public key to the client, which the client subsequently uses to encrypt data being sent to the server. The server stores the Private key in the database used later to decrypt data received from the client and then send the encrypted response to it.

Clearly, this needed the ability to serialize the public and private portions of the RSA key pair separately to strings and then generate the original Key objects from the serialized strings. My initial instinct was this should be easy to do with .Net and there much be some API method somewhere.

The closest I found were the ToXmlString() and FromXmlString() methods in System.Security.Cryptography.RSA class. I will cover these methods in another blog post, but for my present scenario, I decided to go with the open-source Bouncy Castle cryptographic library's CSharp port (the decision was based on the fact that I was writing a client that needed to run on multiple diverse platforms, and Bouncy Castle library has a Java version with the same architecture and design as the C# version making secure communication between multiple platforms easier to manage). In fact the Java version was the primary version for the Bouncy Castle library and the C# version is a port of it.

It was easy to browse the architecture and class hierarchy of the library in Visual Studio's Object Browser. In no time was I able to write the following lines to generate a RSA key-pair using the Bouncy Castle library:

 

 

RsaKeyPairGenerator g=new RsaKeyPairGenerator();
g.Init(new KeyGenerationParameters(new SecureRandom(), 1024));
var pair=g.GenerateKeyPair();

 

Having done that so quickly, I thought it should be another breeze to serialize the Public and Private Key components inside the key pair object. However, it was not so easy as I found out.

The documentation for the Bouncy Castle library is limited (and more so for the CSharp version), and you essentially have to browse through the examples provided and explore the library manually to accomplish what you want. After some hair-pulling, I was finally able to find the classes and methods to serialize the Public/Private keys in the key pair. The code turned out to be as follows:

 

 

PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(pair.Private);
byte[] serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetDerEncoded();
string serializedPrivate = Convert.ToBase64String(serializedPrivateBytes);

SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pair.Public);
byte[] serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded();
string serializedPublic = Convert.ToBase64String(serializedPublicBytes);

 

The code as such is pretty short, but it took me some real time to figure out the classes handling the serialization for the Keys. Having done that, the final step was to deserialize the strings into the original key objects. Some more browsing through the library and I was able to assemble the following code:

 

 

RsaPrivateCrtKeyParameters privateKey = (RsaPrivateCrtKeyParameters) PrivateKeyFactory.CreateKey(Convert.FromBase64String(serializedPrivate));
RsaKeyParameters publicKey = (RsaKeyParameters) PublicKeyFactory.CreateKey(Convert.FromBase64String(serializedPublic));

 

In the end, it was well worth the effort. Although I have used the Bouncy Castle's Java version briefly before, I haven't tried to serialize the RSA keys in its java version earlier. However I expect that to be on the similar lines (probably exactly similar).

Attached below is the complete code demonstrating serializing/deserializing RSA public/private keys using the Bouncty Castle library.

You might want to encrypt the serialized Private Key before storing it somewhere. It would be a serious security issue if someone gets access to all private keys stored plainly somewhere. For this purpose, you might want to have a look at the Org.BouncyCastle.Pkcs.EncryptedPrivateKeyInfoFactory class in the Bouncy Castle library that provides the CreateEncryptedPrivateKeyInfo method to generate encrypted Private Key bytes. You can then use the PrivateKeyFactory.DecryptKey method to create the original Private Key object from the encrypted key bytes.

 

AttachmentSize
Binary Data RsaKeyPair.cs1.92 KB

Comments

Thank you, Rahul.

seems like a lot of great effort! needed this for my project.
god bless you for sharing this online.

Very useful example!

Simple, clean, working sample code.

You just saved my day

Genesio from Italy Wink

Typo: "Bounty Castle" should be "Bouncy Castle".

rahul's picture

Oops, fixed.. thanks for pointing that out.

Hi Rahul,

it's nice work. Thanks.

I just want to ask, for private and public key string parameters, can I define any of string value ? how about the length? does it have any rule ? or I can use any string ?

public static RsaKeyPair createFrom (string @private, string @public)

rahul's picture

Well Clark, if you already have public and private keys as string and you want to create a RsaKeyPair object from them, then that is what is called "deserialization", so you would use the deserialization code as in the above blog post. Please note different toolkits have different formats for serializaing Rsa keys, so unless you are using a standard .pfx or .cer format, you would need to use the same toolkit for deserialization (e.g. .Net serializes in xml format and can parse back the same xml).

Hi Rahul, I create 1024 key with your create function (public static RsaKeyPair create)

and I want to return the private and public key in string format. I search there is several method. I compare your code with another code which I found in stackoverflow, and the result for private key in string format is different. but the public key in string format is same. Do you have any suggestion? Thanks in advance

 public RsaPrivateCrtKeyParameters privateKey { get; internal set; }
 public RsaKeyParameters publicKey { get; internal set; }
 public string @public { get; internal set; }
 public string @private { get; internal set; }

 //adding 2 string variable to compare

 public string @public2 { get; internal set; }
 public string @private2 { get; internal set; }

PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(pair.privateKey);
            byte[] serializedPrivate = privateKeyInfo.ToAsn1Object().GetDerEncoded();
            pair.@private = Convert.ToBase64String(serializedPrivate);

            TextWriter textWriterPrivate = new StringWriter();
            PemWriter pemWriterPrivate = new PemWriter(textWriterPrivate);
            pemWriterPrivate.WriteObject(p.Private);
            pemWriterPrivate.Writer.Flush();
            pair.@private2 = textWriterPrivate.ToString();

            SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pair.publicKey);
            byte[] serializedPublic = publicKeyInfo.ToAsn1Object().GetDerEncoded();
            pair.@public = Convert.ToBase64String(serializedPublic);

            TextWriter textWriterPublic = new StringWriter();
            PemWriter pemWriterPublic = new PemWriter(textWriterPublic);
            pemWriterPublic.WriteObject(p.Public);
            pemWriterPublic.Writer.Flush();
            pair.@public2 = textWriterPublic.ToString();

-------------------------------------------------------------------------------------------------

with your method :

private key : MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKk8oX+GkJZvhl00g33ZCmFttLjveyhqx8lmbgDBmj7DUBWPq/eFUmMVH3rKqUb6CSfSgoYtN8HHsPukKIwZAJSVdb834pB1o/iRc8OUy283dWipNb1ALG7dj8jAZlfBAvJhyZF2PvNd+YyicVOr7AbjZVjZhcP4gImc6q8XfMcxAgMBAAECgYEAhQZtg0oIuNbs7LJscS17JV4QYhWL3xcf90UWTm3filoHxwrph2Q6gDuIRQKr0GiVbcHgawt7+ku25/X/ETBN7S70QBMh0A38+dUMv9MujLz9mURwpI5w+Q5xLl6+2Jbo6idP5Sah3z9LbsF1puacEBmzyG4FHI391CMlPknEemECQQD/ivd37YFlUfNUQ0fFYc7t6DpXtbZ2Ma7s4VYgeGjlFMKRpjnnIew2nlJg0DURlsUTmB4gegV6Fw9rce75xex9AkEAqYojSBU+2gc4WHB6D6/BpSDQcaIsEKDpyVTiCrLULxU7ZBIQz28ONGScGhHn8l5Jw53y3VDT23WfR+rIsD3nxQJALyY42svboBIqz1VKnMSbJZI/kYdZjx1DpTk+ZudQk1PtQmplLJw5tSopEOvZntEus5rRlDRvZkNy+OQgr70xEQJAfheVIflbI4EXMP+GaMBI/20mWj1JFJz5A5oz+80A7nuWDlk5U22/XMwvJVyH68Sgi/KfPGbvCluyuSQvWpTQGQJAC+FzndA5N/DXjk8jENgoQEGUH5V00Hn0ootiutjYSM2DGVE3G9wQBHFp6APsFs7tF1HdqendrjvEeiNdDM2c0g==

public key : MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCpPKF/hpCWb4ZdNIN92QphbbS473soasfJZm4AwZo+w1AVj6v3hVJjFR96yqlG+gkn0oKGLTfBx7D7pCiMGQCUlXW/N+KQdaP4kXPDlMtvN3VoqTW9QCxu3Y/IwGZXwQLyYcmRdj7zXfmMonFTq+wG42VY2YXD+ICJnOqvF3zHMQIDAQAB

with openSSL method :

private key :

-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCpPKF/hpCWb4ZdNIN92QphbbS473soasfJZm4AwZo+w1AVj6v3
hVJjFR96yqlG+gkn0oKGLTfBx7D7pCiMGQCUlXW/N+KQdaP4kXPDlMtvN3VoqTW9
QCxu3Y/IwGZXwQLyYcmRdj7zXfmMonFTq+wG42VY2YXD+ICJnOqvF3zHMQIDAQAB
AoGBAIUGbYNKCLjW7OyybHEteyVeEGIVi98XH/dFFk5t34paB8cK6YdkOoA7iEUC
q9BolW3B4GsLe/pLtuf1/xEwTe0u9EATIdAN/PnVDL/TLoy8/ZlEcKSOcPkOcS5e
vtiW6OonT+Umod8/S27BdabmnBAZs8huBRyN/dQjJT5JxHphAkEA/4r3d+2BZVHz
VENHxWHO7eg6V7W2djGu7OFWIHho5RTCkaY55yHsNp5SYNA1EZbFE5geIHoFehcP
a3Hu+cXsfQJBAKmKI0gVPtoHOFhweg+vwaUg0HGiLBCg6clU4gqy1C8VO2QSEM9v
DjRknBoR5/JeScOd8t1Q09t1n0fqyLA958UCQC8mONrL26ASKs9VSpzEmyWSP5GH
WY8dQ6U5PmbnUJNT7UJqZSycObUqKRDr2Z7RLrOa0ZQ0b2ZDcvjkIK+9MRECQH4X
lSH5WyOBFzD/hmjASP9tJlo9SRSc+QOaM/vNAO57lg5ZOVNtv1zMLyVch+vEoIvy
nzxm7wpbsrkkL1qU0BkCQAvhc53QOTfw145PIxDYKEBBlB+VdNB59KKLYrrY2EjN
gxlRNxvcEARxaegD7BbO7RdR3anp3a47xHojXQzNnNI=
-----END RSA PRIVATE KEY-----

public key :

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCpPKF/hpCWb4ZdNIN92QphbbS4
73soasfJZm4AwZo+w1AVj6v3hVJjFR96yqlG+gkn0oKGLTfBx7D7pCiMGQCUlXW/
N+KQdaP4kXPDlMtvN3VoqTW9QCxu3Y/IwGZXwQLyYcmRdj7zXfmMonFTq+wG42VY
2YXD+ICJnOqvF3zHMQIDAQAB
-----END PUBLIC KEY-----

Opps I tried to get the value of modulus, public exponent, private exponent, prime p, prime q, and another value, and the result is same. So I think it just different way to export. Please correct me if I am wrong. Thanks

            sw.WriteLine("Public Modulus : " + pair.publicKey.Modulus.ToString(16));
            sw.WriteLine("Public Exponent : " + pair.publicKey.Exponent.ToString(16));
            sw.WriteLine("Private Modulus : " + pair.privateKey.Modulus.ToString(16));
            sw.WriteLine("Private Exponent : " + pair.privateKey.Exponent.ToString(16));
            sw.WriteLine("Prime 1 (p) : " + pair.privateKey.P.ToString(16));
            sw.WriteLine("Prime 2 (q) : " + pair.privateKey.Q.ToString(16));
            sw.WriteLine("exponent1 (d mod (p-1)) : " + pair.privateKey.DP.ToString(16));
            sw.WriteLine("exponent2 (d mod (q-1)) : " + pair.privateKey.DQ.ToString(16));

rahul's picture

Hi Clark, like I said earlier, different libraries have different formats for serializing/deserializing keys. The one presented in this blog post uses Bouncy Castle library and I guess that way is specific to this library only. However OpenSsl is a standard format and you should be able to deserialize keys serialized with OpenSsl in a cross-platform and interoperable manner.

I haven't myself tried exporting the components independently like in the sample code you presented, but if the result comes similar to Bouncy Castle serialization as you suggested, then maybe this library is itself using the same approach for serialization.

you littraly saved my life with this :)

thank you so much !!!!!!!

Thank you very much. You really help me :)

Hi Rahul,

That's really nice work. now could you please tell me how can i do encryption and decryption with the generated string public and private key.

I am using this concept to encrypt my data. please have a look this code.

string inputMessage = "Test Message";
UTF8Encoding utf8enc = new UTF8Encoding();
byte[] inputBytes = utf8enc.GetBytes(inputMessage);

RsaKeyPairGenerator rsaKeyPairGnr = new RsaKeyPairGenerator();
rsaKeyPairGnr.Init(new KeyGenerationParameters(new SecureRandom(), 512));
AsymmetricCipherKeyPair keyPair = rsaKeyPairGnr.GenerateKeyPair();
RsaKeyParameters publicKey = (RsaKeyParameters)keyPair.Public;

IAsymmetricBlockCipher cipher = new RsaEngine();

cipher.Init(true, PublicKey);

byte[] cipheredBytes = cipher.ProcessBlock(inputBytes, 0, inputMessage.Length);
string p = utf8enc.GetString(cipheredBytes);
textBox4.Text = p;

 

this is running perfectly but data is not encrypted with the string public key, I would like to encrypt the message with the string public key and do the decryption as well. could you please provide me some sample of that code.

Thanks in advance

rahul's picture

Vipin, going by the way RSA works, you can encrypt with public key and decrypt with private. But you can't:

  1. Encrypt with private and decrypt with public
  2. Encrypt with public and decrypt with public

That would defeat the whole purpose of asymmetric cryptography.

Hi Rahul,

I am sorry, but that was not my question.

I just would like to know how can we encrypt the data with the public key (data type: string) and then how can decrypt the generated cipher text with the private key (data type: string).

In your block you are able to generate the public key and private key and storing them into string data type. I just want to know how the encryption can be done with the public key whose data type is string.

thanks in advance..

Hey rahul,

please reply me..

Thanks a lot for this post, I had a hard time searching for a solution to this.

In my case I needed to serialize/deserialize a PGP keypair instead of just an RSA pair, so I needed to do it this way:

http://pastebin.com/mA8rPyXG

It works like a charm :)

rahul's picture

Hi there, thanks for sharing your solution..

Really this is great :) Thanks again to make life easier.

Hi rahul , thanks for this post.

recently i was asked to do almost the same task(c# application/service)as you mentioned in this.the Difference is instead of database we would be storing the keys to an HSM device(safe net).I got aprotectToolkit SDK from this vendor which supports PKCS#11 stds. same which the device was following.But that was more suitable for c/c++ development. So i got an API , NCryptoki which provides a .net library for PKCS#11.

They provided an User interface to create slotes,tokens,generate key pair etc. So i created a key pair and exported it to disk. By defult it is saving to a file with out extension.also it allowing to save it to .txt file or anything. 

Ncryptoki provides all example code to generate key pair from code, search for keys using its attributes . but i need some thing like. i need to read that exported key file in code and then validate exixtence of the key in the HSM. I tried severals ways to read the key from the file and then convert it to the normal RSApublic key.But failed. 

rahul's picture

Hi Akhil, I am unable to provide any support with proprietary devices/systems used for generating or managing keys.

I have P, Q, N, Da nd E in decimal format. How do I convert them into RSAParameters Format? 

rahul's picture

Hi Renu, that is outside the scope of this blog post. I advice you search online or seek help at one of the online forums or groups.

Okays. Thanks. Can you at least tell me how do I use my own key pair instead of those that are generated automatically.

rahul's picture

Hi Renu, you would use the deserializing portion of the code from this blog post. Here's it for you again:

{syntaxhighlighter brush: csharp;fontsize: 100; first-line: 1; }RsaPrivateCrtKeyParameters privateKey = (RsaPrivateCrtKeyParameters) PrivateKeyFactory.CreateKey(Convert.FromBase64String(serializedPrivate)); RsaKeyParameters publicKey = (RsaKeyParameters) PublicKeyFactory.CreateKey(Convert.FromBase64String(serializedPublic));{/syntaxhighlighter}

 

Thank you Rahul.

I have below private key:

-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCpPKF/hpCWb4ZdNIN92QphbbS473soasfJZm4AwZo+w1AVj6v3
hVJjFR96yqlG+gkn0oKGLTfBx7D7pCiMGQCUlXW/N+KQdaP4kXPDlMtvN3VoqTW9
QCxu3Y/IwGZXwQLyYcmRdj7zXfmMonFTq+wG42VY2YXD+ICJnOqvF3zHMQIDAQAB
AoGBAIUGbYNKCLjW7OyybHEteyVeEGIVi98XH/dFFk5t34paB8cK6YdkOoA7iEUC
q9BolW3B4GsLe/pLtuf1/xEwTe0u9EATIdAN/PnVDL/TLoy8/ZlEcKSOcPkOcS5e
vtiW6OonT+Umod8/S27BdabmnBAZs8huBRyN/dQjJT5JxHphAkEA/4r3d+2BZVHz
VENHxWHO7eg6V7W2djGu7OFWIHho5RTCkaY55yHsNp5SYNA1EZbFE5geIHoFehcP
a3Hu+cXsfQJBAKmKI0gVPtoHOFhweg+vwaUg0HGiLBCg6clU4gqy1C8VO2QSEM9v
DjRknBoR5/JeScOd8t1Q09t1n0fqyLA958UCQC8mONrL26ASKs9VSpzEmyWSP5GH
WY8dQ6U5PmbnUJNT7UJqZSycObUqKRDr2Z7RLrOa0ZQ0b2ZDcvjkIK+9MRECQH4X
lSH5WyOBFzD/hmjASP9tJlo9SRSc+QOaM/vNAO57lg5ZOVNtv1zMLyVch+vEoIvy
nzxm7wpbsrkkL1qU0BkCQAvhc53QOTfw145PIxDYKEBBlB+VdNB59KKLYrrY2EjN
gxlRNxvcEARxaegD7BbO7RdR3anp3a47xHojXQzNnNI=
-----END RSA PRIVATE KEY-----

could you please let me know how can i create RSACryptoServiceProvider  object from it?

I need to decrypt a message by using above private key.

rahul's picture

Hi Mahesh, try using this:

 

{syntaxhighlighter brush: csharp;fontsize: 100; first-line: 1; }RsaPrivateCrtKeyParameters privateKey = (RsaPrivateCrtKeyParameters) PrivateKeyFactory.CreateKey(Convert.FromBase64String(serializedPrivate));{/syntaxhighlighter}

Pass your key as a string in serializedPrivate variable.

 

If i have a Client side and a web api and i need everything encrypted assymetrical, how i do ? How send the public key securely ? I have to get the input text on client, encrypt, send to the web api, decrypt here, search on DB, get the json result, encrypt it and send to the client side encrypted. Then, decrypt and show...

rahul's picture

Hi Marcelo, that question is outside the scope of this blog post. But I have a very simple counter question:
Why don't you simply run your server-side over HTTPS?

You get exactly what you want, the entire communication between the client and the server is securely encrypted by stndard technique.