Asymmetric Encryption and Signing with RSA in Silverlight

November 22, 2010 at 12:44 AMDustin Horne

While Silverlight is a powerful tool for rich client applications, it lacks the ability to perform asymmetric encryption out of the box.  In this article, I'm going to share a cryptography class library I've been working on and show you how to use it to perform standards compliant RSA Encryption in Silverlight that is cross compatible with .NET's built in RSACryptoServiceProvider, allowing you to encrypt from Silverlight using my library and decrypt on your website using the RSACryptoServiceProvider.  For brevity, only examples using my class library will be shown except for a few examples that show equivelant functionality from the RSACryptoServiceProvider (RSACSP).

Update 11/24/2010: The Scrypt library has been updated.  Key generation is now performed Asynchronously to avoid blocking the UI thread and freezing the browser.  I've updated the applicable source samples in this article to reflect the changes.

Edit*:  I've decided to open up the source for this project. You can download this library and/or source and view the current applicable license on its new home at CodePlex: http://scrypt.codeplex.com/

Background

Before I get into the sample code, I'm going to give you a little bit of background.

What does it all mean?
RSA is an encryption scheme that uses a public and private key.  There are a variety of uses for RSA.  The two most common are encryption to protect data, and signing to verify the authenticity of data.  Encryption is performed with the public key, with the premise that data encrypted with the public key can only be decrypted using the private key.  The private key should be kept safe and secure and the public key can be shared with everyone.  Signing works the opposite direction and is used to verify the source of data. 

To build a signature, the data is first hashed and then encrypted using the private key.  For verification, the hashed data is decrypted using the public key, and the original value is hashed and compared to the decrypted hash.  If the values match, the data is considered to be verified.  While the actual process of constructing the signed data is a bit more involved, this illustrates the basic premise behind it.  Signing does not provide security for your data, but since the private key is required to produce the signed value, the data can be determined to be verified as long as your private key has been kept secret as a hacker will not be able to reproduce the signed value.

Key Size, Padding and Security
While RSA itself is fairly secure, there are some other considerations that should be taken into account.  RSA allows you to specify your key size, or cipher strength.  The stronger the cipher, the more security is provided by the encryption.  RSA operates by performing a series of mathematical operations that begin with two very large pseudoprime numbers.  What makes RSA secure is the inherent difficulty in factoring those numbers.  If a hacker were able to determine those two primes, that hacker could then reproduce your public and private keys and gain access to your data.  The smaller your key size, the smaller those prime numbers will be and the easier it will be for a hacker to break your key.

Let's say for instance that we used two rediculously small prime numbers, 11 and 17 as our values.  The hacker will only have to generate keys with a small number of primes before they are able to successfully guess the two prime values that were used.  By utilizing extremely large primes, RSA makes this process too time consuming to complete.  For instance, if a key size of 1024-bit is specified, the two primes being used will actually be 512-bit numbers.  For a 2048-bit key, they will be 1024-bit numbers.  This is part of what makes RSA encryption a slow and intensive process, however it also makes it secure.  The current recommended encryption strength to use for secure data is 2048-bit.

In addition to the key size, different message padding implementations have been developed.  The potential drawback of not using padding for RSA encryption is that an attacker can use your public key to start encrypting their own known data.  With enough rounds of encrypting different known data, the attacker can begin to determine part of your originally encoded data, and eventually all of it.  To combat this, padding schemes have been developed.  Padding schemes serve a couple of main purposes:

  1. Ensure that your data is always of a fixed length.  For instance, if your key is 1024 bits, the data to be encrypted will always end up being 1024 bits.  This prevets the attacker from knowing the original length of the data you are encrypting.
  2. Add a degree of randomness.  Good padding schemes produce random padding that is added to your data before it is encrypted.  This means that you could encrypt your data many times with the same key and the encrypted result would be different every time.

There are a few different padding standards in existence today.  One of the older standards, and the one that is used by default in Microsoft's RSACSP is the PKCS#1 padding scheme.  While good, it provides less security than more sophisticated padding schemes such as the currently recommended OAEP padding.

Compatibility with .NET's RSACryptoServiceProvider
My RSACrypto class has been built to be compatible with the .NET implementation.  By default, the RSACrypto class uses OAEP padding, but this can be changed using the PaddingProvider property.  While .NET's implementation simply takes a True or False value indicating whether to use OAEP padding, my class uses an instance that implements IPaddingProvider.  This allows the class to be extensible by adding additional padding providers in the future.  For example, I have included 3 different padding providers, PKCS1v1_5, which is the standard PKCS padding implementation, OAEP which is the standard OAEP implementation, and OAEP256 which is based on the standard OAEP implementation but uses a SHA256 hash in the padding generation instead of the default SHA1.  OAEP256 is not compatible with the .NET implementation as it only supports PKCS#1 v1.5 and OAEP.

Similarly, I have included a few different hashing providers that implement an IHashProvider interface.  I have included the hashing algorithims provided by Silverlight:  SHA1, SHA256, HMACSHA1 and HMACSHA256.  The HMAC versions allow you to specify a seed, or private value to use in your hash as well.  The Hash Providers are used in the Signing and Verification of data.  For full compatibility with the .NET framework's RSACSP, either SHA1 or SHA256 should be used.  The .NET implementation takes a string value indicating the type of hash to use. 

For instance, to sign data with a SHA1 hash, you would use:
oRSA.SignData(dataBytes, "SHA1"). 

With my class, you would use:
oRSA.SignData(dataBytes, new SignatureProviders.EMSAPKCS1v1_5_SHA1)

The EMSAPKCS1v1_5_SHA1 Signature Provider uses the SHA1 Hash Provider internally.

And Now...The Code

Now that you have a litte background on RSA, it's time to look at how we implement the RSACrypto class.  The code below illustrates how to use the most common functions of the RSACrypto class.  Once you've added a reference to the DH.Scrypt.dll assembly in your Silverlight project and added an Import / Using for the RSA Namespace you will be able to follow along with the code below.

Creating an Instance
The RSACrypto class currently has two constructors.  The first, an empty constructor, initializes the class with the default cipher strength of 1024 bits.  The second allows you to specify the cipher strength.  The supplied cipher strength must be a multiple of eight and RSACrypto currently supports keys in the range of 256-bit to 4096-bit.  Below is an example of creating an instance of the RSACrypto class that will generate a 2048-bit key.  If you are loading a key from an external source, such as XML, it is not necessary to specify the key size:

 C#

RSACrypto oRSA = new RSACrypto(2048);

VB.NET

Dim oRSA As New RSACrypto(2048)

  

Configuring RSACrypto Properties
The RSACrypto class currently has only two configurable properties.  The first, PaddingProvider, specifies which padding provider to use during encryption.  The second, UseThreads, specifies whether key generation should be performed as a multi-threaded task.  By default, OAEP is used as the padding provider and UseThreads is "True".  UseThreads is recommended for all keys larger than 512-bit as the key generation is a CPU intensive process.  Unless you know you won't receive a performance gain from using multi-threaded key generation, it is recommended to leave this at the default value.

C#

oRSA.PaddingProvider = new PaddingProviders.PKCS1v1_5();

VB.NET

oRSA.PaddingProvider = New PaddingProviders.PKCS1v1_5 

 

Generating Keys
New RSA key pairs can be generated using the GenerateKeys() method.  The GenerateKeys() method also has two additional overloads, allowing you to override the cipher strength the class was initialized with, and to specify your own public exponent value.  The public exponent is a prime number that must also be co-prime with another calculated value in the RSA algorithm.  Common numbers used for the public exponent range between 3 and 65537.  Lower exponents (such as 3) pose a greater security risk.  In almost every case, you should avoid setting your own exponent value and allow the default value to be used.  If you do supply your own public exponent value, it may be automatically adjusted by the RSACrypto class to the closest value compatible with the RSA algorithm.  Key generation is performed asynchronously.  Attempting to perform data operations while a key generation is in progress will result in an exception.  Below are examples of calling the GenerateKeys method and its overloads.

Defining an Event Handler to handle the completion of key generation:

C#

private void KeysGenerated(Object sender)
{
     RSACrypto oRSA = (RSACrypto)sender;
}

VB.NET

Private Sub KeysGenerated(ByVal sender As Object)
     Dim oRSA As RSACrypto = DirectCast(sender, RSACrypto)
End Sub

 

Attaching the KeysGenerated event handler and generating keys:

C#

int cipherStrength = 2048;
int pubExponent = 17;

//Attach the KeysGenerated event handler
oRSA.OnKeysGenerated += KeysGenerated;

//Use default values
oRSA.GenerateKeys();
//Override the cipher strength
oRSA.GenerateKeys(cipherStrength);
//Override cipher strength and public exponent
oRSA.GenerateKeys(cipherStrength, pubExponent);

VB.NET

Dim cipherStrength As Integer = 2048
Dim pubExponent As Integer = 17

'Attach the KeysGenerated event handler
AddHandler oRSA.OnKeysGenerated, AddressOf KeysGenerated

'Use default values
oRSA.GenerateKeys()
'Override the cipher strength
oRSA.GenerateKeys(cipherStrength)
'Override cipher strength and public exponent
oRSA.GenerateKeys(cipherStrength, pubExponent)

 

Importing / Exporting Keys
Keys can be exported to / imported from XML.  These XML keys are compatible with .NET's RSACryptoServiceProvider, so an XML key that is exported from the RSACSP can be imported into the RSACrypto class.  As a note, I've also included an RSAParameters type.  Keys can also be exported to / imported from an RSAParameters instance.

C#

string xmlKeys;

//Export to XML.  Pass "True" to include private key data, "False" for public only.
xmlKeys = oRSA.ToXmlString(true);

//Import the keys from XML
oRSA.FromXmlString(xmlKeys);

VB.NET

Dim xmlKeys As String

'Export to XML.  Pass "True" to include private key data, "False" for public only.
xmlKeys = oRSA.ToXmlString(True)

'Import the keys from XML
oRSA.FromXmlString(xmlKeys)

 

Encrypting / Decrypting
Once your RSACrypto instance has been initialized, you can encrypt and decrypt data.  If no keys have been generated, the RSACrypto class will generate an exception.  To perform data operations you must first make a call to GenerateKeys( ) or import a key pair from XML or an RSAParameters instance.  For this example, we're assuming that you have a Textbox (Textbox1) with the data you want to be encrypted, and another Textbox (Textbox2) where you want the decrypted text to be placed.

C#

//Convert your  text to a byte array
byte[] rawBytes = System.Text.Encoding.UTF8.GetBytes(Textbox1.Text);

//Encrypt your raw bytes and return the encrypted bytes
byte[] encBytes = oRSA.Encrypt(rawBytes);

//Now decrypt your encrypted bytes
byte[] decBytes = oRSA.Decrypt(encBytes);

//Convert your decrypted bytes back to a string
Textbox2.Text = System.Text.Encoding.UTF8.GetString(decBytes, 0, decBytes.Length);

VB.NET

'Convert your text to a byte array
Dim rawBytes() As Byte = System.Text.Encoding.UTF8.GetBytes(Textbox1.Text)

'Encrypt your raw bytes and return the encrypted bytes
Dim encBytes() As Byte = oRSA.Encrypt(rawBytes)

'Now decrypt your encrypted bytes
Dim decBytes() As Byte = oRSA.Decrypt(encBytes)

'Convert your decrypted bytes back to a string
Textbox2.Text = System.Text.Encoding.UTF8.GetString(decBytes, 0, decBytes.Length)

 

Signing / Verification
Often times digital signatures are used to validate product keys and license information.  The example below demonstrates signature generation and verification using SHA256.  This example assumes you have two text boxes, Textbox1 containing the text you are signing, and Textbox2 containing the text you want to verify against the signed data.  If no key data has been loaded the RSACrypto class will generate an exception.  To perform data operations you must first make a call to GenerateKeys( ) or import a key pair from XML or an RSAParameters instance.
Note*: The private key is used to generate the signed data and the public key is used when verifying.  This example assumes you've already loaded the appropriate key data into the RSACrypto class.

C#

//Create an instance of the signature provider that will be used
RSA.SignatureProviders.EMSAPKCS1v1_5_SHA256 sigProvider;
sigProvider = new RSA.SignatureProviders.EMSAPKCS1v1_5_SHA256();

//Convert the text you want to sign into a byte array
byte[] rawBytes = System.Text.Encoding.UTF8.GetBytes(Textbox1.Text);

//Sign your data and return the signed bytes
byte[] signedBytes = oRSA.SignData(rawBytes, sigProvider);

//Convert the text you want to verify against into a byte array
byte[] compareBytes = System.Text.Encoding.UTF8.GetBytes(Textbox2.Text);

//Return a boolean value indicating whether the values matched
bool isMatch = oRSA.VerifyData(compareBytes, signedBytes, sigProvider);

VB.NET

'Create an instance of the signature provider that will be used
Dim sigProvider As New RSA.SignatureProviders.EMSAPKCS1v1_5_SHA256

'Convert the text you want to sign into a byte array
Dim rawBytes() As Byte = System.Text.Encoding.UTF8.GetBytes(Textbox1.Text)

'Sign your data and return the signed bytes
Dim signedBytes() AS Byte = oRSA.SignData(rawBytes, sigProvider)

'Convert the text you want to verify against into a byte array
Dim compareBytes() As Byte = System.Text.Encoding.UTF8.GetBytes(Textbox2.Text)

'Return a boolean value indicating whether the values matched
Dim isMatch As Boolean = oRSA.VerifyData(compareBytes, signedBytes, sigProvider)

 

Future Changes

As a note, currently none of the work is performed asynchronously (aside from some background processing performed during key generation).  I will be re-vamping the key generation process to function async to avoid blocking the user interface and freezing the browser while the keys are being generated.

Key generation is now performed Asynchronously.  In the future I will be adding additional cryptography functionality to the library that doesn't exist in Silverlight, such as support for SHA384, SHA512, 3DES, and various AES implementations.

 

Extensibility and Conclusion

I've tried to design the RSACrypto class to be somewhat extensible.  There are three primary interfaces that can be implemented to extend RSACrypto.   If you would like to add a different hashing scheme, such as MD5, you can Implement the IHashProvider interface.  If you want to create your own SignatureProvider you can implement the ISignatureProvider interface.  And if you want to create your own PaddingProvider, you can implement the IPaddingProvider interface.  The most important notes are as follows:

  1. The signature provider does not encrypt the data, however it performs both the hashing and padding prior to encryption.  Since ISignatureProvider uses IHashProvider, you can easily implement your own signature provider, however you will need to make sure you also implement proper padding.
  2. The PaddingProvider is also executed prior to encrypting the data.  Data is not encrypted inside the PaddingProvider, merely padded.

I hope someone will find this class useful.  If you have any questions or comments please feel free to comment below or contact me via the Contact page.

Sample Application

Here's a quick sample application that leverages the DH.Cryptography library for RSA encryption.  It quickly demonstrates key generation and the encryption / decryption capabilities.  It also illustrates the issue of performing intensive operations synchronously as the browser will temporarily freeze while generating keys (hence the reason for future async development for the library).

Posted in: Silverlight

Tags: , , , ,

Comments (43) -

I'm getting an error:

The type 'RSA.SignatureProviders.EMSAPKCS1v1_5_SHA1' has no constructors defined

... huh?

Reply

We get a 'bad data' error when the byte array 'rawBytes' has above 56 elements when we use:

byte[] encBytes = oRSA.Encrypt(rawBytes);

Is this intended?

Reply

sigProvider = new RSA.SignatureProviders.EMSAPKCS1v1_5_SHA256;
i get error
no constructor defined

Reply

Mark Jones says:

error BC30251: Type 'RSA.SignatureProviders.EMSAPKCS1v1_5_SHA1' has no constructors.

I am trying to Verify a Signature, and VS2010 Silverlight 3 does not like the NEW on the providers

I can't figure out how to pass it to VerifyData

Reply

My apologies for the delay in approving comments and replying.  There was an issue with my blog configuration and I wasn't aware of the unapproved posts.

I have addressed the issue of missing constructors on the signature providers.  Please use Scrypt version 1.0.2.2 which corrects the issue.

Reply

mourad zouabi says:

Hello,

How to laod the public keey from a .cer file ?

Reply

I'm working on an update now to include support for CspBlobImport/Export (for compatibility with the unamanged CAPI provider).  Once that's complete I will look into adding suport for certificate data (CER, DER, CRT).  If you want to attempt it yourself you can, I'm not sure off the top of my head, but I believe .cer files are just the base-64 encoded byte representation of the key data.  You can't simply convert it to text because there may be null characters separating the key elements (I'm not positive on this, I need to check) but you can convert it back to a byte array and parse out the individual values which you would then supply to an RSAParameters instance and use it to load the key data.

Reply

Don't miss Cryptography Structures [msdn.microsoft.com/.../aa380258(v=VS.85).aspx] for additional information.

Reply

Thanks for the link Nick, I'll definitely have to make sure the structures match as closely as possible.  I probably won't get any of these things added until the next major version once I have some more free time to work on it.

Also for everyone's benefit, thanks to Nick it now supports CspBlob import and export.

Reply

If I want to use modulus and exponent as public key, how can i do?

Reply

What format are they in?  Are they in XML format?  If you want to use from the RSACryptoServiceProvider you just use:

string strPubKey = objRSACP.ToXmlString(false);  //only export public key

to import that you just get the key and stor it in a string, then import:
oRSA.FromXmlString(strPubKey);

That will load the public key for you.  If your key is in a different format, such as you're returning the actual number, you'll need to use the included BigInteger class and import into an RSAParameters object:

Utils.BigInteger biModulus = new Utils.BigInteger("xxxx", 10); //<-- your modulus value
Utils.BigInteger biExponent = new Utils.BigInteger("65537", 10); // <-- your exponent value

The , 10 just indicates that this is a base 10 number.  If you're using Hex (base 16) you would use "16" there.  Then in your parameters object:

oParams.N = biModulus.GetBytesRaw();
oParams.E = biExponent.GetBytesRaw();

Or if you're already reading your modulus or exponent in as a byte array you can just assign that to the oParams object.  Then:

oRSA.ImportParameters(oParams);

Reply

Hi,
I was fortunate enough to find your library today after getting the nasty surprise that Silverlight has no RSA capabilities. However, I get the following error when I try to import from xml:

Could not load file or assembly 'System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e' or one of its dependencies. The system cannot find the file specified.

I am using the sample code in the post above - the xml string is the xml generated from the ToXmlString(false) method . How can I fix that?

Also, it is possible to import just the public key from xml?

Reply

Let me take a look at what might be causing the error.  To export just the public key, use ToXmlString(false).

Reply

Thanks for the reply,  I  found the problem a few hours ago and I kind of feel stupid for not thinking about it earlier- I just had to recompile the library for Silverlight 4, I had not noticed that it was originally compiled against an earlier version. Sorry for bothering you. Otherwise, it works great - I am using it to communicate with a php web service that uses this library: http://phpseclib.sourceforge.net/. Great work!

Reply

Andy Philpotts says:

I get different results every time I run, with the same input string! Here is my encrypt code...

        public static string EncryptPassword(string plainText)
        {
            string result;
            try
            {
                RSACrypto crypto = new RSACrypto();
                crypto.FromXmlString(Resources.LIServiceEncryptorPublic);

                byte[] rawBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
                byte[] encrypted = crypto.Encrypt(rawBytes);

                result = Convert.ToBase64String(encrypted);
            }
            catch
            {
                // Trace.TraceError("Encryption of password failed");
                return null;
            }

            return result;
        }

And the key XML (in a resource file) looks like:

"pmL47O8bo2YLXBB28gUzrD+dp7+zXoiMnoq6nUpyxAelW6
uEjEOXdFKkuN3G5droPagWqWshZnNcmYeOL/yvMhSO//BVUUHEyxWaBZkfje0mW8k
lHIYAJx7QoQsi7RBZNPfhr6Sb/QqEfk+3sdKoDYbEKikIQERbfoAvMtPWnZM=

AQAB
"

I run once with "fred" as a password and I get:

"PjI2FpCmGahnKuA/+3IpTIj5kLhdq/B4qPZox/E95pS6fS3NXeDrUx0PA8k0hWaK/iUqj
2h5k1HA5GGDgI+G81keVf85UrTn++9ApiXzAKj1r5tuRPAcN8X3pS18VBTlwFgOjr4e
waoLm3AVO/JQ8EFKl8I92qua84WM/P3JdrI="

run again with "fred" I get

"QLozqbgDwvwLs1GLQ6n6aU489kfzksV3qym8mCshU3Ld5WOVajpgdX3GbikjyC7
oyuOGvRHT7tXD6HX1SdoDBT0WrCBEKCSCfJds7ga31yQbTcV0F+p///TZaawY2Ix
d45bJzXaZoiQODyXqUIODgXS2TOPUpJ8XaEJPSc562ZE="

Am I going crazy?

Reply

Andy -

You're not going crazy, that is the correct result. Smile  It is actually the result of random padding (i.e. OAEP).  The padding is intended to produce a different result on each run.  After decryption, the padding is removed.

Reply

Newbie question: I encrypt the inputstring, and get the encrypted bytearray. Then I convert this bytearray to the  encryptedstring as in Andy's post. How can I get the original inputstring from this encrytedstring?

When I try it, I get the Decrysption Error. Bad Data.

Thank You in advance

Gabriel

Reply

Thanks for your sharing, to solve the wp7 loopholes. and I ask, how to achieve the 3des

Reply

As far as I know TripleDES is not currently available in WP7.  I'll look it over and think about implementing it if I get time.

Reply

hi,
First let me thank you for the great work.
I have tried your code and successfully created the public key and encrypt data.
Sorry to say but i don't have that much of knowledge in RSA.
i need to pass the DER value of generated Public Key as parameter in one of service call.
and can't understand how to find DER value from generated Public key.

can you please provide some light on it?

Regards,
Joyous Suhas

Reply

Eldon Elledge says:

First, Thank you. Great work.

I have two apps, one is a console app using the .Net for encryption rsa = new RSACryptoServiceProvider(1024);
public static string EncryptData(string data2Encrypt, string publicKey)
        {
            rsa.FromXmlString(publicKey);

            //read plaintext, encrypt it to ciphertext  
            byte[] plainbytes = System.Text.Encoding.UTF8.GetBytes(data2Encrypt);
            byte[] cipherbytes = rsa.Encrypt(plainbytes, true);
            return Convert.ToBase64String(cipherbytes);
        }


I use your library in a LightSwitch (silverLight) app to genirate the Key Set and decrypt.
rsa.GenerateKeys(1024);

I decrypt with the following code. The last line always returns an error "Bad Data"
rsa.FromXmlString(privateKey);
            byte[] rawBytes = System.Text.Encoding.UTF8.GetBytes(password);
            byte[] dncBytes = rsa.Decrypt(rawBytes);

Reply

DustinHorne says:

One thing I'd like to note... you don't need to call rsa.GenerateKeys.  That method is only necessary if you're generating a new key pair.  I definitely want to re-test this with your scenario.  There are a couple of things that I want to look at... mainly making sure that calling GenerateKeys before loading from an xml string doesn't do something I didn't anticipate.  

The other thing I need to know though is where you're calling GenerateKeys.  Key generation is performed asynchronously to improve performance on Silverlight and WP7.  If you're calling GenerateKeys and immediately trying to decrypt, you may be attempting to perform a decryption operation while key generation is in progress.

These are fun to debug because of the generic "Bad Data" messages, however this was to coply with the RSA spec... the less information you give a potential hacker the better.

Reply

Eldon Elledge says:

  Thanks for quick reply.
  I call the GenirateKeys in a Button Click event (Only as needed, not a regular step). I get the keys from the KeysGenirated event. I put the public key in a database record and the private key is stored on a jump drive.
  The console app pulls the public key from the database to encrypted a randomly genirated password and write the encrypted password back to the database.
  I then use the SiverLight application to pullup the list of encrypted passwords from the SQL server database. Once a password is selected, I click the "Get Password" button, from here I pull the private key from the jump drive and perform the decryption.
  I can provide Screen shots or more code samples if you would like. I just don't want you to debug my application. :o)

Reply

Eldon Elledge says:

  I have resolved my issue. It was caused by using the Convert.ToBase64String(cipherbytes);
is the console app while using System.Text.Encoding.UTF8.GetBytes(password);
in he SilverLight app.
   As soon as I made the change to the SilverLight app, problem solved.
   Thanks again for your great work on this library

Reply

Ahh because the console app isn't UTF8.  Probably ASCII or possible Unicode, not really sure.

And thanks, glad it's helpful and hope it does everything that you need!  Don't hesitate to reach out if you need assistance.

Reply

Greate work! I hope X509Certificate2 could be used in silverlight.

Reply

I'm hoping I get a chance to look into it.  I've been concentrating on my XNA tutorial series at the moment and also have a report that the library isn't producing correct results under Mango so I have to do some digging.

Reply

Great work, i made a mistake first time. RSACrypto does not provide SignHash and VerifyHash. I didn't realize the difference with SignData and VerifyData at first.After 2 hour's debugging, i finally realize the problem. Oops!

Reply

Moisés Medeiros says:

Hi,

First let me say that I don't have much knowledge in RSA but I was looking for a way to sign invoice documents and I found your blog. I have made some tests using your assembly and I think it has almost everything that I need. What I am missing is that I have the keys in pem format in external files and I am not sure if it is possible to import them into the RSA object.
Can I use your solution with pem formatted keys?

Thank you!

Reply

Currently the library doesn't support direct import of PEM formatted keys, but you could decode the PEM data (it's base64 encoded) and manually extract the keys pretty easily I think.  Here's a link to some good info on converting the PEM keys to be usable by the RSACryptoServiceProvider in .NET.  The same would apply to my library:
social.msdn.microsoft.com/.../

Reply

Moisés Medeiros says:

Thanks for the quick reply.

I can get the key from the external file and then decode it to a byte array using Convert.FromBase64String. Then I should use the RSAParameter class to import to the RSACrypto object? How do I set the RSAParameters object properties D, DP, DQ, E... with my byte array?
Probably what I am asking is obvious but has I said before I don't understand much of RSA, I just recently started investigating on that subject. Thanks.

Reply

Honestly I'm not sure exactly how it's stored.  I've used PEM files on multiple occasions but never had to decode one, but I can tell you this... once you've decoded the contents between the begin and end rsa sections, the byte array you have will contain all of the key data you need.  Try converting that byte array to a UTF-8 string and write it out to a textbox and see what it looks like.  Somehow you'll need to separate the key data into separate byte arrays and use those to reconstruct using the RSAParameters class.

Reply

Moisés Medeiros says:

Hi,

Thanks for the help, I really appreciate it. I also found a way to decode pem files. I'll post it here in case someone find it useful too. The complete example can be found in http://www.jensign.com/opensslkey/opensslkey.cs

public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
        {
            byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;

            // ---------  Set up stream to decode the asn.1 encoded RSA private key  ------
            MemoryStream mem = new MemoryStream(privkey);
            BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading
            byte bt = 0;
            ushort twobytes = 0;
            int elems = 0;
            try
            {
                twobytes = binr.ReadUInt16();
                if (twobytes == 0x8130)  //data read as little endian order (actual data order for Sequence is 30 81)
                    binr.ReadByte();  //advance 1 byte
                else if (twobytes == 0x8230)
                    binr.ReadInt16();  //advance 2 bytes
                else
                    return null;

                twobytes = binr.ReadUInt16();
                if (twobytes != 0x0102)  //version number
                    return null;
                bt = binr.ReadByte();
                if (bt != 0x00)
                    return null;


                //------  all private key components are Integer sequences ----
                elems = GetIntegerSize(binr);
                MODULUS = binr.ReadBytes(elems);

                elems = GetIntegerSize(binr);
                E = binr.ReadBytes(elems);

                elems = GetIntegerSize(binr);
                D = binr.ReadBytes(elems);

                elems = GetIntegerSize(binr);
                P = binr.ReadBytes(elems);

                elems = GetIntegerSize(binr);
                Q = binr.ReadBytes(elems);

                elems = GetIntegerSize(binr);
                DP = binr.ReadBytes(elems);

                elems = GetIntegerSize(binr);
                DQ = binr.ReadBytes(elems);

                elems = GetIntegerSize(binr);
                IQ = binr.ReadBytes(elems);

                Console.WriteLine("showing components ..");
                if (verbose)
                {
                    showBytes("\nModulus", MODULUS);
                    showBytes("\nExponent", E);
                    showBytes("\nD", D);
                    showBytes("\nP", P);
                    showBytes("\nQ", Q);
                    showBytes("\nDP", DP);
                    showBytes("\nDQ", DQ);
                    showBytes("\nIQ", IQ);
                }

                // ------- create RSACryptoServiceProvider instance and initialize with public key -----
                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
                RSAParameters RSAparams = new RSAParameters();
                RSAparams.Modulus = MODULUS;
                RSAparams.Exponent = E;
                RSAparams.D = D;
                RSAparams.P = P;
                RSAparams.Q = Q;
                RSAparams.DP = DP;
                RSAparams.DQ = DQ;
                RSAparams.InverseQ = IQ;
                RSA.ImportParameters(RSAparams);
                return RSA;
            }
            catch (Exception)
            {
                return null;
            }
            finally { binr.Close(); }
        }

Reply

Thanks for the update!  I'll check out the website and contact the author to see if I can include this in the library.  If I can, I'll consider adding support for PEM file import.

Reply

I successfully encrypt the string with the code below:

        public string EncryptPassword(string plainText)
        {
            string result;
            try
            {
                crypto.FromXmlString(Strings.publicKey);

                byte[] rawBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
                encryptedBytes = crypto.Encrypt(rawBytes);

                result = Convert.ToBase64String(encryptedBytes);
            }
            catch (Exception e)
            {
                // Trace.TraceError("Encryption of password failed");
                return null;
            }

            return result;
        }

When I try to decrypt with the code below, at the row "byte[] decBytes = crypto.Decrypt(rawBytes);"get an error: Bad Data"

        public string DecryptPassword(string paswd)
        {
            string result;
            try
            {
                crypto.FromXmlString(Strings.privateKey);

                byte[] rawBytes = System.Text.Encoding.UTF8.GetBytes(paswd);

                byte[] decBytes = crypto.Decrypt(rawBytes);
                
                result = Convert.ToBase64String(decBytes);
            }
            catch (Exception e)
            {
                // Trace.TraceError("Encryption of password failed");
                return null;
            }

            return result;
        }

The stack:
RSA.PaddingProviders.OAEP.DecodeMessage(Byte[] dataBytes, RSAParameters params)

RSA.RSACrypto.RemoveEncryptionPadding(Byte[] dataBytes)
  
RSA.RSACrypto.Decrypt(Byte[] encryptedBytes)

MyApp.Controls.ChildWindows.LoginWindow.DecryptPassword(String paswd)

Does anybody can point me to the right direction, how to decrypt generated encrypted string?
Thanks

Reply

Does this thread is dead?

Reply

It's not dead, I've just been very busy.  There could be any number of things wrong... your string might not actually be UTF-8 encoded... or there may be something wrong with your private key... If you could send me a link to your project using my Contact page I could take a look at it for yoy.

Reply

Thank You very much Dustin, and apologize for my impatience. I will create a small demo project and send you to better demonstrate my problem. Thanks in advance
Gabriel

Reply

The problem solved.

What I wanted to do is to send the encrypted string and not the byte array to the decryptor.

(The RSACsypto object, the key generation, the xmlPublicKey and the xmlKey strings declarations are omitted for brevity)

/////////////////////////////////     Encryptor

crypto.FromXmlString(xmlPublicKey);

string plainText = "myPassword";

byte[] plainBytes = System.Text.Encoding.UTF8.GetBytes(plainText);

encryptedBytes = crypto.Encrypt(plainBytes);

//Doesn't work
encryptedString = System.Text.Encoding.UTF8.GetString(encryptedBytes, 0, encryptedBytes.Length);

//Works
encryptedString = Convert.ToBase64String(encryptedBytes);

/////////////////////////////////      Decryptor

crypto.FromXmlString(xmlKeys);

//Doesn't work
byte[] pswdArray = System.Text.Encoding.UTF8.GetBytes(encryptedString);

//Works
byte[] pswdArray = Convert.FromBase64String(encryptedString);

byte[] decBytes = crypto.Decrypt(pswdArray);

result = System.Text.Encoding.UTF8.GetString(decBytes, 0, decBytes.Length);

The point here, to use the Convert.(To/From)Base64String when transmitting byte array via string.  
Thanks Gabor

Reply

What's the format of the xml key strings?
I'd need it to import a public key generated by ssh-keygen.

Reply

They keys are in the same format used by Microsoft's RSACryptoServiceProvider:

<RSAKeyValue>
   <Modulus>…</Modulus>
   <Exponent>…</Exponent>
   <P>…</P>
   <Q>…</Q>
   <DP>…</DP>
   <DQ>…</DQ>
   <InverseQ>…</InverseQ>
   <D>…</D>
</RSAKeyValue>

msdn.microsoft.com/.../...phy.rsa.toxmlstring.aspx

Reply

Thanks!
I have also found this article on how to extract the modulus and the exponent from id_rsa.pub files and convert them for .NET:
43n141e.blogspot.com/.../...-ruby-to-c-and_27.html

Reply

Thanks for the share!

Reply

Pingbacks and trackbacks (2)+

Add comment

biuquote
  • Comment
  • Preview
Loading