Click here to Skip to main content
13,042,421 members (138,431 online)
Click here to Skip to main content
Add your own
alternative version


584 bookmarked
Posted 18 Apr 2005

.NET Encryption Simplified

, 28 Jan 2007
Rate this:
Please Sign up or sign in to vote.
A simple, string-oriented class for symmetric encryption, asymmetric encryption, and hashing.


Microsoft'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 Encryption. This class is heavily documented, string oriented, and most of all, simple! It's ideal for learning more about encryption.


There are three essential cryptographic concepts represented in the Encryption namespace. It's important that every developer understands these concepts before proceeding any further:

  1. Hashing

    Hashes aren't encryption, per se, but they are fundamental to all other encryption operations. A hash is a data fingerprint - a tiny set of bytes that represents the uniqueness of a much larger block of bytes. Like fingerprints, no two should ever be alike, and a matching fingerprint is conclusive proof of identity. A full discussion of hashes is outside the scope of this article, but I highly recommend Steve Friedl's Illustrated Guide to Cryptographic Hashes for more background.

  2. Symmetric Encryption

    In symmetric encryption, a single key is used for encrypting and decrypting the data. This type of encryption is quite fast, but has a severe problem: in order to share a secret with someone, they have to know your key. This implies a very high level of trust between people sharing secrets; if an unscrupulous person has your key-- or if your key is intercepted by a spy-- they can decrypt all the messages you send using that key!

  3. Asymmetric Encryption

    Asymmetric encryption solves the trust problem inherent in symmetric encryption by using two different keys: a public key for encrypting messages, and a private key for decrypting messages. This makes it possible to communicate in secrecy with people you don't fully trust. If an unscrupulous person has your public key, who cares? The public key is only good for encryption; it's useless for decryption. They can't decrypt any of your messages! However, asymmetric encryption is very slow. It's not recommended for use on more than roughly 1 kilobyte of data.

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.

  • A hash of the check is calculated.
  • The hash is encrypted with our public key using asymmetric encryption.
  • The encrypted hash is appended to the document.
  • The document is encrypted using a unique one-time symmetric encryption key.
  • The one-time symmetric encryption key is encrypted with the recipient's public key using asymmetric encryption.
  • The encrypted key and encrypted document are transmitted to the recipient.

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!


Let'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")
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, Encryption.Data, to aid us in converting those 4 bytes to and from familiar string representations:

.ToHex = 'FDBFBC6D'
.ToBase64 = '/b+8bQ=='
.ToString = 'y¿¼m'

It doesn't make much sense to display an array of raw bytes using the .ToString method; that's shown only for illustrative purposes. You'll want raw byte values displayed either as Hexadecimal or Base64 encoded. If necessary, you can get to the raw byte representation via the Encryption.Data.Bytes array.

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)

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 Encryption.Data byte arrays of fixed length. We can also calculate the hash of an IO.Stream of any arbitrary size:

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 & "'")

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 Hash.Provider:

ProviderLength (bits)SecuritySpeed


Symmetric 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)

Like the Encryption.Hash class, this also works for any arbitrarily-sized IO.Stream as well as the fixed size Encryption.Data:

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, 
Dim br As New IO.BinaryReader(fs)
Dim encryptedData As Encryption.Data
encryptedData = sym.Encrypt(br.BaseStream, key)
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 Encryption.Symmetric class:

  • All symmetric encryption is currently performed in memory. Be careful when encrypting extremely large files!
  • .NET always chooses the largest available key size by default. If you want to manually specify a smaller key size, use the .KeySizeBytes or .KeySizeBits properties.
  • The key is optional in the .Encrypt method. If you don't provide a key, a key of appropriate length will be auto generated for you and it can be retrieved via the .Key property. It won't be fun to pronounce, because it'll be a randomly generated array of bytes, but it'll sure be hard to guess!
  • The .InitializationVector property is completely optional. The symmetric algorithms are block-oriented and seed the next block with the results from the previous block. This means the very first block has no seed, so that's where the IV comes in. It's annoying to have to remember both a password and an initialization vector to decrypt your data, and I don't think this is a serious weakness, so I recommend accepting the default initialization vector.

.NET provides four different Symmetric.Provider algorithms; I would avoid the ones with shorter keys and known weaknesses:

ProviderLength (bits)Known Vulnerabilities
Symmetric.Provider.Rijndael128, 192, 256no
Symmetric.Provider.TripleDES128, 192no


Asymmetric 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 Encryption.Asymmetric.PublicKey and Encryption.Asymmetric.PrivateKey classes support XML serialization via the .ToXml and .FromXml methods. They also support exporting to config file format via the .ToConfigSection method, which returns a string suitable for cutting and pasting into the <appSettings> section of your *.config file:

  <add key="PublicKey.Modulus" 

    h1Q90Bz+ZxCp/V8Hcf86p+4LPeb1o9EOa01zd0yUwvkE=" />
  <add key="PublicKey.Exponent" 

    value="AQAB" />
  <add key="PrivateKey.P" 

    YHVVkRyVoH16PwLW6Tt2gpdYw==" />
  <add key="PrivateKey.Q" 

    GGMBg8OT5+EVtWAOdto8KTJCw==" />
  <add key="PrivateKey.DP" 

    5umYGwWmRSL20Ufc+gnZQo6Pw==" />
  <add key="PrivateKey.DQ" 

    NVhxVZwsHNM1z2LC5Q+O8BPXQ==" />
  <add key="PrivateKey.InverseQ" 

    7dwgPBIA+uA/LF+C1LXcXe9Aw==" />
  <add key="PrivateKey.D" 

    emNKQDeFW81UOELVszUXNjhVex+k67Ma4omR6iTHSE=" />

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 <appSettings> section of your .config file, it will be used automatically; you no longer have to specify a private key in the .Decrypt method:

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)

Note that we didn't specify any keys here; everything was automatically absorbed from the <appSettings> section of the config file.

There are a few caveats when using Encryption.Asymmetric:

  • Microsoft's implementation of asymmetric encryption offers no choice of providers: you'll get RSA and you'll like it! You do get a choice of key sizes, though-- anywhere from 384 bits to 16,384 bits in steps of 8 bits. If you don't specify a size in the constructor, you'll get 1,024 bits by default. That should be more than enough for most uses.
  • Asymmetric encryption is designed for small inputs. This is partly because asymmetric encryption is brutally slow, but it's also by design: depending on the key size you choose, you'll get an exception if you try to encrypt something too big! There are workarounds, but I don't recommend them. Follow best practices as defined at the top of this article; use asymmetric encryption to protect short stuff, like symmetric passwords or hashes.

The Annoying File Dependency in Encryption.Asymmetric

Unfortunately, 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!


Encryption 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.


  • Tuesday, April 19th, 2005
    • Published.
  • Sunday, May 1st, 2005
    • Minor bugfixes to article code.
    • Corrected issue with byte array nulls and Encoding.GetString in C#.
  • Monday, January 29th, 2007
    • Straight port to .NET 2.0 and Visual Studio 2005.


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


About the Author

Web Developer
United States United States
My name is Jeff Atwood. I live in Berkeley, CA with my wife, two cats, and far more computers than I care to mention. My first computer was the Texas Instruments TI-99/4a. I've been a Microsoft Windows developer since 1992; primarily in VB. I am particularly interested in best practices and human factors in software development, as represented in my recommended developer reading list. I also have a coding and human factors related blog at

You may also be interested in...

Comments and Discussions

GeneralMy vote of 5 Pin
amitmishra70719-Nov-11 17:58
memberamitmishra70719-Nov-11 17:58 
BugThree errors in .NetFx3 fixed Pin
Gabriel X27-Jun-11 5:04
memberGabriel X27-Jun-11 5:04 
AnswerRe: Three errors in .NetFx3 fixed Pin
Dush Abe7-Aug-11 18:01
memberDush Abe7-Aug-11 18:01 
GeneralMy vote of 5 Pin
Senthil Kumar Selvaraj23-May-11 14:32
memberSenthil Kumar Selvaraj23-May-11 14:32 
GeneralMy vote of 5 Pin
parthii3-Mar-11 20:39
memberparthii3-Mar-11 20:39 
GeneralMy vote of 2 Pin
Garry Lowther11-Jun-10 3:50
memberGarry Lowther11-Jun-10 3:50 
GeneralRe: My vote of 2 Pin
dimethol19-Sep-11 0:20
memberdimethol19-Sep-11 0:20 
GeneralEncrypted text same length as plaintext Pin
trhalvorson1-Feb-10 6:37
membertrhalvorson1-Feb-10 6:37 
Beautimous. Lovely article and source code. Thanks a bunch.

In some applications, it is necessary to generate encrypted text that has the same length as the plaintext. Which of the encryption algorithms provided in .NET does that? Is there one you particularly recommend?
GeneralSmooth and Easy to Understand Pin
mjordan16-Dec-09 13:58
membermjordan16-Dec-09 13:58 
GeneralGreat Article [modified] Pin
Richard Osafo15-Nov-09 4:49
memberRichard Osafo15-Nov-09 4:49 
GeneralRe: Great Article Pin
ShaneMcDonald12-Aug-10 7:08
memberShaneMcDonald12-Aug-10 7:08 
QuestionSymmetric encryption - code issue or by design? Pin
GalleySlave30-Jun-09 22:09
memberGalleySlave30-Jun-09 22:09 
GeneralCode translated to C# Pin
felipecsl18-Jun-09 3:54
memberfelipecsl18-Jun-09 3:54 
GeneralRe: Code translated to C# Pin
Prishalan3-Aug-09 22:45
memberPrishalan3-Aug-09 22:45 
GeneralRe: Code translated to C# Pin
may_east19-Aug-09 10:01
membermay_east19-Aug-09 10:01 
GeneralRe: Code translated to C# Pin
bscaer27-Oct-11 8:31
memberbscaer27-Oct-11 8:31 
GeneralRe: Code translated to C# Pin
namit25077-Aug-14 9:33
membernamit25077-Aug-14 9:33 
Question.Clear() method to cleanup Pin
mongoose_za7-May-09 2:03
membermongoose_za7-May-09 2:03 
GeneralDecrypt using Symmetric key Pin
may_east29-Apr-09 6:34
membermay_east29-Apr-09 6:34 
GeneralRe: Decrypt using Symmetric key Pin
Jason Vetter22-Sep-10 6:41
memberJason Vetter22-Sep-10 6:41 
GeneralTroubleshooting Decryption in C# - Please Help Pin
Clayton Q.18-Mar-09 9:57
memberClayton Q.18-Mar-09 9:57 
AnswerRe: Troubleshooting Decryption in C# - Please Help Pin
Patrick Wolf22-Feb-10 0:25
memberPatrick Wolf22-Feb-10 0:25 
QuestionError on RSACryptoServiceProvider.ImportParameters in Utils._ImportKey Pin
mdepaepe25-Feb-09 0:36
membermdepaepe25-Feb-09 0:36 
GeneralProblem with Key Pin
jfsanchez2k28-Nov-08 9:49
memberjfsanchez2k28-Nov-08 9:49 
Generalproblem using the Encryption class Pin
Hasan Jaffal25-Jun-08 0:32
memberHasan Jaffal25-Jun-08 0:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170713.1 | Last Updated 29 Jan 2007
Article Copyright 2005 by wumpus1
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid