目录
-
密码安全的核心概念
-
加密 vs 哈希:何时使用?
-
密钥管理的重要性
-
常见攻击手段(中间人攻击、彩虹表)
-
-
基础加密技术
-
对称加密(AES)
-
非对称加密(RSA)
-
哈希算法(SHA3、HMAC)
-
-
现代安全实践
-
密码存储方案(PBKDF2、BCrypt)
-
数据保护API(Microsoft DPAPI)
-
安全随机数生成(CSPRNG)
-
-
代码实战
-
AES加密解密完整流程
-
RSA密钥对生成与加密
-
使用BCrypt安全存储密码
-
-
安全陷阱与防御
-
不要使用ECB模式!
-
密钥硬编码的风险
-
防止Padding Oracle攻击
-
-
企业级解决方案
-
Azure Key Vault集成
-
硬件安全模块(HSM)
-
-
常见问题解答
1. 密码安全的核心概念
加密 vs 哈希
特性 | 加密 | 哈希 |
---|---|---|
可逆性 | 是 | 否 |
适用场景 | 数据传输(如信用卡号) | 密码存储 |
典型算法 | AES、RSA | SHA3、PBKDF2、BCrypt |
密钥管理三原则
-
保密性:主密钥必须安全存储(如HSM)
-
轮换策略:定期更换加密密钥
-
最小权限:按需分配密钥访问权限
2. 基础加密技术
AES对称加密(CBC模式示例)
using System.Security.Cryptography;
public class AesHelper
{
public static (string cipherText, string iv) Encrypt(string plainText, byte[] key)
{
using Aes aes = Aes.Create();
aes.Key = key;
aes.Mode = CipherMode.CBC; // 必须使用CBC或GCM模式
aes.Padding = PaddingMode.PKCS7;
// 生成随机IV(16字节)
aes.GenerateIV();
byte[] iv = aes.IV;
ICryptoTransform encryptor = aes.CreateEncryptor();
byte[] encrypted;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
cs.Write(plainBytes, 0, plainBytes.Length);
}
encrypted = ms.ToArray();
}
return (Convert.ToBase64String(encrypted), Convert.ToBase64String(iv));
}
public static string Decrypt(string cipherText, byte[] key, string ivBase64)
{
using Aes aes = Aes.Create();
aes.Key = key;
aes.IV = Convert.FromBase64String(ivBase64);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
ICryptoTransform decryptor = aes.CreateDecryptor();
byte[] cipherBytes = Convert.FromBase64String(cipherText);
using (var ms = new MemoryStream(cipherBytes))
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
using (var sr = new StreamReader(cs))
{
return sr.ReadToEnd();
}
}
}
RSA非对称加密
using System.Security.Cryptography;
public class RsaHelper
{
// 生成2048位密钥对
public static (string publicKey, string privateKey) GenerateKeyPair()
{
using RSA rsa = RSA.Create(2048);
return (
Convert.ToBase64String(rsa.ExportRSAPublicKey()),
Convert.ToBase64String(rsa.ExportRSAPrivateKey())
);
}
public static string Encrypt(string plainText, string publicKeyBase64)
{
byte[] publicKey = Convert.FromBase64String(publicKeyBase64);
using RSA rsa = RSA.Create();
rsa.ImportRSAPublicKey(publicKey, out _);
byte[] encrypted = rsa.Encrypt(Encoding.UTF8.GetBytes(plainText), RSAEncryptionPadding.OaepSHA256);
return Convert.ToBase64String(encrypted);
}
public static string Decrypt(string cipherText, string privateKeyBase64)
{
byte[] privateKey = Convert.FromBase64String(privateKeyBase64);
using RSA rsa = RSA.Create();
rsa.ImportRSAPrivateKey(privateKey, out _);
byte[] decrypted = rsa.Decrypt(Convert.FromBase64String(cipherText), RSAEncryptionPadding.OaepSHA256);
return Encoding.UTF8.GetString(decrypted);
}
}
3. 现代安全实践
密码存储方案(PBKDF2 + Salt)
using System.Security.Cryptography;
public class PasswordHasher
{
// 生成密码哈希(推荐参数)
public static (string hash, string salt) HashPassword(string password)
{
byte[] salt = new byte[32];
using var rng = RandomNumberGenerator.Create();
rng.GetBytes(salt);
int iterations = 150000; // OWASP 2021推荐值
using var pbkdf2 = new Rfc2898DeriveBytes(
password,
salt,
iterations,
HashAlgorithmName.SHA512
);
byte[] hash = pbkdf2.GetBytes(64); // 64字节=512位
return (
Convert.ToBase64String(hash),
Convert.ToBase64String(salt)
);
}
// 验证密码
public static bool VerifyPassword(string password, string storedHash, string storedSalt)
{
byte[] salt = Convert.FromBase64String(storedSalt);
byte[] hash = Convert.FromBase64String(storedHash);
using var pbkdf2 = new Rfc2898DeriveBytes(
password,
salt,
150000,
HashAlgorithmName.SHA512
);
byte[] testHash = pbkdf2.GetBytes(64);
return CryptographicOperations.FixedTimeEquals(hash, testHash);
}
}
Microsoft数据保护API(DPAPI)
// 适合本地数据保护(自动管理密钥)
using Microsoft.AspNetCore.DataProtection;
public class DataProtector
{
private readonly IDataProtector _protector;
public DataProtector(IDataProtectionProvider provider)
{
_protector = provider.CreateProtector("AppName.Purpose");
}
public string Protect(string input) => _protector.Protect(input);
public string Unprotect(string protectedData) => _protector.Unprotect(protectedData);
}
4. 安全陷阱与防御
AES加密的致命错误
// 错误示例:使用ECB模式(不安全的!)
aes.Mode = CipherMode.ECB; // ❌ 相同输入产生相同输出,易被分析
// 正确做法:使用CBC或GCM模式
aes.Mode = CipherMode.CBC; // ✅ 需要随机IV
aes.GenerateIV(); // 每次加密生成新IV
密钥存储的典型错误
// 错误:硬编码密钥
byte[] key = Encoding.ASCII.GetBytes("ThisIsASecretKey123"); // ❌
// 正确:从安全存储获取
byte[] key = Convert.FromBase64String(
ConfigurationManager.AppSettings["EncryptionKey"] // ✅ 使用密钥库
);
5. 企业级解决方案
Azure Key Vault集成
using Azure.Security.KeyVault.Keys.Cryptography;
public async Task<string> EncryptWithAzureKeyVault(string text)
{
var credential = new DefaultAzureCredential();
var cryptoClient = new CryptographyClient(
new Uri("https://your-vault.vault.azure.net/keys/key-name/"),
credential
);
byte[] data = Encoding.UTF8.GetBytes(text);
EncryptResult result = await cryptoClient.EncryptAsync(EncryptionAlgorithm.RsaOaep, data);
return Convert.ToBase64String(result.Ciphertext);
}
6. 常见问题解答
Q1:应该选择对称还是非对称加密?
-
对称加密(AES):速度快,适合大数据量(如文件加密)
-
非对称加密(RSA):适合密钥交换或小数据加密
Q2:如何安全存储加密密钥?
-
开发环境:使用
dotnet user-secrets
-
生产环境:Azure Key Vault/AWS KMS/HSM
Q3:加密后的数据如何比较?
-
使用固定时间比较函数防止时序攻击:
// 正确方式 CryptographicOperations.FixedTimeEquals(hash1, hash2); // 错误方式 if (hash1 == hash2) { ... } // ❌ 可能泄露比较时间
Q4:如何迁移旧加密系统?
-
解密旧数据(使用旧密钥)
-
用新算法/密钥重新加密
-
销毁旧密钥(物理安全)