|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionMicrosoft's .NET framework has robust support for encryption in the System.Security.Cryptography namespace. Everything you need to
perform encryption is available in that class, but it's difficult to understand
unless you have a firm grasp of cryptographic theory. Over the last four months,
I've struggled with the concepts and theory behind encrypting and decrypting
data. I've wrapped all my derived knowledge into a class I call
BackgroundThere are three essential cryptographic concepts represented in the
These three concepts are heavily intertwined and always seen together in modern cryptography. They have different strengths and weaknesses; combining them offers a much higher level of security than can be achieved using a single method alone. For example, when digitally transmitting a check to your bank, all three of these methods are used:
Image reprinted from Entrust's Introduction to Cryptography and Digital Signatures PDF.
In order to open the check, these steps are simply performed in the reverse order by the recipient. Note that if any of these steps were missing, the transaction would have significant weaknesses that could be exploited! Encryption.HashLet's start with the simplest operation-- Hashing the string "Hash Browns": Dim h As New Encryption.Hash(Encryption.Hash.Provider.CRC32)
Dim d As New Encryption.Data("Hash Browns")
h.Calculate(d)
Console.WriteLine(".ToHex = '" & h.Value.ToHex & "'")
Console.WriteLine(".ToBase64 = '" & h.Value.ToBase64 & "'")
Console.WriteLine(".ToString = '" & h.Value.ToString & "'")
The unique data fingerprint of the string "Hash Browns" using the CRC32 algorithm is
32 bits or 4 bytes in length. We have a custom data type,
.ToHex = 'FDBFBC6D'
.ToBase64 = '/b+8bQ=='
.ToString = 'y¿¼m'
It doesn't make much sense to display an array of raw bytes using the
The CRC32 hash is not a good choice for security work; it's optimized for speed and detection of machine transmission errors. It would be relatively easy for a knowledgeable human hacker to generate a string that produces the same CRC32 hash. Let's take a look at a slower, but more secure hash: SHA1. Dim h As New Encryption.Hash(Encryption.Hash.Provider.SHA1)
Dim d As New Encryption.Data("Hash Browns")
Dim salt As New Encryption.Data("NaCl")
h.Calculate(d, salt)
Console.WriteLine(h.Value.ToHex)
Console.WriteLine(h.Value.ToBase64)
SHA1 produces a much longer and more tamper-resistant 160-bit hash code. .ToHex = '95CF26B3BB0.F377347B6D414951456A16DD0CF5F'
.ToBase64 = 'lc8ms7sPN3NHttQUlRRWoW3Qz18='
Notice the salt I added? Hashes are commonly used to avoid plain-text storage of passwords in a database. You calculate the hash of the password and store the hash instead of the actual password. When the user types in their password, hash it, then compare it against the stored hash in the database. It's clever, but there is a vulnerability: you can still mount a dictionary attack by hashing the English dictionary and matching it against the hashes stored in the database. We can prevent this by adding a salt-- a unique string-- to every password before hashing it. You'd typically salt with some arbitrary value from the same record, such as the record ID, user's birthday, or a GUID. It doesn't really matter what your salt is, as long as it makes the values unique. By adding the salt as shown above, we are effectively hashing the string "NaClHash Browns" instead of "Hash Browns". Good luck finding "NaClHash" in a dictionary! Also note that string representations aren't particularly efficient; it takes 40 characters to represent the 160 bit (20 byte) hash in string using Hexadecimal, and 28 characters to represent that same hash using Base64 encoding. If you don't need to display your data in semi-human readable format, stick to binary formats. But the textual representations sure are convenient for use in XML or .config files! We're not limited to Dim sr As New IO.StreamReader("c:\test.txt")
Dim h As New Encryption.Hash(Encryption.Hash.Provider.MD5)
Console.WriteLine(".ToHex = '" & h.Calculate(sr.BaseStream).ToHex & "'")
sr.Close()
So the file test.txt has an MD5 hash of: .ToHex = '92C7C0F251D98DEA2ACC49B21CF08070'
Let's see what happens if we add a single space character to test.txt, and hash it again: .ToHex = 'FADECF02C2ABDC7B65EBF2382E8AC756'
One of the defining properties of a hash is that small changes in the source bytes produce big differences in the resulting hash bytes. All hashes have the same purpose: to digitally fingerprint code. However,
there are different speed and security tradeoffs for each
Encryption.SymmetricSymmetric encryption is the most familiar kind of encryption; you have a single secret key which is used to both encrypt and decrypt: Dim sym As New Encryption.Symmetric(Encryption.Symmetric.Provider.Rijndael)
Dim key As New Encryption.Data("My Password")
Dim encryptedData As Encryption.Data
encryptedData = sym.Encrypt(New Encryption.Data("Secret Sauce"), key)
Dim base64EncryptedString as String = encryptedData.ToBase64
We now have some Rijndael encrypted bytes, expressed as a Base64 string. Let's decrypt them: Dim sym As New Encryption.Symmetric(Encryption.Symmetric.Provider.Rijndael)
Dim key As New Encryption.Data("My Password")
Dim encryptedData As New Encryption.Data
encryptedData.Base64 = base64EncryptedString
Dim decryptedData As Encryption.Data
decryptedData = sym.Decrypt(encryptedData, key)
Console.WriteLine(decryptedData.ToString)
Like the Dim sym As New Encryption.Symmetric(Encryption.Symmetric.Provider.TripleDES)
Dim key As New Encryption.Data("My Password")
Dim fs As New IO.FileStream("c:\test.txt", IO.FileMode.Open,
IO.FileAccess.Read)
Dim br As New IO.BinaryReader(fs)
Dim encryptedData As Encryption.Data
encryptedData = sym.Encrypt(br.BaseStream, key)
br.Close()
Dim sym2 As New Encryption.Symmetric(Encryption.Symmetric.Provider.TripleDES)
Dim decryptedData As Encryption.Data
decryptedData = sym2.Decrypt(encryptedData, key)
There are a few things to remember when using the
.NET provides four different
Encryption.AsymmetricAsymmetric encryption requires the use of two keys: one public, one private, together known as a "keyset". Let's generate a new keyset and encrypt some data: Dim asym As New Encryption.Asymmetric
Dim pubkey As New Encryption.Asymmetric.PublicKey
Dim privkey As New Encryption.Asymmetric.PrivateKey
asym.GenerateNewKeyset(pubkey, privkey)
Dim secret As String = "ancient chinese"
Dim encryptedData As Encryption.Data
encryptedData = asym.Encrypt(New Encryption.Data(secret), pubkey)
Dim decryptedData As Encryption.Data
Dim asym2 As New Encryption.Asymmetric
decryptedData = asym2.Decrypt(encryptedData, privkey)
Note that we used the public key to encrypt, and the private key to decrypt. Although you can certainly generate as many new public/private keysets as you
want, you'll typically load an existing keyset. To facilitate loading and saving
of keys, the <appSettings>
<add key="PublicKey.Modulus"
value="3uWxbWSnlL2ntr/gcJ0NQeiWRfzj/72zIDuBW/TmegeodMdPUvI5vXur0fKp
6RbSU112oPf9o7hoAF8bdR9YOiJg6axZYKh+BxEH6pUPLbrtn1dPCUgTxlMeo0IhKvi
h1Q90Bz+ZxCp/V8Hcf86p+4LPeb1o9EOa01zd0yUwvkE=" />
<add key="PublicKey.Exponent"
value="AQAB" />
<add key="PrivateKey.P"
value="76iHZusdN1TYrTqf1gExNMMWbiHS7zSB/bi/xeUR0F3fjvnvsayn6s5ShM0jx
YHVVkRyVoH16PwLW6Tt2gpdYw==" />
<add key="PrivateKey.Q"
value="7hiVRmx0z1KERw+Zy86MmlvuODUsn2kuM06kLsSHbznSkYl5lekH9RFxFemNk
GGMBg8OT5+EVtWAOdto8KTJCw==" />
<add key="PrivateKey.DP"
value="ksvo/EqBn9XRzvH826npSQdCYv1G5gyEnzQeC4qPidEmUb6Yan12cWYlt4CsK
5umYGwWmRSL20Ufc+gnZQo6Pw==" />
<add key="PrivateKey.DQ"
value="QliLUCJsslDWF08blhUqTOENEpCOrKUMgLOLQJT3AGFmcbOTM9jJpNqFXovEL
NVhxVZwsHNM1z2LC5Q+O8BPXQ==" />
<add key="PrivateKey.InverseQ"
value="pjEtLwYB4yeDpdORNFxhFVXWZCqoky86bmAnrrG4+FvwkH/2dNe65Wmp62JvZ
7dwgPBIA+uA/LF+C1LXcXe9Aw==" />
<add key="PrivateKey.D"
value="EmuZBhlTYA9sVMX2nlfcSJ4YDSChFvluXDOOtTK/+UW4vi3aeFhcPTSDNo5/T
Cv+pbULoLHd3DHZJm61rjAw8jV5n09Trufg/Z3ybzUrAOzT3iTR2rvg7mNS2IBmaTyJg
emNKQDeFW81UOELVszUXNjhVex+k67Ma4omR6iTHSE=" />
</appSettings>
The private key is a superset of the public key; it can be used for both
encryption and decryption, whereas the public key can only be used for
encryption. Once a key is placed in the Dim encryptedData As Encryption.Data
Dim decryptedData As Encryption.Data
Dim asym As New Encryption.Asymmetric
Dim asym2 As New Encryption.Asymmetric
Dim secret As String = "Michael Bolton"
encryptedData = asym.Encrypt(New Encryption.Data(secret))
decryptedData = asym2.Decrypt(encryptedData)
Console.WriteLine(decryptedData.ToString)
Note that we didn't specify any keys here; everything was automatically
absorbed from the There are a few caveats when using
The Annoying File Dependency in Encryption.AsymmetricUnfortunately, Microsoft chose to provide some System.Security.Cryptography functionality through the existing COM-based CryptoAPI. Typically this is no big deal; lots of things in .NET are delivered via COM interfaces. However, there is one destructive side effect in this case: asymmetric encryption, which in my opinion should be an entirely in-memory operation, has a filesystem "key container" dependency:
Even worse, this weird little "key container" file usually goes to the current user's folder! I have specified a machine folder as documented in this Microsoft knowledge base article. Every time we perform an asymmetric encryption operation, a file is created and then destroyed in the C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys folder. It is simply unavoidable, which you can see for yourself by opening this folder and watching what happens to it when you make asymmetric encryption calls. Make sure whatever account .NET is running as (ASP.NET, etc.) has permission to this folder! ConclusionEncryption is a deep, complicated subject. I hope this article and the accompanying classes made it at least a little more approachable. Please don't hesitate to provide feedback, good or bad! If you enjoyed this article, you may also like my other articles as well. History
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||