Summary of encryption algorithms in. NET (custom encryption Helper class continued),. nethelper

Source: Internet
Author: User
Tags sha1 hash sha1 hash algorithm

Summary of encryption algorithms in. NET (custom encryption Helper class continued),. nethelper

1.1.1 Summary

I believe many people have used encryption algorithms provided by. NET, and we must understand the characteristics of each encryption algorithm (symmetric or asymmetric, key length, initialization vector, and so on ). I have also seen many people write. summary of encryption algorithms in. NET, but I have found some problems. Many people like to list the specific implementations of each encryption algorithm. Suppose we want to implement the AES and Triple DES encryption algorithms, it is indeed possible to give their specific implementations in many ways.

So is it really necessary for us to implement each encryption algorithm? This design does not conform to the OOP design philosophy. The most important thing is that we need to maintain multiple encryption algorithms! OK. Let's implement a scalable and well-maintained encryption algorithm Helper.

1.1.2 text

 

Figure 1 Hierarchy of Hash encryption algorithms

From the above inheritance level, we can know that. NET provides seven Hash encryption algorithms, all of which inherit from the abstract class HashAlgorithm, and we often use MD5, SHA1, SHA256 and other encryption algorithms. The following describes how to implement MD5 and SHA1.

Figure 2 Symmetric encryption algorithm hierarchy

From the above inheritance layer, we can know that. NET provides five symmetric encryption algorithms, all of which inherit from the abstract class SymmetricAlgorithm. Below we will give them a general implementation.

Figure 3 asymmetric encryption algorithm hierarchy

From the above inheritance level, we can know that. NET provides four asymmetric encryption algorithms, all of which inherit from the abstract class asyuncricalgorithm. The RSA implementation will be given below.

In addition to the above encryption algorithms,. NET also provides many other types of encryption. Here we mainly introduce some common encryption algorithms. If you need to know about them, refer to MSDN. OK. Let's implement the Hash encryption algorithm.

Hash Encryption Algorithm

Before providing specific algorithm implementations, let's first recall what is a Hash encryption algorithm?

Hash encryption uses the hash function to encrypt the information to be encrypted and generate the corresponding hash value. Then, we can define a Hash () function, the information m to be encrypted and the hash value h after encryption.

Hash encryption is performed on Information m1 and m2 to obtain the corresponding hash values hash (m1) and hash (m2 ).

If the information m1 is m2, the same hash address will be obtained, but the information m1! = M2 may also get the same hash address, so there is a hash conflict (collision). In general, hash conflicts can only be minimized, not completely avoided. When a hash conflict occurs, we need to use the conflict resolution method. The main methods are the open address method, the rehash method, the link address method, and the establishment of a Public overflow zone.

Figure 4 Hash encryption process (image source wiki)

Now let's implement a general hash encryption method.

 /// <summary>/// Encrypts the specified hash algorithm./// 1. Generates a cryptographic Hash Key for the provided text data./// </summary>/// <param name="hashAlgorithm">The hash algorithm.</param>/// <param name="dataToHash">The data to hash.</param>/// <returns></returns>public static string Encrypt(HashAlgorithm hashAlgorithm, string dataToHash){  var tabStringHex = new string[16];  var UTF8 = new System.Text.UTF8Encoding();  byte[] data = UTF8.GetBytes(dataToHash);  byte[] result = hashAlgorithm.ComputeHash(data);  var hexResult = new StringBuilder(result.Length);  for (int i = 0; i < result.Length; i++)  {    //// Convert to hexadecimal    hexResult.Append(result[i].ToString("X2"));  }  return hexResult.ToString();}

The preceding encryption method contains a HashAlgorithm parameter. We can pass the specific hash algorithm (MD5, SHA1, and SHA256) inherited from the abstract class HashAlgorithm ), through inheritance polymorphism, we make the encryption method more flexible and simple. The most important thing is that now we only need to maintain a general encryption method.

Next, we need to add a method to determine whether the hash value after encryption is equal and whether the hash value is equal to the IsHashMatch () method.

 /// <summary>/// Determines whether [is hash match] [the specified hash algorithm]./// </summary>/// <param name="hashAlgorithm">The hash algorithm.</param>/// <param name="hashedText">The hashed text.</param>/// <param name="unhashedText">The unhashed text.</param>/// <returns>///  <c>true</c> if [is hash match] [the specified hash algorithm]; /// otherwise, <c>false</c>./// </returns>public static bool IsHashMatch(HashAlgorithm hashAlgorithm,  string hashedText, string unhashedText){  string hashedTextToCompare = Encrypt(    hashAlgorithm, unhashedText);  return (String.Compare(hashedText,    hashedTextToCompare, false) == 0);}

Symmetric encryption algorithm

Now we have completed the general Hash encryption method. Next we will continue to introduce symmetric and asymmetric algorithms.

Before implementing the symmetric encryption algorithm, let's take a look at the symmetric encryption process. If we want to encrypt a group of data, we can use one or more keys to encrypt and decrypt the data, however, there is a problem that the key length of the symmetric encryption algorithm is different. For example, the key length of DES is 64 bits, and the length of AES can be 256 bits, bits, or bits, do we need the key length of each algorithm in hard code? Can we dynamically generate keys for corresponding algorithms?

In fact,. NET provides methods for generating corresponding keys based on different symmetric algorithms, and encapsulates these methods in the PasswordDeriveBytes and Rfc2898DeriveBytes classes.

First, let's take a look at the PasswordDeriveBytes class which contains two methods: CryptDeriveKey and GetBytes used to generate the key of the corresponding algorithm. Now let's take a look at how they generate the key.

CryptDeriveKey:

 // The sample function.public void Encrypt(){  // The size of the IV property must be the same as the BlockSize property.  // Due to the RC2 block size is 64 bytes, so iv size also is 64 bytes.   var iv = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };  var pdb = new PasswordDeriveBytes("pwd", null);  // Set the encrypted algorithm and export key algorithm.  // Then get the key base on encrypt algorithm.  byte[] key = pdb.CryptDeriveKey("RC2", "SHA1", 128, iv);  Console.WriteLine(key.Length * 8);  Console.WriteLine(new RC2CryptoServiceProvider().BlockSize);  // Creates an RC2 object to encrypt with the derived key  var rc2 = new RC2CryptoServiceProvider         {           Key = key,           IV = new byte[] { 21, 22, 23, 24, 25, 26, 27, 28 }         };  // now encrypt with it  byte[] plaintext = Encoding.UTF8.GetBytes("NeedToEncryptData");  using (var ms = new MemoryStream())  {    var cs = new CryptoStream(      ms, rc2.CreateEncryptor(), CryptoStreamMode.Write);    cs.Write(plaintext, 0, plaintext.Length);    cs.Close();    byte[] encrypted = ms.ToArray();  }}

Example 1: We use the SHA1 hash algorithm to generate a-bit key for the RC2 encryption algorithm, so that we can obtain the key of the corresponding Length Based on Different symmetric encryption algorithms, note that we do not dynamically generate the initialization vector iv. This is for the sake of simplicity. In practice, we should not obtain the initialization vector like this.

Next, let's take a look at the implementation of key generation through PBKDF1 and PBKDF2s algorithms.

PBKDF1

GetBytes: The GetBytes () method of PasswordDeriveBytes implements PBKDF1 (Password Based Key Derivation Function ).

PBKDF1 algorithm process:

1. Splicing key and Salt: R0 = Pwd + Salt

2. Hash encryption process: R1 = Hash (R2-1)

........

3. Hash encryption process: Rn = Hash (Rn-1)

4. n indicates the number of iterations. For details, click here)

Now we have a preliminary understanding of the principles of the PBKDF1 algorithm. Next we will call this algorithm through GetBytes () to generate a key.

/// <summary>/// Uses the PBKDF1 to genernate key, /// then use it to encrypt plain text./// </summary>public void PBKDF1(){  byte[] salt = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };  // Creates an RC2 object to encrypt with the derived key  var pdb = new PasswordDeriveBytes("pwd", salt)   {IterationCount = 23, HashName = "SHA1"};  // Gets the key and iv.  byte[] key = pdb.GetBytes(16);  byte[] iv = pdb.GetBytes(8);  var rc2 = new RC2CryptoServiceProvider { Key = key, IV = iv };  byte[] plaintext = Encoding.UTF8.GetBytes("NeedToEncryptData");  using (var ms = new MemoryStream())  {    // Encrypts data.    var cs = new CryptoStream(      ms, rc2.CreateEncryptor(), CryptoStreamMode.Write);    cs.Write(plaintext, 0, plaintext.Length);    cs.Close();    byte[] encrypted = ms.ToArray();  }}

Example 2: we use the PBKDF1 algorithm to generate a 128-bit key and a 64-bit initialization vector for the RC2 encryption algorithm. Note that the GetBytes () method of PasswordDeriveBytes is outdated, the alternative is the GetBytes () method of Rfc2898DeriveBytes.

PBKDF2

GetBytes: Because the Rfc2898DeriveBytes GetBytes () method implements the PBKDF2 algorithm, and it also replaces the GetBytes () method of PBKDF1, we recommend that you use the Rfc2898DeriveBytes GetBytes () method.

 /// <summary>/// Uses the PBKDF2 to genernate key, /// then use it to encrypt plain text./// </summary>public void PBKDF2(){  byte[] salt = new byte[] { 23, 21, 32, 33, 46, 59, 60, 74 };  var rfc = new Rfc2898DeriveBytes("pwd", salt, 23);  // generate key and iv.  byte[] key = rfc.GetBytes(16);  byte[] iv = rfc.GetBytes(8);  // Creates an RC2 object to encrypt with the derived key  var rc2 = new RC2CryptoServiceProvider { Key = key, IV = iv };  // Encrypts the data.  byte[] plaintext = Encoding.UTF8.GetBytes("NeedToEncryptData");  using (var ms = new MemoryStream())  {    var cs = new CryptoStream(      ms, rc2.CreateEncryptor(), CryptoStreamMode.Write);    cs.Write(plaintext, 0, plaintext.Length);    cs.Close();    byte[] encrypted = ms.ToArray();  }}

Example 3: We found that the PBKDF2 () method is no different from the previous PBKDF1 () method, that is, the hash algorithm of the encryption key is not required (refer to PBKDF2 specifications here ).

The preceding three methods are used to dynamically generate an encryption key. We will use the Rfc2898DeriveBytes GetBytes () method to obtain the key. Next, let's use this method to implement a general symmetric encryption algorithm!

Figure 5 symmetric algorithm encryption process

First, we encode the encrypted plain text. UTF8 is used by default to encode the plain text. You can also use other encoding methods, then, encrypt the encoded plain text using the corresponding encryption algorithm, and finally convert the encrypted Byte array to a Base64 string to return.

/// <summary>/// Encrypts with specified symmetric algorithm./// Can be Aes, DES, RC2, Rijndael and TripleDES./// </summary>/// <param name="algorithm">The symmertric algorithm (Aes, DES, RC2, Rijndael and TripleDES).</param>/// <param name="plainText">The plain text need to be encrypted.</param>/// <param name="key">The secret key to encrypt plain text.</param>/// <param name="iv">The iv should be 16 bytes.</param>/// <param name="salt">Salt to encrypt with.</param>/// <param name="pwdIterations">The number of iterations for plain text.</param>/// <param name="keySize">Size of the key.</param>/// <param name="cipherMode">The cipher mode.</param>/// <param name="paddingMode">The padding mode.</param>/// <returns></returns>public static byte[] Encrypt(SymmetricAlgorithm algorithm, byte[] plainText, string key, string iv,  string salt, int pwdIterations, int keySize, CipherMode cipherMode, PaddingMode paddingMode){  if (null == plainText)    throw new ArgumentNullException("plainText");  if (null == algorithm)    throw new ArgumentNullException("algorithm");  if (String.IsNullOrEmpty(key))    throw new ArgumentNullException("key");  if (String.IsNullOrEmpty(iv))    throw new ArgumentNullException("iv");  if (String.IsNullOrEmpty(salt))    throw new ArgumentNullException("salt");  // Note the salt should be equal or greater that 64bit (8 byte).  var rfc = new Rfc2898DeriveBytes(key, salt.ToByteArray(), pwdIterations);  using (SymmetricAlgorithm symmAlgo = algorithm)  {    symmAlgo.Mode = cipherMode;    //symmAlgo.Padding = paddingMode;    byte[] cipherTextBytes = null;    using (var encryptor = symmAlgo.CreateEncryptor(      rfc.GetBytes(keySize / 8), iv.ToByteArray()))    {      using (var ms = new MemoryStream())      {        using (var cs = new CryptoStream(          ms, encryptor, CryptoStreamMode.Write))        {          cs.Write(plainText, 0, plainText.Length);          cs.FlushFinalBlock();          cipherTextBytes = ms.ToArray();          ms.Close();          cs.Close();        }      }      symmAlgo.Clear();      return cipherTextBytes;    }  }}

Figure 5 symmetric algorithm decryption process

Through the decryption process, we found that the decryption process is exactly the opposite of encryption. First, convert the ciphertext in Base64 format to a Byte array, and then use the corresponding decryption algorithm to decrypt the ciphertext, finally, encode the decrypted data and return the plain text (UTF8 is used by default ).

/// <summary>/// Decrypts the specified algorithm./// Can be Aes, DES, RC2, Rijndael and TripleDES./// </summary>/// <param name="algorithm">The symmertric algorithm (Aes, DES, RC2, Rijndael and TripleDES).</param>/// <param name="cipherText">The cipher text.</param>/// <param name="key">The secret key to decrypt plain text.</param>/// <param name="iv">The iv should be 16 bytes.</param>/// <param name="salt">Salt to decrypt with.</param>/// <param name="pwdIterations">The number of iterations for plain text.</param>/// <param name="keySize">Size of the key.</param>/// <param name="cipherMode">The cipher mode.</param>/// <param name="paddingMode">The padding mode.</param>/// <returns></returns>public static byte[] Decrypt(SymmetricAlgorithm algorithm, byte[] cipherText,  string key, string iv, string salt, int pwdIterations, int keySize,  CipherMode cipherMode, PaddingMode paddingMode){  if (null == cipherText)    throw new ArgumentNullException("cipherText");  if (null == algorithm)    throw new ArgumentNullException("algorithm");  if (String.IsNullOrEmpty(key))    throw new ArgumentNullException("key");  if (String.IsNullOrEmpty(iv))    throw new ArgumentNullException("iv");  if (String.IsNullOrEmpty(salt))    throw new ArgumentNullException("salt");  // Note the salt should be equal or greater that 64bit (8 byte).  var rfc = new Rfc2898DeriveBytes(key, salt.ToByteArray(), pwdIterations);  using (SymmetricAlgorithm symmAlgo = algorithm)  {    symmAlgo.Mode = cipherMode;    //symmAlgo.Padding = paddingMode;    byte[] plainTextBytes = new byte[cipherText.Length];    int cnt = -1;    using (var encryptor = symmAlgo.CreateDecryptor(      rfc.GetBytes(keySize / 8), iv.ToByteArray()))    {      using (var ms = new MemoryStream(cipherText))      {        using (var cs = new CryptoStream(          ms, encryptor, CryptoStreamMode.Read))        {          cnt = cs.Read(plainTextBytes, 0, plainTextBytes.Length);          ms.Close();          cs.Close();        }      }    }    symmAlgo.Clear();    Array.Resize(ref plainTextBytes, cnt);    return plainTextBytes;  }}

In the preceding encryption and decryption methods, we use Rfc2898DeriveBytes to obtain the password, salt value, and number of iterations, and then call the GetBytes method to generate the key.

Now we have completed a general symmetric encryption algorithm. We only need a set of encryption and decryption methods to use any symmetric encryption algorithm at will, instead of writing the corresponding encryption and decryption methods for each encryption and decryption algorithm.

Asymmetric encryption algorithm

The. NET Framework provides four asymmetric encryption algorithms (DSA, ECDiffieHellman, ECDsa, and RSA). They all inherit from the abstract class asypolicricalgorithm. Next we will provide the Implementation of the RSA algorithm.

RSA is an asymmetric and dual-key encryption algorithm, which is widely used in public key encryption standards and electronic commerce.

When two keys are encrypted, one is the public key and the other is the private key that is not disclosed.

The principle of two-key encryption is as follows:

A) the relationship between the public key and the private key is one-to-one. If there is a public key, there must be a unique private key corresponding to it, and vice versa.

B) All (Public Key and private key) pairs are different.

C) The public key can be used to unbind the Information encrypted by the private key, and vice versa.

D) It is relatively easy to generate a public key and a private key at the same time, but it is difficult or impossible to calculate a private key from the public key.

The current digital signature encryption mainly uses the RSA algorithm. For more information about digital signatures, click here (Chinese) And here (English ).

Now we know that the RSA algorithm uses public keys and keys for encryption and decryption, so we first define a method to generate public keys and keys.

 /// <summary>/// Generates the RSA public and private key./// </summary>/// <param name="algorithm">The algorithm to creates key.</param>/// <returns></returns>public static void GenerateRSAKey(RSACryptoServiceProvider algorithm){  // Contains public and private key.  RSAPrivateKey = algorithm.ToXmlString(true);  using (var streamWriter = new StreamWriter("PublicPrivateKey.xml"))  {    streamWriter.Write(RSAPrivateKey);  }  // Only contains public key.  RSAPubicKey = algorithm.ToXmlString(false);  using (var streamWriter = new StreamWriter("PublicOnlyKey.xml"))  {    streamWriter.Write(RSAPubicKey);  }}

Using the ToXmlString () method of RSACryptoServiceProvider, we generate a pair of public keys and keys. If the parameter is true, both the RSA public key and the private key are included. If the parameter is true, only the public key is included.

/// <summary>/// Encrypts with the specified RSA algorithm./// </summary>/// <param name="rsa">A RSA object.</param>/// <param name="plainText">The plain text to decrypt.</param>/// <param name="key">The key.</param>/// <param name="encoding">The encoding.</param>/// <returns></returns>public static string Encrypt(RSACryptoServiceProvider rsa,  string plainText, string key, Encoding encoding){  if (null == rsa)    throw new ArgumentNullException("rsa");  if (String.IsNullOrEmpty(plainText))    throw new ArgumentNullException("plainText");  if (String.IsNullOrEmpty(key))    throw new ArgumentNullException("key");  if (null == encoding)    throw new ArgumentNullException("encoding");  string publicKey;  // Reads public key.  using (var streamReader = new StreamReader("PublicOnlyKey.xml"))  {    publicKey = streamReader.ReadToEnd();  }  rsa.FromXmlString(publicKey);  byte[] cipherBytes = rsa.Encrypt(plainText.ToBytesEncoding(encoding), true);  rsa.Clear();  return cipherBytes.ToBase64String();}

Next, we define the RSA encryption method. First, we read the key and public key from the stream, pass it to the FromXmlString () method, and finally encrypt the plain.

/// <summary>/// Decrypts with the specified RSA algorithm./// </summary>/// <param name="rsa">a RSA object.</param>/// <param name="cipherText">The cipher text to encrypt.</param>/// <param name="key">The key.</param>/// <param name="encoding">The encoding.</param>/// <returns></returns>public static string Decrypt(RSACryptoServiceProvider rsa,  string cipherText, string key, Encoding encoding){  string privateKey;  // Reads the private key.  using (var streamReader = new StreamReader("PublicPrivateKey.xml"))  {    privateKey = streamReader.ReadToEnd();  }  rsa.FromXmlString(privateKey);  byte[] plainBytes = rsa.Decrypt(cipherText.FromBase64String(), true);  rsa.Clear();  return plainBytes.FromByteToString(encoding);}

We quickly implemented the RSA decryption method by referring to the encryption method. We also read the key from the stream, passed it to the FromXmlString () method, and finally decrypted the ciphertext, note that the Decrypt () method of the RSA algorithm is called. Do not forget to modify it when using the glue code.

Now we have finally completed the general encryption and decryption methods. Next we will definitely test the effects of these methods. This time I will use the unit test.

[TestMethod] public void TestStart () {try {string cipherText; string plainText; # region Hash Algo cipherText = CryptographyUtils. encrypt (CryptographyUtils. createHashAlgoMd5 (), @ "Hello everyone ). "); Assert. isTrue (CryptographyUtils. isHashMatch (CryptographyUtils. createHashAlgoMd5 (), cipherText, @ "Hello everyone ). "); cipherText = CryptographyUtils. encrypt (CryptographyUtils. createHashAlgoSHA 1 (), @ "Hello everyone ). "); Assert. isTrue (CryptographyUtils. isHashMatch (CryptographyUtils. createHashAlgoSHA1 (), cipherText, @ "Hello everyone ). "); # endregion # region Asymm Algo CryptographyUtils. generateRSAKey (CryptographyUtils. createAsymmAlgoRSA (); cipherText = CryptographyUtils. encrypt (CryptographyUtils. createAsymmAlgoRSA (), @ "% dk> JK. rusH ", @" c579D-E>? $) _ "); PlainText = CryptographyUtils. Decrypt (CryptographyUtils. CreateAsymmAlgoRSA (), cipherText, @" c579D-E>? $) _ "); Assert. areEqual <string> (@ "% dk> JK. rusH ", plainText); # endregion # region Symm Algo cipherText = CryptographyUtils. encrypt (CryptographyUtils. createSymmAlgoAes (), "JK_huangJK_huangJK_huang Huang hang", "JK_huangJK_huang", 256); plainText = CryptographyUtils. decrypt (CryptographyUtils. createSymmAlgoAes (), cipherText, "JK_huangJK_huang", 256); Assert. areEqual <string> ("JK_huangJK_huangJK_huang ", plainText); cipherText = CryptographyUtils. encrypt (CryptographyUtils. createSymmAlgoDES (), "JK_huangJK_huangJK_huang Huang hang", "JK_huangJK_huang", 64); plainText = CryptographyUtils. decrypt (CryptographyUtils. createSymmAlgoDES (), cipherText, "JK_huangJK_huang", 64); Assert. areEqual <string> ("JK_huangJK_huangJK_huang ", plainText); cipherText = CryptographyUtils. encrypt (CryptographyUtils. createSymmAlgoRC2 (), "JK_huangJK_huangJK_huang Huang hang", "JK_huangJK_huang", 128); plainText = CryptographyUtils. decrypt (CryptographyUtils. createSymmAlgoRC2 (), cipherText, "JK_huangJK_huang", 128); Assert. areEqual <string> ("JK_huangJK_huangJK_huang ", plainText); cipherText = CryptographyUtils. encrypt (CryptographyUtils. createSymmAlgoRijndael (), "JK_huangJK_huangJK_huang Huang hang", "JK_huangJK_huang", 256); plainText = CryptographyUtils. decrypt (CryptographyUtils. createSymmAlgoRijndael (), cipherText, "JK_huangJK_huang", 256); Assert. areEqual <string> ("JK_huangJK_huangJK_huang ", plainText); cipherText = CryptographyUtils. encrypt (CryptographyUtils. createSymmAlgoTripleDes (), "JK_huangJK_huangJK_huang Huang hang", "JK_huangJK_huang", 192); plainText = CryptographyUtils. decrypt (CryptographyUtils. createSymmAlgoTripleDes (), cipherText, "JK_huangJK_huang", 192); Assert. areEqual <string> ("JK_huangJK_huangJK_huang ", plainText); # endregion} catch (Exception ex) {Debug. assert (false, ex. message );}}

Figure 6 unit test results

1.1.3 Summary

This article provides. NET, because the encryption and decryption methods are relatively fixed, and each algorithm has its own characteristics, so here we provide their implementation, and we can encapsulate the method in an encrypted Helper class, which can greatly improve our development efficiency, in addition, it makes full use of polymorphism to greatly improve the flexibility and maintainability of encryption algorithms.

The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.