最近在看我所在项目系统里支付方面的代码,里面包括很多加密解密方面的知识,结合平时对加密解密的了解,对.net里加解密做一个总结。
最开始遇到加密解密,可能和大家一样,MD5加密,读大学的时候 ,我喜欢了解一些安全方面知识,当时很多小型网站都存在SQL注入,找个小工具,很轻松跑出用户帐号和密码,一看密码是16位或32位,一般都是MD5加密后没有再修改过,通过http://www.cmd5.com/ 或http://www.xmd5.org/ 等网站,直接就“解密”成原始明文了(但现在这些网站已开始收费了)。最后过了一段时间才知道,原来这个是Hash散列加密,不可逆的,这些网站都通过人工学把不同帐号跑出来存储到数据库,查询的时候从数据库查询了。
.Net框架集成了常用加解密的类库,在System.Security.Cryptography命名空间下,包括可逆向加密和不可逆加密。可逆向加密又包括对称加密和非对称加密。
对称加密常用的有DES(Data Encryption Standard),TripleDES(基于DES,对一块数据用三个不通密钥进行3次加密)。DES的密钥Key为8字节,初始向量IV也为8字节;TripleDES的密钥Key为24字节,初始向量IV为8字节。都以8字节为一个块进行加密。还有AES(Advanced Encryption Standard),根据我的理解,AES只是一个标准,现在一般用该标准的算法为Rijndael。
非对称加密常用的有RSA,它通过公钥加密,私钥解密,并且他还可以用私钥签名,公钥验证的功能,一般应用于网络交互,比如IPS支付时,我们需要向IPS申请一个公钥,我们通过公钥加密一些关键字符,就只有IPS的私钥才能解密了。但在使用过程中,常常添加了比如散列比较等,而且非对称算法比对称算法慢很多,非对称公钥加密也常用来加密较少字段或者加密临时对称加密Key使用。
不可逆加密主要是散列算法加密,常见的有MD5,SHA1算法。MD5可以生产16字节的不可逆数字,SHA1可以生成20字节的不可逆数字。MD5有时表现出来是32位的数字或字符,这是每个字节的16进制展现。经常能看到的16位的MD5值,是取的MD5从第8位到第23位(包括23)之间的数字。一般在下载一些软件的时候,也会提示该软件MD5值是多少,帮助下载下来后验证,可以写个小工具通过从读取软件到内存流,再计算MD5,来校验软件。
下面直接写出TripleDES加密解密方法,步骤可以见注释:
public static byte[] IV = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };//加密IV向量
public static string EncryptString(string plainText, string key)
{
//如果Key不到24位,则用0补
key = key.PadLeft(24, '0');
byte[] bKey = Encoding.UTF8.GetBytes(key.Substring(0, 24));
// 把字符串明文转换成utf-8编码的字节流
byte[] plainTextArray = Encoding.UTF8.GetBytes(plainText);
try
{
// 用MemoryStream存放加密后的数据流
using (MemoryStream mStream = new MemoryStream())
{
// 使用MemoryStream 和Key、IV新建一个CryptoStream 对象
using (CryptoStream cStream = new CryptoStream(mStream,
new TripleDESCryptoServiceProvider().CreateEncryptor(bKey, IV),
CryptoStreamMode.Write))
{
// 将加密后的字节流写入MemoryStream
cStream.Write(plainTextArray, 0, plainTextArray.Length);
//把缓冲区更新到MemoryStream
cStream.FlushFinalBlock();
// 把解密后的数据流转成字节流,并转换成Base64编码
return Convert.ToBase64String(mStream.ToArray());
}
}
}
catch (Exception ex)
{
throw ex;
}
}
public static string DecryptString(string encryptedData, string key)
{
//如果Key不到24位,则用0补
key = key.PadLeft(24, '0');
byte[] bKey = Encoding.UTF8.GetBytes(key.Substring(0, 24));
// 把字符串明文转换成utf-8编码的字节流
byte[] encryptedDataArray = Convert.FromBase64String(encryptedData);
try
{
// MemoryStream存放加密后的数据流
using (MemoryStream msDecrypt = new MemoryStream(encryptedDataArray))
{
// 使用MemoryStream 和key、IV新建一个CryptoStream 对象
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt,
new TripleDESCryptoServiceProvider().CreateDecryptor(bKey, IV),
CryptoStreamMode.Read))
{
byte[] decryptDataArray = new byte[encryptedDataArray.Length];
// 把解密后的数据读入到DecryptDataArray
csDecrypt.Read(decryptDataArray, 0, decryptDataArray.Length);
return Encoding.UTF8.GetString(decryptDataArray);
}
}
}
catch (Exception ex)
{
throw ex;
}
}
这里加密用csDecrypt写到内存流中,解密用csDecrypt流读取解码后的字符读到临时byte[]变量里,其实都可以一样,这里不同方法都可以读写出来,最后加密的结果直接转换成Base64编码,方便网络传输。默认new TripleDESCryptoServiceProvider().IV或Key就可以读取一个默认的Key和IV。除此之外,还有加密模式和填充模式的选择,这里用的默认模式,可以设置Mode,Padding来分别设置加密模式和填充模式,这块我也只是了解,当要求如果严格的时候,需要再仔细看下。
RSA加密解密方法如下:
public static string[] GenerateKeys()
{
string[] sKeys = new string[2];
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
//私钥
sKeys[0] = rsa.ToXmlString(true);
//公钥
sKeys[1] = rsa.ToXmlString(false);
return sKeys;
}
public static string EncryptString(string sSource, string sPublicKey)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
string plaintext = sSource;
try
{
//导入公钥加密
rsa.FromXmlString(sPublicKey);
byte[] cipherbytes = rsa.Encrypt(Encoding.UTF8.GetBytes(plaintext), false);
return Convert.ToBase64String(cipherbytes);
}
catch (Exception ex)
{
throw ex;
}
}
public static string DecryptString(string sSource, string sPrivateKey)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
try
{
//导入私钥解密
rsa.FromXmlString(sPrivateKey);
byte[] byteEn = Convert.FromBase64String(sSource);
byte[] plaintbytes = rsa.Decrypt(byteEn, false);
return Encoding.UTF8.GetString(plaintbytes);
}
catch (Exception ex)
{
throw ex;
}
}
由于非对称加密公钥和私钥表现成一个xml的字符串,这里用GenerateKeys方法返回一个默认的公钥私钥对。私钥类试:
<RSAKeyValue><Modulus>qZ0BKzsUNJR5x9TUYfAaDzuLuJ6uxT3eE9Zqd2MKQ+FmS1az43uH8bGHerWoStl2tSXUDENQ0AieaxFpn/BTJZ33rmT3PPxv6022vwB+17XYfCDigb7db30jKIefDydQMEC8P/4EAEp6/IiE4H35P+2dwoN3/p4U+C2aTFIpQ78=</Modulus><Exponent>AQAB</Exponent><P>7GkvmXofKoNwEUEtVtYT0Na04Ua4cZA25GfK5ZB8XsxMyTEr9pNMjNZLJlRAG1TeOSM2qOisofGfytQHidU/rw==</P><Q>t6rjt7IXXRF8/ye+bnzhvFvRv0fo0wObdYTuD8d4ruxLI8p847cfI+IGIyXGz7lXkLOuAVk5hVfpFqgx3WCw8Q==</Q><DP>VI0eIAcERIEzwIgN/iOcfLF9iaBwcPVCHJhegZIWWRU6VS6H6a0u0KQHxpKVRvEodUj50Jk+vMCdBL6mX45sMw==</DP><DQ>q/VsdL1h0HoiLIZabfmwI3lYHJ3H52C2OUY22UEpxaRoCV94pH77wc3JbzjcNfnSeSExJgQSrbyL9/GLljgEEQ==</DQ><InverseQ>ppiIZBMKM74wSwSyBVoYNjScnW2Hjf3kP+q15GONazTp9Gx8uSQViy8UI+GCqMT/8p5ySzVISwF2jWFw8uhhog==</InverseQ><D>KQ6QUd6jLXcjY3PpVSvBox5O3AnNVIF9WF/2tZ+LxJKzKFl8gfxNE/xdRx7h9fxd98uIYM+KqFDtyA0W/Fg4R+Uda1SHOLDgsdo1FISmQsCEK8E8jIINf7QeYTOPconvYDglznAfZBE0Ri+keLZPnsy6Pslths56wB40yW+GJwE=</D></RSAKeyValue>。
公钥就只有Modulus和Exponent节点。
散列算法MD5加密如:
public static string GetMd5(string contents)
{
if (string.IsNullOrEmpty(contents))
{
return string.Empty;
}
StringBuilder result = new StringBuilder();
System.Security.Cryptography.MD5CryptoServiceProvider md = new System.Security.Cryptography.MD5CryptoServiceProvider();
byte[] hash = md.ComputeHash(System.Text.Encoding.UTF8.GetBytes(contents));
md.Clear();
for (int i = 0, len = hash.Length; i < len; i++)
{
//每个字符转换为16进制
result.Append(hash[i].ToString("X").PadLeft(2, '0'));
}
return result.ToString();
}
但在B/S网站中生产MD5值时,一般可以直接:
System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(source, "MD5");返回32位长的MD5值。
以上介绍了可逆不可逆加密方式,和常用的加密解密算法,当然还有很多算法没有介绍,并且这些算法的深入使用也是有很多可以介绍的地方,但本篇作为抛砖引玉,以后更加深入使用时再仔细学习了解。
最后上传测试代码,VS2008,.NET3.5下编译运行通过!
/Files/Lawson/EncryptionTests.rar