那些常用的加密演算法

kiba518發表於2020-11-24

前言

本文主要講解一下C#常用的那些加密演算法。

MD5加密

MD5加密是最常見的加密方式,因為MD5是不可逆的,所以很多系統的密碼都是用MD5加密儲存的。

雖然MD5是不可以解碼的,但因為MD5加密的字串是固定的,所以,理論上只需要建立一個龐大的資料庫,把所有的字串都加密一遍,那就可以解碼所有的MD5密文了。

雖然建立一個可以解碼全部MD5的資料庫不太現實,但一個五六百億資料量的資料庫就可以解碼絕大部分字串了,畢竟大部分情況下,我們的密碼也是有長度限制的。

實際應用中MD5有64位和32位加密之分,程式碼如下:

#region MD5加密 32和64  
public static string GetMd532(string ConvertString)
{
    MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
    string t2 = BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(ConvertString)), 4, 8);
    t2 = t2.Replace("-", "");

    return t2;
} 
public static string Get64Md5(string str)
{
    string cl = str;
    string pwd = "";
    var md5 = MD5.Create();
    byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(cl)); 
    for (int i = 0; i < s.Length; i++)
    {
        pwd = pwd + s[i].ToString("X2");
    } 
    return pwd;
} 
#endregion

我們執行一下,加密函式。

Console.WriteLine($"MD5-64:{ MD5Helper.Get64Md5("Kiba518")}");
Console.WriteLine($"MD5-32:{ MD5Helper.Get32Md5("Kiba518")}");

結果如下圖所示:

SHA1加密

SHA1加密演算法與MD5加密類似,都是不可逆的,只是演算法不同。所以也和MD5一樣,存在容易被大資料解碼的問題。

程式碼如下:

private static readonly Encoding Encoder = Encoding.UTF8;
public static String Sha1(String content )
{
    try
    {
        SHA1 sha1 = new SHA1CryptoServiceProvider();//建立SHA1物件
        byte[] bytes_in = Encoder.GetBytes(content);//將待加密字串轉為byte型別
        byte[] bytes_out = sha1.ComputeHash(bytes_in);//Hash運算
        sha1.Dispose();//釋放當前例項使用的所有資源
        String result = BitConverter.ToString(bytes_out);//將運算結果轉為string型別
        result = result.Replace("-", "").ToUpper();
        return result;
    }
    catch (Exception ex)
    {
        return ex.Message;
    }
}

執行加密函式,結果如下圖所示:

Base64加密

準確的來說,Base64是一種編碼,而不是加密,通常Base64編碼後字串會用於傳輸資料。 不過也因為Base64編碼後字串具有不可讀性,所以,不少人也把他當做加密演算法來使用。

程式碼如下:

private static readonly Encoding Encoder = Encoding.UTF8;
public static string EncodeBase64(string source)
{
    string target = "";
    byte[] bytes = Encoder.GetBytes(source);
    try
    {
        target = Convert.ToBase64String(bytes);
    }
    catch
    {
        target = source;
    }
    return target;
}
public static string DecodeBase64(string result)
{
    string decode = "";
    byte[] bytes = Convert.FromBase64String(result);
    try
    {
        decode = Encoder.GetString(bytes);
    }
    catch
    {
        decode = result;
    }
    return decode;
}

執行Base64編碼函式。

string base64Str = Base64Helper.EncodeBase64("Kiba518");
Console.WriteLine($"SHA1編碼:{ base64Str}");
Console.WriteLine($"SHA1解碼:{ Base64Helper.DecodeBase64(base64Str)}");

結果如下圖所示:

Des加密

DES加密演算法是對金鑰進行保密,而公開演算法,即只有擁有相同金鑰的人才能解密。

DES加密演算法對金鑰有要求,必須是8個字元,如abcdefgh這樣的。

程式碼如下:

public static string Encrypt(string stringToEncrypt, string shortKey)
{
    DESCryptoServiceProvider des = new DESCryptoServiceProvider();
    byte[] inputByteArray = Encoding.GetEncoding("UTF-8").GetBytes(stringToEncrypt);
    des.Key = ASCIIEncoding.UTF8.GetBytes(shortKey);
    des.IV = ASCIIEncoding.UTF8.GetBytes(shortKey);
    MemoryStream ms = new MemoryStream();
    CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
    cs.Write(inputByteArray, 0, inputByteArray.Length);
    cs.FlushFinalBlock();
    StringBuilder ret = new StringBuilder();
    foreach (byte b in ms.ToArray())
    {
        ret.AppendFormat("{0:X2}", b);
    }
    ret.ToString();
    return ret.ToString();
}
public static string Decrypt(string stringToDecrypt, string sKey)
{
    DESCryptoServiceProvider des = new DESCryptoServiceProvider();
​
    byte[] inputByteArray = new byte[stringToDecrypt.Length / 2];
    for (int x = 0; x < stringToDecrypt.Length / 2; x++)
    {
        int i = (Convert.ToInt32(stringToDecrypt.Substring(x * 2, 2), 16));
        inputByteArray[x] = (byte)i;
    }
    des.Key = ASCIIEncoding.UTF8.GetBytes(sKey);
    des.IV = ASCIIEncoding.UTF8.GetBytes(sKey);
    MemoryStream ms = new MemoryStream();
    CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);
    cs.Write(inputByteArray, 0, inputByteArray.Length);
    cs.FlushFinalBlock();
    StringBuilder ret = new StringBuilder();
    return System.Text.Encoding.Default.GetString(ms.ToArray());
}

如程式碼所示,我們使用DESCryptoServiceProvider類來進行DES加密。

DESCryptoServiceProvider類的Mode屬性可以指定加密運算模式。

加密運算模式如下:

CBC:密碼塊鏈模式。

ECB:電子密碼本模式。

OFB:輸出反饋模式。

CFB:密碼反饋模式。

CTS: 密碼文字竊取模式。

在C#中預設的加密運算模式是CBC—密碼塊鏈模式。

在Java中預設的加密運算模式是ECB—電子密碼本模式。

即,如果密文是在C#專案和Java專案之間傳遞,那麼必須配置相同的加密運算模式。

執行DES加密函式程式碼如下:

string key_8 = "abcdefgh";
string desShortKeyStr = DESHelper.Encrypt("Kiba518", key_8);
Console.WriteLine($"DES加密:{ desShortKeyStr}");
Console.WriteLine($"DES解密:{ DESHelper.Decrypt(desShortKeyStr, key_8)}");

結果如下圖所示:

有時候,我們的金鑰不是正好8個字元,那我們就擷取前8為作為金鑰就可以了。

RSA加密

RSA加密採用公鑰加密,私鑰解密的模式。Https的數字證照也是使用這種模式加密的。

程式碼如下:

public static string RSADecrypt(string xmlPrivateKey, string enptStr)
 {
     RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
     provider.FromXmlString(xmlPrivateKey);
     byte[] rgb = Convert.FromBase64String(enptStr);
     byte[] bytes = provider.Decrypt(rgb, RSAEncryptionPadding.OaepSHA1);
     return new UnicodeEncoding().GetString(bytes);
 }
 public static string RSAEncrypt(string xmlPublicKey, string enptStr)
 {
     RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
     provider.FromXmlString(xmlPublicKey);
     byte[] bytes = new UnicodeEncoding().GetBytes(enptStr);
     return Convert.ToBase64String(provider.Encrypt(bytes, RSAEncryptionPadding.OaepSHA1));
 }

執行DES加密函式程式碼如下:

//加密公鑰  
string publicKey = "<RSAKeyValue><Modulus>18+I2j3HU/fXQasRXOWGegP3dG75I/It2n42rgeIATeftBkoQNH73Rz0IYW++arqd0Yy5hFpNkqzY/dOmD+bDXWUheWA0P/dVZf+qeWwVV+iW3lRAU8SmnPcaD35Ic1jMEPFQVeX1zGI2ofD8aGodeSRA4+JKo+KLgyGVGDI+d0=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
//解密私鑰 
string privateKey = "<RSAKeyValue>      <Modulus>18+I2j3HU/fXQasRXOWGegP3dG75I/It2n42rgeIATeftBkoQNH73Rz0IYW++arqd0Yy5hFpNkqzY/dOmD+bDXWUheWA0P/dVZf+qeWwVV+iW3lRAU8SmnPcaD35Ic1jMEPFQVeX1zGI2ofD8aGodeSRA4+JKo+KLgyGVGDI+d0=</Modulus><Exponent>AQAB</Exponent><P>2EEAI+cO1fyvmGpg3ywMLHHZ1/X3ZrF6xZBNM2AL7bJFVfL8RS8UznUCdsL/R/o1b+lGo1CetlI++n6IvYYwyw==</P><Q>/3muAXWOU3SMKFWSDpHUgeM9kZev0ekQDefRSayXM8q9ItkaWTOJcIN614A0UGdYE6VX1ztPgveQFzm0qJDy9w==</Q><DP>NM/i/eGewOmd5IYONFJogq4nOlOKYNz1E6yC/gn1v83qmuvlaevuk+EFggVrHKPhSvxYUOgOao45bSlbsZVE8w==</DP><DQ>MKU7w91dh3iWw4tfr1SHUWAytglbGi41t2Af0taBSARftUX/pWKR1hHDD0vDKlgzRjJiooIRps966WE8jChliw==</DQ><InverseQ>YEIfQArVNP27AJn3WOBswHP/+gJ6Bk434MZ80CJONp4b6e+Ilxd2dwloxGKNbGgCyaNJEFI5J8qYSNNe0KqPkw==</InverseQ><D>ZAscSPesqLtS+WlBMkxgy719AGfVbRl+sjQiSwjIvq+3hDjJVUtCs90RO10SDBF0gfhz7f2SRY3ZnXTu5VtPF9KEQyUaY0F6eXwz4YQNzJTI2c1o5SFXZP8Ynqwltg8gNIhMe8bB6nVgASeADBim22DlSFCzmD3vt1gTI8nxmO0=</D></RSAKeyValue>";
string myname = "my name is Kiba518!my name is Kiba518!!!!43"; //最大長度43
string rsaStr = RSAHelper.RSAEncrypt(publicKey, myname);
Console.WriteLine($"RSA加密:{ rsaStr}");
string dersaStr = RSAHelper.RSADecrypt(privateKey, rsaStr);
Console.WriteLine($"RSA解密:{ dersaStr}");

結果如下圖所示:

RSA加密有個特點,就是他對被加密的字串有長度限制。

長度限制規則:待加密的位元組數不能超過金鑰的長度值除以 8 再減去 11(即:RSACryptoServiceProvider.KeySize / 8 - 11),而加密後得到密文的位元組數,正好是金鑰的長度值除以 8(即:RSACryptoServiceProvider.KeySize / 8)。注:該長度指的是byte[]陣列的長度,而不是字串的長度。

簡單來說,就是被加密字串不能太長。

但是,在真實的業務中,我們需要加密的字串往往會很長,那麼,RSA又對被加密字串有長度限制,我們該怎麼辦呢? 很簡單,把待加密的字串拆開,每段長度都小於等於限制長度,然後分段加密,這樣,問題就解決了。

分段加密程式碼如下:

public static String SubRSAEncrypt(string xmlPublicKey, string enptStr)
{
    RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
    provider.FromXmlString(xmlPublicKey);
    Byte[] bytes = Encoder.GetBytes(enptStr);
    int MaxBlockSize = provider.KeySize / 8 - 11;    //加密塊最大長度限制
​
    if (bytes.Length <= MaxBlockSize)
        return Convert.ToBase64String(provider.Encrypt(bytes, false));
​
    using (MemoryStream PlaiStream = new MemoryStream(bytes))
    using (MemoryStream CrypStream = new MemoryStream())
    {
        Byte[] Buffer = new Byte[MaxBlockSize];
        int BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);
​
        while (BlockSize > 0)
        {
            Byte[] ToEncrypt = new Byte[BlockSize];
            Array.Copy(Buffer, 0, ToEncrypt, 0, BlockSize);
​
            Byte[] Cryptograph = provider.Encrypt(ToEncrypt, false);
            CrypStream.Write(Cryptograph, 0, Cryptograph.Length);
​
            BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);
        }
​
        return Convert.ToBase64String(CrypStream.ToArray(), Base64FormattingOptions.None);
    }
​
}
/// <summary>
/// 分段解密,應對長字串
/// </summary>
/// <param name="xmlPublicKey"></param>
/// <param name="enptStr"></param>
/// <returns></returns>
public static String SubRSADecrypt(string xmlPublicKey, string enptStr)
{
    RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
    provider.FromXmlString(xmlPublicKey);
    Byte[] bytes = Convert.FromBase64String(enptStr);
    int MaxBlockSize = provider.KeySize / 8;    //解密塊最大長度限制
​
    if (bytes.Length <= MaxBlockSize)
        return Encoder.GetString(provider.Decrypt(bytes, false));
​
    using (MemoryStream CrypStream = new MemoryStream(bytes))
    using (MemoryStream PlaiStream = new MemoryStream())
    {
        Byte[] Buffer = new Byte[MaxBlockSize];
        int BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);
​
        while (BlockSize > 0)
        {
            Byte[] ToDecrypt = new Byte[BlockSize];
            Array.Copy(Buffer, 0, ToDecrypt, 0, BlockSize);
​
            Byte[] Plaintext = provider.Decrypt(ToDecrypt, false);
            PlaiStream.Write(Plaintext, 0, Plaintext.Length);
​
            BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);
        }
​
        return Encoder.GetString(PlaiStream.ToArray());
    }
}

結果如下圖所示:

國密加密

國密加密有以下幾種模式。

如圖所示,SM3是一種資料摘要計算,與MD5和SHA1類似,都是不可逆的。而SM2演算法中還需要使用SM3對資料加密。

C#使用國密加密,首先引用BouncyCastle.Crypto.dll。

具體使用方式參考網址:https://www.cnblogs.com/valu/p/12842778.html

Demo中有程式碼具體封裝的程式碼。

呼叫程式碼如下:

#region SM3 不可逆
 byte[] md = new byte[32];
 byte[] msg1 = Encoding.Default.GetBytes("KIBA518");
 SM3Digest sm3 = new SM3Digest();
 sm3.BlockUpdate(msg1, 0, msg1.Length);
 sm3.DoFinal(md, 0);
 System.String s = new UTF8Encoding().GetString(Hex.Encode(md));
 System.Console.Out.WriteLine(s.ToUpper());
​
 #endregion
​
 #region SM2
 SM2Utils.GenerateKeyPair();
​
 String plainText = "KIBA518";
 byte[] sourceData = Encoding.Default.GetBytes(plainText);
​
 //下面的祕鑰可以使用generateKeyPair()生成的祕鑰內容  
 // 國密規範正式私鑰  
 String prik = "3690655E33D5EA3D9A4AE1A1ADD766FDEA045CDEAA43A9206FB8C430CEFE0D94";
 // 國密規範正式公鑰  
 String pubk = "04F6E0C3345AE42B51E06BF50B98834988D54EBC7460FE135A48171BC0629EAE205EEDE253A530608178A98F1E19BB737302813BA39ED3FA3C51639D7A20C7391A";
​
 System.Console.Out.WriteLine("加密: ");
 String cipherText = SM2Utils.Encrypt(Hex.Decode(pubk), sourceData);
 System.Console.Out.WriteLine(cipherText);
 System.Console.Out.WriteLine("解密: ");
 plainText = Encoding.Default.GetString(SM2Utils.Decrypt(Hex.Decode(prik), Hex.Decode(cipherText)));
 System.Console.Out.WriteLine(plainText);
​
 Console.ReadLine();
 #endregion

結果如下圖所示

結語

到此C#常用的那些加密演算法就介紹完了,下面我們一起看一下,同一字串,加密後情況。

可以看到,不同加密方式得到的密文長度都不一樣,其中DES加密後在Base64編碼的模式的密文長度最短。RSA加密的密文最長。

----------------------------------------------------------------------------------------------------

到此那些常用的加密演算法基本使用已經介紹完了,程式碼已經傳到Github上了,歡迎大家下載。

程式碼已經傳到Github上了,歡迎大家下載。

Github地址: https://github.com/kiba518/EncryptionDemo

----------------------------------------------------------------------------------------------------

注:此文章為原創,任何形式的轉載都請聯絡作者獲得授權並註明出處!
若您覺得這篇文章還不錯,請點選下方的推薦】,非常感謝!

https://www.cnblogs.com/kiba/p/14028476.html

 

 

相關文章