C#加解密
加密解密在开发中经常用到,比如登录密码加密解密、消息传输加密解密等。但是很多人只会使用不理解其中的原理,这篇文章就带领大家快速学习加密解密的原理和使用。
零、什么是加密解密
通俗的说加密解密就是将不想让别人很容易看到的东西根据一定的规则进行处理,形成无规则的内容。拿到加密后内容的人通过一定规则将加密后的内容还原成原文。这里所说的原文在计算机中被称为明文,加密后的内容被称为密文。加密后的内容并不是完全不可破解的,只是提高了破解的难度,让大多数人退缩。那么怎么判断一个加解密安全呢?一般来说加密消息在发送方和接收方进行传递时必须满足三个条件:
- 发送方可以确定所发送的消息只有预期接收方可以解密,但不保证其他非接收方获得密文,只要保证非接收方获得密文后无法解密即可;
- 接收方可以确定消息是谁发送的;
- 接收方可以确定消息在传输过程中没有被篡改,也就是说可以验证消息的完整性。
一般来说常用的加密方式有三种: - 不可逆加密;
- 对称可逆加密;
- 非堆成可逆加密。
下面我来讲一讲这三种常用的加密方式。
一、不可逆加密
常用的不可逆加密是 MD5 加密,MD5是Message-Digest Algorithm 5英文简称,MD5 的特点如下:
- 任意长度的输入,经过 MD5 处理后都会输出128位的信息,这个信息称作数字指纹;
- 输入不同的信息产生的数字指纹不一样,数字指纹是全球唯一的;
- 无法根据数字指纹推导出加密前的信息。
MD5 我们经常用在文档防篡改、密码加密、数字签名、网盘秒传。针对这四个方面我下面简单讲解一下: - 文档防篡改
在文档发送前记录文档的 MD5 值,接收方收到文档后计算文档的 MD5 值,如果两个 MD5 值不一样就说明文档在发送过程中被篡改过。 - 密码加密
如果将密码明文存储在数据库中,泄露后可以别人可以直接登录,在用 MD5 将密码加密后即使泄露了也无法通过密文直接登录。但是这里需要注意,目前网上有很多破解 MD5 密文的网站,这些网站说白了就是利用撞库实现的,这些网站收集了常用的密码组合方式,比如生日、连续相同的数字或密码等,因此我们在验证密码强度时应当将常见的简单密码列,针对所有密码最好将密码中加入随机的信息内容然后再进行 MD5 加密,这就是所谓的加盐。 - 数字签名
某人写了一个重要文件,这个文件经过认证机构利用 MD5 生成摘要信息进行认证,如果以后这个人不承认这个文件是他写的,只需认证机构再次对文件生成摘要信息和以前的照耀信息进行对比即可知道该文件是所写的。 - 网盘秒传
网盘记录文件第一次上传的 MD5 值,以后当有人上传具有相同 MD5 值的文件时只需要将存在于网盘中的这个文件的链接发送出去即可。
MD5 的实现很简单代码如下:
public class MD5
{
/// <summary>
///
/// </summary>
/// <param name="source">待加密字串</param>
/// <param name="length">加密结果长度</param>
/// <returns>加密后的字串</returns>
public string Encrypt(string source, int length = 32)
{
if (string.IsNullOrEmpty(source))
{
return string.Empty;
}
HashAlgorithm provider = CryptoConfig.CreateFromName("MD5") as HashAlgorithm;
byte[] bytes = Encoding.UTF8.GetBytes(source);
byte[] hashValue = provider.ComputeHash(bytes);
StringBuilder sb = new StringBuilder();
switch (length)
{
case 16:
for (int i = 4; i < 12; i++)
{
sb.Append(hashValue[i].ToString("x2"));
}
break;
case 32:
for (int i = 0; i < 16; i++)
{
sb.Append(hashValue[i].ToString("x2"));
}
break;
default:
for (int i = 0; i < hashValue.Length; i++)
{
sb.Append(hashValue[i].ToString("x2"));
}
break;
}
return sb.ToString();
}
/// <summary>
/// 获取文件的MD5
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public string AbstractFile(string fileName)
{
FileStream file = new FileStream(fileName, FileMode.Open);
System.Security.Cryptography.MD5 md5 = new MD5CryptoServiceProvider();
byte[] retVal = md5.ComputeHash(file);
file.Close();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < retVal.Length; i++)
{
sb.Append(retVal[i].ToString("x2"));
}
return sb.ToString();
}
}
二、对称可逆加密
对称可逆加密就是用密钥将需要加密的内容进行加密,然后将加密后的内容发送给接收方,接收方街道内容后利用相同的密钥进行解密后就拿到了明文。对称可逆加密方式有一个严重的问题就是要保证密钥的安全,一旦密钥泄密第三方就可以利用这个密钥对解密收到的密文,甚至可以利用密钥伪造信息发送给接收方,接收方也就无法分辨出到底是谁发送的密文。
最常用的对称可逆加密算法是 DES ,下面我们通过代码看一下 DES 的实现:
public class DES
{
/// <summary>
/// 密钥
/// </summary>
private byte[] key = ASCIIEncoding.ASCII.GetBytes("123456");
/// <summary>
/// 偏移量
/// </summary>
private byte[] iv = ASCIIEncoding.ASCII.GetBytes("123456".Insert(0, "w").Substring(0, 8));
/// <summary>
/// 加密
/// </summary>
/// <param name="text">需要加密的值</param>
/// <returns>加密后的结果</returns>
public string Encrypt(string text)
{
DESCryptoServiceProvider dsp = new DESCryptoServiceProvider();
using (MemoryStream memStream = new MemoryStream())
{
CryptoStream crypStream = new CryptoStream(memStream, dsp.CreateEncryptor(key, iv), CryptoStreamMode.Write);
StreamWriter sWriter = new StreamWriter(crypStream);
sWriter.Write(text);
sWriter.Flush();
crypStream.FlushFinalBlock();
memStream.Flush();
return Convert.ToBase64String(memStream.GetBuffer(), 0, (int)memStream.Length);
}
}
/// <summary>
/// 解密
/// </summary>
/// <param name="encryptText"></param>
/// <returns>解密后的结果</returns>
public string Decrypt(string encryptText)
{
DESCryptoServiceProvider dsp = new DESCryptoServiceProvider();
byte[] buffer = Convert.FromBase64String(encryptText);
using (MemoryStream memStream = new MemoryStream())
{
CryptoStream crypStream = new CryptoStream(memStream, dsp.CreateDecryptor(key, iv), CryptoStreamMode.Write);
crypStream.Write(buffer, 0, buffer.Length);
crypStream.FlushFinalBlock();
return ASCIIEncoding.UTF8.GetString(memStream.ToArray());
}
}
}
三、非对称可逆加密
非对称加密比较麻烦,发送方和接收方都保存有两个密钥,其中一个密钥是公钥,公钥是对外公开的,任何人都可以拿到,另一个是私钥,每个人的私钥不一样。规则是由A公钥加密的信息只能用A的私钥解密,由A的私钥加密的消息只能由A的公钥解密。
非对称可逆加密的模式由两种:
- 加密模式
使用接收方的公钥加密,然后使用接收方的私钥解密,这样可以保证只有特定的接收方能收到信息,但是无法确认发送方是谁。 - 认证模式
使用发送方的私钥加密,然后使用发送方的公钥解密,这样可以保证特定发送方发送消息,但是无法确定消息是发给了谁。
从上述两种模式的描述中我们可以看到他们存在问题,因此就出现了数字签名,在上述认证模式中加入了散列算法(例如MD5),对明文进行处理后再把信息进行加密后发送出去,接收方收到信息解密后比较信息的散列值和原始消息的散列值就可以确定信息是否被篡改。
常用的非对称可逆加密算法常用的是RSA,代码实现如下:
public class Rsa
{
/// <summary>
/// 获取加密/解密对
/// </summary>
/// <returns>Encrypt Decrypt</returns>
public static KeyValuePair<string, string> GetKeyPair()
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
string publicKey = RSA.ToXmlString(false);
string privateKey = RSA.ToXmlString(true);
return new KeyValuePair<string, string>(publicKey, privateKey);
}
/// <summary>
/// 加密
/// </summary>
/// <param name="content"></param>
/// <param name="encryptKey">加密key</param>
/// <returns></returns>
public static string Encrypt(string content, string encryptKey)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(encryptKey);
UnicodeEncoding ByteConverter = new UnicodeEncoding();
byte[] DataToEncrypt = ByteConverter.GetBytes(content);
byte[] resultBytes = rsa.Encrypt(DataToEncrypt, false);
return Convert.ToBase64String(resultBytes);
}
/// <summary>
/// 解密
/// </summary>
/// <param name="content"></param>
/// <param name="decryptKey">解密key</param>
/// <returns></returns>
public static string Decrypt(string content, string decryptKey)
{
byte[] dataToDecrypt = Convert.FromBase64String(content);
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSA.FromXmlString(decryptKey);
byte[] resultBytes = RSA.Decrypt(dataToDecrypt, false);
UnicodeEncoding ByteConverter = new UnicodeEncoding();
return ByteConverter.GetString(resultBytes);
}
/// <summary>
/// 产生一组新的密钥
/// </summary>
/// <param name="content"></param>
/// <param name="encryptKey">加密key</param>
/// <param name="decryptKey">解密key</param>
/// <returns>加密后结果</returns>
private static string Encrypt(string content, out string publicKey, out string privateKey)
{
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider();
publicKey = rsaProvider.ToXmlString(false);
privateKey = rsaProvider.ToXmlString(true);
UnicodeEncoding ByteConverter = new UnicodeEncoding();
byte[] DataToEncrypt = ByteConverter.GetBytes(content);
byte[] resultBytes = rsaProvider.Encrypt(DataToEncrypt, false);
return Convert.ToBase64String(resultBytes);
}
}
总结
上述三种加密方式在开发中会经常用到,根据我在开发中的经验我建议使用非对称加密,这样更安全。