-
C#网络编程之加密与解密(对称加密、非对称加密、哈希算法)
第17章 加密与解密
17.1 加密与解密(对称加密、非对称加密、哈希算法)
一、我踩过的加密坑:从密码泄露到性能雪崩
刚做第一个用户系统时,为了图快用AES对称加密存用户密码,结果数据库被拖库,所有密码被破解,赔了用户10万;后来改用SHA1哈希算法存密码,又没加盐,被彩虹表破解,一半用户密码泄露;再后来用RSA加密用户上传的大文件,结果性能极差,服务器CPU直接跑满,响应时vb.net教程C#教程python教程SQL教程access 2010教程间超过10秒。踩过这些坑后,我才明白:加密不是“随便选个算法就行”,必须根据场景选对算法,还要注意细节(比如加盐、加密模式)。这节我把自己从“加密小白”到“实战专家”的血泪史揉进去,用大白话讲透对称加密、非对称加密、哈希算法的核心原理,结合C#实战代码逐行讲解,以及常见坑和最佳实践,让你的数据像银行一样安全。
二、对称加密:“一把钥匙开所有锁”,适合加密大数据
对称加密是用同一个密钥(Key)进行加密和解密,比如AES、DES、3DES——就像你用一把钥匙锁门,开门也用这把钥匙。它的核心优势是速度快,适合加密大数据(比如文件、视频、大文本)。
核心概念(拓展知识)
密钥(Key):加密和解密用的同一个字符串,长度决定安全性(AES-128、AES-256,数字越大越安全);
初始化向量(IV):防止相同明文得到相同密文,必须随机生成,不需要保密,但要和密钥一起使用;
加密模式:
ECB:最不安全,相同明文得到相同密文,容易被破解(不推荐);
CBC:需要IV,相同明文得到不同密文,但需要额外的MAC校验完整性;
GCM:目前推荐的模式,支持认证加密,同时保证机密性和完整性,性能好。
类比:对称加密就像你和朋友共用一把钥匙,你用钥匙锁文件,朋友用同一把钥匙打开——但钥匙不能丢,否则别人也能打开文件。
实战1:AES-GCM对称加密(C#实战)
AES是目前最安全的对称加密算法,GCM模式支持认证加密,同时保证机密性和完整性,性能比CBC好。
csharp
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace SymmetricEncryption;
class AesGcmExample
{
static void Main(string[] args)
{
// 1. 生成256位密钥(AES-256,最安全)
byte[] key = AesGcm.GenerateKey(AesGcm.KeyByteSizes.MaxSize);
Console.WriteLine($"生成密钥:{Convert.ToBase64String(key)}");
// 2. 要加密的明文
string plaintext = "用户敏感数据:银行卡号1234567890123456,密码abc123";
Console.WriteLine($"明文:{plaintext}");
// 3. 加密
byte[] ciphertext;
byte[] tag; // 认证标签,用于验证密文完整性
byte[] nonce = new byte[AesGcm.NonceByteSizes.MaxSize]; // 随机生成Nonce(IV)
RandomNumberGenerator.Fill(nonce); // 用安全随机数生成器生成Nonce,避免重复
using var aesGcm = new AesGcm(key);
byte[] plaintextBytes = Encoding.UTF8.GetBytes(plaintext);
ciphertext = new byte[plaintextBytes.Length];
tag = new byte[AesGcm.TagByteSizes.MaxSize];
// 加密:plaintextBytes->ciphertext,生成tag
aesGcm.Encrypt(nonce, plaintextBytes, ciphertext, tag);
Console.WriteLine($"密文:{Convert.ToBase64String(ciphertext)}");
Console.WriteLine($"认证标签:{Convert.ToBase64String(tag)}");
Console.WriteLine($"Nonce:{Convert.ToBase64String(nonce)}");
// 4. 解密
try
{
byte[] decryptedBytes = new byte[ciphertext.Length];
aesGcm.Decrypt(nonce, ciphertext, tag, decryptedBytes);
string decryptedText = Encoding.UTF8.GetString(decryptedBytes);
Console.WriteLine($"解密后明文:{decryptedText}");
}
catch (CryptographicException ex)
{
Console.WriteLine($"解密失败:{ex.Message}(密文被篡改或密钥错误)");
}
}
}
代码逐行讲解:
1.AesGcm.GenerateKey:生成256位密钥,AesGcm支持128、192、256位密钥,256位最安全;
2.Nonce:随机生成的初始化向量,GCM模式的Nonce必须唯一,不能重复——用RandomNumberGenerator.Fill生成安全随机数,避免用Random类(不安全,容易预测);
3.Encrypt:加密明文,生成密文和认证标签(Tag)——Tag用于验证密文完整性,如果密文被篡改,解密时会抛出CryptographicException;
4.Decrypt:解密时需要Nonce、密文、Tag、密钥,缺一不可——如果密文被篡改或密钥错误,会抛出异常,保证数据完整性。
我踩过的坑:一开始用Random类生成Nonce,结果Nonce重复,导致相同明文得到相同密文,被破解——必须用RandomNumberGenerator生成安全随机数!
拓展知识:AES-GCM vs AES-CBC
| 特性 | AES-GCM | AES-CBC |
|---|---|---|
| 性能 | 快(并行加密) | 慢(串行加密) |
| 完整性验证 | 支持(Tag) | 不支持(需要额外MAC) |
| 安全性 | 高(无已知漏洞) | 中(需要IV,容易被破解如果IV重复) |
| 适用场景 | 所有对称加密场景(推荐) | 兼容旧系统(不推荐) |
三、非对称加密:“公钥锁门,私钥开门”,适合加密小数据
非对称加密是用一对密钥:公钥(Public Key)加密,私钥(Private Key)解密,比如RSA、ECC——就像你把公钥给朋友,朋友用公钥锁文件,只有你用私钥能打开。它的核心优势是安全,适合加密小数据(比如对称密钥、数字签名),但速度慢,不适合加密大数据。
核心概念(拓展知识)
公钥:可以公开给任何人,用于加密数据或验证数字签名;
私钥:必须保密,用于解密数据或生成数字签名;
数字签名:用私钥对数据哈希值加密,别人用公钥验证,确保数据是你发的且未被篡改;
ECC vs RSA:ECC(椭圆曲线加密)比RSA更安全,相同安全强度下,ECC的密钥长度是RSA的1/10,性能更好(比如ECC-256和RSA-3072安全强度相同,但ECC速度快3倍)。
类比:非对称加密就像你有一个邮箱,任何人都可以用邮箱的锁(公钥)把信放进去,但只有你有钥匙(私钥)能打开邮箱——公钥可以随便给人,私钥必须自己保管好。
实战2:RSA非对称加密+数字签名(C#实战)
RSA是最常用的非对称加密算法,适合加密对称密钥或生成数字签名。
步骤1:生成RSA密钥对
csharp
using System;
using System.Security.Cryptography;
namespace AsymmetricEncryption;
class RsaKeyGenerator
{
static void Main(string[] args)
{
// 生成2048位RSA密钥对(2048位是目前的安全标准,1024位已被破解)
using var rsa = RSA.Create(2048);
// 导出公钥(XML格式,可公开)
string publicKey = rsa.ToXmlString(includePrivateParameters: false);
Console.WriteLine($"公钥:
{publicKey}");
// 导出私钥(XML格式,必须保密)
string privateKey = rsa.ToXmlString(includePrivateParameters: true);
Console.WriteLine($"私钥:
{privateKey}");
// 保存到文件(实际项目中私钥要加密存储,比如用DPAPI或Azure Key Vault)
File.WriteAllText("public_key.xml", publicKey);
File.WriteAllText("private_key.xml", privateKey);
}
}
代码逐行讲解:
RSA.Create(2048):生成2048位RSA密钥对,2048位是目前的安全标准,1024位已被破解,4096位性能差;
ToXmlString:导出公钥和私钥,includePrivateParameters: false导出公钥,true导出私钥;
私钥存储:私钥必须保密,不能明文存储在文件或数据库中,实际项目中要用DPAPI(Windows数据保护API)或云密钥管理服务(比如Azure Key Vault)加密存储。
步骤2:用公钥加密,私钥解密
csharp
using System;
using System.Security.Cryptography;
using System.Text;
namespace RsaEncryption;
class RsaExample
{
static void Main(string[] args)
{
// 1. 加载公钥和私钥
string publicKey = File.ReadAllText("public_key.xml");
string privateKey = File.ReadAllText("private_key.xml");
// 2. 要加密的小数据(RSA只能加密小于密钥长度的数据,2048位RSA最多加密245字节)
string plaintext = "对称加密密钥:abc1234567890abc1234567890";
Console.WriteLine($"明文:{plaintext}");
// 3. 用公钥加密
using var rsaPublic = RSA.Create();
rsaPublic.FromXmlString(publicKey);
byte[] plaintextBytes = Encoding.UTF8.GetBytes(plaintext);
// OAEP填充模式比PKCS#1更安全,推荐使用
byte[] ciphertext = rsaPublic.Encrypt(plaintextBytes, RSAEncryptionPadding.OaepSHA256);
Console.WriteLine($"密文:{Convert.ToBase64String(ciphertext)}");
// 4. 用私钥解密
using var rsaPrivate = RSA.Create();
rsaPrivate.FromXmlString(privateKey);
byte[] decryptedBytes = rsaPrivate.Decrypt(ciphertext, RSAEncryptionPadding.OaepSHA256);
string decryptedText = Encoding.UTF8.GetString(decryptedBytes);
Console.WriteLine($"解密后明文:{decryptedText}");
}
}
代码逐行讲解:
RSA加密限制:2048位RSA最多加密245字节(因为OAEP填充需要占用42字节),所以RSA适合加密对称密钥,不适合加密大数据;
RSAEncryptionPadding.OaepSHA256:OAEP填充模式比PKCS#1更安全,推荐使用——PKCS#1有已知漏洞,容易被破解;
加密大数据的正确方式:用AES加密大数据,用RSA加密AES密钥,然后把AES密文、RSA加密的AES密钥、Nonce、Tag一起发给对方,对方用RSA解密AES密钥,再用AES解密大数据。
我踩过的坑:用RSA加密1MB的文件,结果直接抛出异常——RSA只能加密小数据,必须用“对称加密+非对称加密”的混合模式!
步骤3:数字签名与验证
csharp
using System;
using System.Security.Cryptography;
using System.Text;
namespace DigitalSignature;
class SignatureExample
{
static void Main(string[] args)
{
string privateKey = File.ReadAllText("private_key.xml");
string publicKey = File.ReadAllText("public_key.xml");
string data = "用户订单:订单ID123456,金额100元";
// 1. 用私钥生成数字签名
using var rsaPrivate = RSA.Create();
rsaPrivate.FromXmlString(privateKey);
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
// 先对数据做SHA256哈希,再用私钥加密哈希值(数字签名)
byte[] signature = rsaPrivate.SignData(dataBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
Console.WriteLine($"数字签名:{Convert.ToBase64String(signature)}");
// 2. 用公钥验证数字签名
using var rsaPublic = RSA.Create();
rsaPublic.FromXmlString(publicKey);
bool isVerified = rsaPublic.VerifyData(dataBytes, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
Console.WriteLine($"签名验证结果:{isVerified}");
// 3. 篡改数据后验证
string tamperedData = "用户订单:订单ID123456,金额200元";
byte[] tamperedDataBytes = Encoding.UTF8.GetBytes(tamperedData);
bool isTamperedVerified = rsaPublic.VerifyData(tamperedDataBytes, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
Console.WriteLine($"篡改数据后验证结果:{isTamperedVerified}");
}
}
代码逐行讲解:
SignData:用私钥对数据的SHA256哈希值加密,生成数字签名——数字签名的作用是:① 确保数据是私钥持有者发的(不可否认);② 确保数据未被篡改;
VerifyData:用公钥验证数字签名,验证通过说明数据未被篡改且是私钥持有者发的;
RSASignaturePadding.Pss:PSS填充模式比PKCS#1更安全,推荐使用——PKCS#1有已知漏洞,容易被伪造签名。
拓展知识:HTTPS的握手过程
HTTPS就是用了“对称加密+非对称加密”的混合模式:
1.客户端向服务器请求公钥;
2.服务器发送公钥给客户端;
3.客户端生成随机对称密钥,用服务器公钥加密,发给服务器;
4.服务器用私钥解密得到对称密钥;
5.后续通信用对称密钥加密,速度快且安全。
四、哈希算法:“数据指纹”,适合存储密码和校验完整性
哈希算法是把任意长度的数据变成固定长度的哈希值(比如SHA256是256位),不可逆,相同明文得到相同哈希值,不同明文得到不同哈希值(概率极低),比如SHA256、SHA3、MD5(已被破解)——就像你把文件变成一个固定长度的指纹,只要文件变了,指纹就变了。
核心概念(拓展知识)
不可逆:无法从哈希值还原明文(除非暴力破解);
雪崩效应:明文微小变化,哈希值完全不同;
加盐(Salt):在明文前加随机字符串,防止彩虹表破解——比如密码“abc123”加盐“xyz”变成“xyzabc123”,再哈希;
慢哈希算法:比如BCrypt、Argon2,故意设计成慢算法,防止暴力破解密码。
类比:哈希算法就像你把文件放进粉碎机,变成一堆碎纸,无法还原成原来的文件,但相同的文件会变成相同的碎纸——适合存储密码(存哈希值,不存明文)、校验文件完整性(比如下载文件后对比哈希值,确保文件未被篡改)。
实战3:SHA256哈希加盐存储密码(C#实战)
SHA256是目前推荐的哈希算法,适合存储密码,但必须加盐,防止彩虹表破解。
csharp
using System;
using System.Security.Cryptography;
using System.Text;
namespace HashAlgorithm;
class PasswordHashing
{
static void Main(string[] args)
{
string password = "用户密码abc123";
Console.WriteLine($"原密码:{password}");
// 1. 生成随机盐(16字节,推荐长度)
byte[] salt = new byte[16];
RandomNumberGenerator.Fill(salt);
Console.WriteLine($"盐:{Convert.ToBase64String(salt)}");
// 2. 加盐哈希密码(用Rfc2898DeriveBytes,支持迭代次数,慢哈希)
int iterations = 100000; // 迭代次数,越高越慢,越安全(推荐10万次以上)
int hashSize = 256; // 哈希长度256位
using var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256);
byte[] hash = pbkdf2.GetBytes(hashSize / 8);
// 3. 存储盐和哈希值(实际项目中存到数据库:盐+哈希值)
string storedPassword = $"{Convert.ToBase64String(salt)}:{Convert.ToBase64String(hash)}:{iterations}";
Console.WriteLine($"存储到数据库的密码:{storedPassword}");
// 4. 验证密码
string inputPassword = "用户密码abc123";
bool isPasswordValid = VerifyPassword(inputPassword, storedPassword);
Console.WriteLine($"密码验证结果:{isPasswordValid}");
// 5. 错误密码验证
string wrongPassword = "错误密码abc123";
bool isWrongPasswordValid = VerifyPassword(wrongPassword, storedPassword);
Console.WriteLine($"错误密码验证结果:{isWrongPasswordValid}");
}
/// <summary>
/// 验证密码
/// </summary>
static bool VerifyPassword(string inputPassword, string storedPassword)
{
string[] parts = storedPassword.Split(':');
if (parts.Length != 3)
{
return false;
}
byte[] salt = Convert.FromBase64String(parts[0]);
byte[] hash = Convert.FromBase64String(parts[1]);
int iterations = int.Parse(parts[2]);
using var pbkdf2 = new Rfc2898DeriveBytes(inputPassword, salt, iterations, HashAlgorithmName.SHA256);
byte[] inputHash = pbkdf2.GetBytes(hash.Length);
// 用CryptographicOperations.FixedTimeEquals比较,防止计时攻击
return CryptographicOperations.FixedTimeEquals(hash, inputHash);
}
}
代码逐行讲解:
Rfc2898DeriveBytes:基于PBKDF2算法的慢哈希,支持迭代次数——迭代次数越高,哈希越慢,暴力破解越难(推荐10万次以上);
加盐:随机生成16字节盐,每个用户的盐不同,即使两个用户密码相同,哈希值也不同,防止彩虹表破解;
CryptographicOperations.FixedTimeEquals:固定时间比较哈希值,防止计时攻击——如果用==比较,攻击者可以通过比较时间长短猜测哈希值的部分内容。
我踩过的坑:一开始用SHA256直接哈希密码,没加盐,被彩虹表破解——必须加盐,每个用户的盐不同!
拓展知识:哈希算法的应用场景
1.密码存储:存哈希值,不存明文,加盐;
2.文件校验:下载文件后对比哈希值,确保文件未被篡改(比如Linux镜像的SHA256哈希);
3.数字签名:先对数据做哈希,再用私钥加密哈希值,比直接加密数据快;
4.数据去重:用哈希值判断数据是否重复(比如云存储的秒传)。
哈希算法对比:
| 算法 | 安全性 | 适用场景 |
|---|---|---|
| MD5 | 低(已被破解) | 兼容旧系统(不推荐) |
| SHA1 | 低(已被破解) | 兼容旧系统(不推荐) |
| SHA256 | 高(无已知漏洞) | 所有哈希场景(推荐) |
| SHA3 | 高(无已知漏洞) | 新系统(推荐) |
| BCrypt | 高(慢哈希) | 密码存储(推荐) |
| Argon2 | 高(慢哈希,抗ASIC) | 密码存储(最推荐) |
五、总结:三种加密方式的适用场景对比
| 特性 | 对称加密(AES-GCM) | 非对称加密(RSA/ECC) | 哈希算法(SHA256/Argon2) |
|---|---|---|---|
| 可逆性 | 可逆(密钥解密) | 可逆(私钥解密) | 不可逆 |
| 速度 | 快(适合大数据) | 慢(适合小数据) | 快 |
| 安全性 | 高(密钥保密) | 高(私钥保密) | 高(不可逆) |
| 适用场景 | 加密大数据(文件、视频、会话) | 加密小数据(对称密钥、数字签名) | 存储密码、校验完整性、数字签名 |
最佳实践
1.对称加密选AES-GCM:速度快、安全、支持完整性验证;
2.非对称加密选ECC或RSA-2048+:ECC性能更好,RSA兼容性更好;
3.哈希算法选SHA256或Argon2:存储密码用Argon2(慢哈希),其他场景用SHA256;
4.密码存储必须加盐:每个用户的盐不同,用慢哈希算法;
5.密钥必须保密:对称密钥和私钥不能明文存储,用DPAPI或云密钥管理服务加密存储;
6.避免常见坑:
对称加密不要用ECB模式,不要用Random生成IV;
非对称加密不要加密大数据,用混合模式;
哈希算法不要用MD5、SHA1,不要不加盐。
下一节我们会学习数字证书与HTTPS,让你的网络通信既安全又可信。
本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49549.html










