加密
常用的加密算法
前言
本文主要讲解一下C#常用的那些加密算法。
MD5加密
MD5加密是最常见的加密方式,因为MD5是不可逆的,所以很多系统的密码都是用MD5加密保存的。
虽然MD5是不可以解码的,但因为MD5加密的字符串是固定的,所以,理论上只需要建立一个庞大的数据库,把所有的字符串都加密一遍,那就可以解码所有的MD5密文了。
虽然建立一个可以解码全部MD5的数据库不太现实,但一个五六百亿数据量的数据库就可以解码绝大部分字符串了,毕竟大部分情况下,我们的密码也是有长度限制的。
实际应用中MD5有64位和32位加密之分,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#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 |
我们运行一下,加密函数。
1
2
|
Console.WriteLine($ "MD5-64:{ MD5Helper.Get64Md5(" Kiba518 ")}" ); Console.WriteLine($ "MD5-32:{ MD5Helper.Get32Md5(" Kiba518 ")}" ); |
结果如下图所示:
SHA1加密
SHA1加密算法与MD5加密类似,都是不可逆的,只是算法不同。所以也和MD5一样,存在容易被大数据解码的问题。
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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编码后字符串具有不可读性,所以,不少人也把他当做加密算法来使用。
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
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编码函数。
1
2
3
|
string base64Str = Base64Helper.EncodeBase64( "Kiba518" ); Console.WriteLine($ "SHA1编码:{ base64Str}" ); Console.WriteLine($ "SHA1解码:{ Base64Helper.DecodeBase64(base64Str)}" ); |
结果如下图所示:
Des加密
DES加密算法是对密钥进行保密,而公开算法,即只有拥有相同密钥的人才能解密。
DES加密算法对密钥有要求,必须是8个字符,如abcdefgh这样的。
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
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加密函数代码如下:
1
2
3
4
|
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的数字证书也是使用这种模式加密的。
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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加密函数代码如下:
1
2
3
4
5
6
7
8
9
|
//加密公钥 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又对被加密字符串有长度限制,我们该怎么办呢? 很简单,把待加密的字符串拆开,每段长度都小于等于限制长度,然后分段加密,这样,问题就解决了。
分段加密代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
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中有代码具体封装的代码。
调用代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
#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加密的密文最长。