RSA Encryption in

advertisement
RSA Encryption in .NET -- Demystified!
By Peter A. Bromberg, Ph.D.
When I first started studying the CryptoGraphic classes in the .NET Platform, I had some difficulty
understanding the documentation. At first I just felt a little embarrassed about my difficulties (after
all, I've got a postgraduate degree and I'm a Mensa guy). But then as I continued on, I began to
realize it wasn't so much me at all - fact is, the MS documentation in this particular area is simply
obtuse! I don't know what they were thinking when they wrote the docs for the .NET Cryptographic
Provider classes, but they sure weren't targeting it at newbies to encryption!
After a little study, I found it pretty easy to use the RC2, DES and other Symmetric encryption
classes (see my article here on XML-safe encryption). Still, the RSA Asymmetric classes with all
the CSPParameters and Public and Private key stuff just threw me for a loop! And it seems, no
matter where I searched, nobody had written any good examples of how to use them.
Undaunted, I studied on and I think I've finally arrived at the point of being able to comfortably use
this stuff and write meaningful working code with it. Not only that, but I believe I've also
internalized a pretty good way to explain it all without sounding like I'm regurgitating that awful MS
documentation (don't get me wrong, most of MS's documentation is first-rate).
As you probably already know, symmetric cryptographic algorithms use a single "key" for
encryption / decryption. You either know the key or you don't. Because of this they are faster and
more suited to one-pass encryption or decryption of larger amounts of data.
However getting that single key to a user in a secure way may be very difficult. With Public Key
encryption, we generate a Public Key and a corresponding Private key. The way the algorithms
work is that the private key can only be used to decrypt information that has been encrypted using its
matching public key. Conversely, the public key can only decrypt information encrypted with the
private key. Asymmetric encryption/ decryption is notably slower than symmetric and therefore is
more suited for small amounts of data. Now why would this be of value?
Typically, a system that uses public key encryption would make its public key freely available. They
would, however, guard the corresponding private key very carefully. Users could therefore send
private data encrypted with the public key, and only the intended recipient - who must possess the
matching private key - would be able to decrypt and use it. Private keys are also used to verify that a
message is from the actual sender, because the sender is the only one who has the private key. That's
essentially how XMLSignature works.
So for example, a system, which makes its public key freely available (as with a WebMethod) might
receive a user's message containing login information that is encrypted using the public key. It
would then use its private key to decrypt this information, compare the user and password against its
database, and once authenticated, use the user's decrypted password ( or some similar combination)
as the "KEY" to symmetrically encrypt its response to the user. Since the user is the only one who
knows her own password, she can decrypt the response securely.
The "KEY" to RSA Encryption
Whenever you create a new default constructor instance of the RSACryptoServiceProviderclass, it
automatically creates a new set of public / private key information, ready to use. However, if you
want to re-use previously created keys, you can do this by initializing the class with a populated
CspParameters object.
For example [VB.NET]:
Dim cspParam as CspParameters = new CspParameters()
cspParam.Flags = CspProviderFlags.UseMachineKeyStore
Dim RSA As System.Security.Cryptography.RSACryptoServiceProvider
= New System.Security.Cryptography.RSACryptoServiceProvider(cspParam)
The key information from the cspParam object above can be saved via:
Dim publicKey as String = RSA.ToXmlString(False) ' gets the public key
Dim privateKey as String = RSA.ToXmlString(True) ' gets the private key
The above methods enable you to convert the public and / or private keys to Xml Strings. And of
course, as you would guess, there is a corresponding FromXmlString method to get them back. So
to encrypt some data with the Public key:
Dim str as String = "HelloThere"
Dim RSA2 As RSACryptoServiceProvider = New RSACryptoServiceProvider(cspParam)
' ---Load the public key--RSA2.FromXmlString(publicKey)
Dim EncryptedStrAsByt() As Byte =RSA2.Encrypt(System.Text.Encoding.Unicode.GetBytes(str),
False)
Dim EncryptedStrAsString = System.Text.Encoding.Unicode.GetString(EncryptedStrAsByt)
and as a "proof of concept", to DECRYPT the same data, but now using the PRIVATE key:
Dim RSA3 As RSACryptoServiceProvider = New RSACryptoServiceProvider(cspParam)
'---Load the PRIVATE key--RSA3.FromXmlString(privateKey)
Dim DecryptedStrAsByt() As Byte
=RSA3.Decrypt(System.Text.Encoding.Unicode.GetBytes(EncryptedStrAsString), False)
Dim DecryptedStrAsString = System.Text.Encoding.Unicode.GetString(DecryptedStrAsByt)
Now here is an example of an ASPX page that "pulls it all together", and caches the cspParam
object in Application state:
<%@ Import Namespace="System.Security.Cryptography" %>
<script language="VB" runat="server">
Sub Page_Load(sender as Object, e as EventArgs)
Dim cspParam as CspParameters
If TypeOf(Application("cspparam")) is CspParameters then
Response.Write("Application Variable: " & Application("cspparam").ToString
& "<BR>")
cspParam =CType(Application("cspParam"), CspParameters)
else
' Note: When you are using the application from within ASP.NET, you are
not an
'interactive user. Windows needs to use the key container from the user's
' profile for RSA service provider. The profile is not loaded for
' non-interactive users, so you need to use the information stored on the
' local(machine)'s keystore with the cspParam flag as shown in the next 3
lines.
' You then pass the initialized CspParameters object in the contructor to
the RSACryptoServiceProvider
cspParam= New CspParameters()
cspParam.Flags = CspProviderFlags.UseMachineKeyStore
' store in Application state so we only need to create this once
Application("cspparam")=cspParam
End if
Dim RSA As System.Security.Cryptography.RSACryptoServiceProvider = New
System.Security.Cryptography.RSACryptoServiceProvider(cspParam)
Dim publicKey as String = RSA.ToXmlString(False) ' gets the public key
Dim privateKey as String = RSA.ToXmlString(True) ' gets the private key
Response.Write("<Textarea rows=10 cols=100>PUBLIC: " & publicKey &
"</TextArea>")
Response.Write("<Textarea rows=10 cols=100>PRIVATE: " & privateKey &
"</Textarea>")
Response.Write("<BR>Encrypting the string ""HelloThere"" with the PUBLIC
Key:<BR>")
Dim str as String = "HelloThere"
Dim RSA2 As RSACryptoServiceProvider = New
RSACryptoServiceProvider(cspParam)
'---Load the public key--RSA2.FromXmlString(publicKey)
Dim EncryptedStrAsByt() As Byte
=RSA2.Encrypt(System.Text.Encoding.Unicode.GetBytes(str), False)
Dim EncryptedStrAsString =
System.Text.Encoding.Unicode.GetString(EncryptedStrAsByt)
Response.Write( "<Textarea rows=10 cols=100>Encrypted String: " &
EncryptedStrAsString & "</Textarea>")
Response.Write("<BR>Decrypting the Encrypted String with the PRIVATE
key:<BR>")
Dim RSA3 As RSACryptoServiceProvider = New
RSACryptoServiceProvider(cspParam)
'---Load the PRIVATE key--RSA3.FromXmlString(privateKey)
Dim DecryptedStrAsByt() As Byte
=RSA3.Decrypt(System.Text.Encoding.Unicode.GetBytes(EncryptedStrAsString),
False)
Dim DecryptedStrAsString =
System.Text.Encoding.Unicode.GetString(DecryptedStrAsByt)
Response.Write( "<Textarea rows=10 cols=100>Decrypted String: " &
DecryptedStrAsString & "</Textarea>")
End Sub
</script>
You can see that I test for the Application variable holding the CspParameters and, if necessary,
initialize one and store it in the Application variable. In this manner we always have the same public
/ private key pair, and we only need to create the keys once. Of course, you could go one step further
and save the ToXmlString values on the hard drive, then simply reload them each time. And here is
a link to a working live version of the above page so you can try it out: Also, I'd lke to thank Shawn
Steele of Microsoft who suggested changing my original choice of
Encoding.Default.GetBytes/String to Encoding.UTF8.GetBytes/String (or Unicode, which is my
choice here). The reason is that Encoding.Default provides the windows default code page behavior
for your machine (so it can be different if you run it on different machines), and also it maps some
characters to their “best-fit” counterparts if they don’t exist in that code page. For example, É and È
could both be best fit to E in some code pages. That probably isn’t good behavior for encryption.
UTF8 (or Unicode) provides mappings for all characters, so the best fit issue isn’t a problem.
Explicitly stating the code page prevents non-ASCII character gibberish if its decoded on another
machine with a different system locale.
Really, That's about 90% of all you need to know about RSA encryption. Sure, there are finer details
to learn along the way, but if you can understand everything in this article you've pretty much got it
covered. I only wish there was somebody out there that had an article like this I could read when I
needed it! And so I leave you with this little gem:
"If you want creative workers, give them enough time to play."
--John Cleese
Peter Bromberg is an independent consultant who has worked in the banking and financial industry for 20 years.
He has architected and developed web - based corporate distributed application solutions since 1995,
and currently focuses on the .NET Platform. Peter can be reached at pbromberg@yahoo.com Pete Says: "Post your
questions and comments to our FORUMS!"
Download