目录
由于接触的新项目是用户认证相关,把密码相关的知识重新复习并记录下来
基础概念
什么是加密?
加密是将明文信息转换为密文的过程,目的是保护数据的机密性、完整性和真实性。
加密的主要类型
- 对称加密:使用相同的密钥进行加密和解密
- 非对称加密:使用一对密钥(公钥和私钥)进行加密和解密
- 哈希算法:将任意长度的数据映射为固定长度的数据,不可逆
- 密码哈希专用算法:专门为密码存储设计的哈希算法,通常包含盐值和多次迭代
加密的主要目标
- 机密性:确保只有授权用户才能访问信息
- 完整性:确保数据在传输过程中未被篡改
- 认证:验证通信双方的身份
- 不可否认性:防止发送方否认曾发送过信息
对称加密算法
对称加密使用相同的密钥进行加密和解密,速度快但密钥分发是个挑战。
常见对称加密算法
AES (Advanced Encryption Standard)
- 密钥长度:128位、192位或256位
- 特点:目前最安全和广泛使用的对称加密算法
- 应用场景:文件加密、数据库字段加密、通信加密
DES (Data Encryption Standard)
- 密钥长度:56位
- 特点:已被破解,不再安全
- 应用场景:历史遗留系统
3DES (Triple DES)
- 密钥长度:168位(实际安全强度约112位)
- 特点:对DES的增强版,但速度较慢
- 应用场景:金融系统中仍有使用
Blowfish
- 密钥长度:32-448位
- 特点:快速、免费、无专利限制
- 应用场景:文件和磁盘加密
非对称加密算法
非对称加密使用一对密钥:公钥用于加密,私钥用于解密。解决了密钥分发问题,但计算开销较大。
常见非对称加密算法
RSA
- 密钥长度:通常为2048位或4096位
- 特点:最广泛使用的非对称加密算法
- 应用场景:数字签名、密钥交换、HTTPS通信
ECC (Elliptic Curve Cryptography)
- 密钥长度:256位ECC提供与3072位RSA相当的安全性
- 特点:更短的密钥长度,更高的效率
- 应用场景:移动设备、IoT设备、TLS
DSA (Digital Signature Algorithm)
- 特点:专为数字签名设计
- 应用场景:数字签名验证
哈希算法
哈希算法将任意长度的输入转换为固定长度的输出,且不可逆。
常见哈希算法
MD5 (Message Digest Algorithm 5)
- 输出长度:128位(16字节)
- 特点:已被破解,存在碰撞风险
- 应用场景:文件完整性校验(不安全),不应用于安全场景
SHA-1 (Secure Hash Algorithm 1)
- 输出长度:160位(20字节)
- 特点:已被证明不安全
- 应用场景:不应再用于安全场景
SHA-256 (SHA-2家族)
- 输出长度:256位(32字节)
- 特点:目前广泛使用的安全哈希算法
- 应用场景:数字签名、区块链、文件完整性验证
SHA-3
- 输出长度:可变(224, 256, 384, 512位)
- 特点:最新的哈希标准,结构与SHA-2完全不同
- 应用场景:高安全性要求的系统
密码哈希专用算法
这类算法专为存储用户密码设计,具有计算开销大、抗暴力破解的特性。
常见密码哈希算法
BCrypt
- 特点:自带盐值,计算开销可调整
- 应用场景:用户密码存储,Spring Security默认使用
PBKDF2 (Password-Based Key Derivation Function 2)
- 特点:可配置迭代次数,使用HMAC作为伪随机函数
- 应用场景:密码存储、密钥派生
Argon2
- 特点:2015年密码哈希竞赛冠军,抗ASIC攻击
- 应用场景:高安全性密码存储
SCrypt
- 特点:内存密集型,抗硬件攻击
- 应用场景:密码存储、加密货币
SpringBoot中的加密最佳实践
密码存储 - BCrypt
Spring Security默认使用BCrypt进行密码加密,这是处理用户密码的最佳实践。
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
// 可以指定强度因子,默认为10
return new BCryptPasswordEncoder(12);
}
}
使用示例:
@Service
public class UserService {
private final PasswordEncoder passwordEncoder;
private final UserRepository userRepository;
public UserService(PasswordEncoder passwordEncoder, UserRepository userRepository) {
this.passwordEncoder = passwordEncoder;
this.userRepository = userRepository;
}
public void registerUser(String username, String rawPassword) {
// 加密密码
String encodedPassword = passwordEncoder.encode(rawPassword);
User user = new User();
user.setUsername(username);
user.setPassword(encodedPassword);
userRepository.save(user);
}
public boolean verifyPassword(String rawPassword, String encodedPassword) {
// 验证密码
return passwordEncoder.matches(rawPassword, encodedPassword);
}
}
敏感数据加密 - AES
对称加密适合加密数据库中的敏感字段,如信用卡号、身份证号等。
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class AESEncryptionService {
@Value("${app.encryption.secret-key}")
private String secretKey; // 应该是16、24或32字节长度的密钥
public String encrypt(String data) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encryptedBytes = cipher.doFinal(data.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public String decrypt(String encryptedData) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] decodedBytes = Base64.getDecoder().decode(encryptedData);
byte[] decryptedBytes = cipher.doFinal(decodedBytes);
return new String(decryptedBytes);
}
}
非对称加密 - RSA
RSA适合加密API密钥、JWT签名等场景。
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import org.springframework.stereotype.Component;
@Component
public class RSAEncryptionService {
private final PrivateKey privateKey;
private final PublicKey publicKey;
public RSAEncryptionService() throws Exception {
// 在实际应用中,应从配置文件或安全存储中加载密钥
KeyPair keyPair = generateKeyPair();
this.privateKey = keyPair.getPrivate();
this.publicKey = keyPair.getPublic();
}
// 生成RSA密钥对
private KeyPair generateKeyPair() throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048); // 推荐使用2048位或更高
return generator.generateKeyPair();
}
// 使用公钥加密
public String encrypt(String data) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(data.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
// 使用私钥解密
public String decrypt(String encryptedData) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decodedBytes = Base64.getDecoder().decode(encryptedData);
byte[] decryptedBytes = cipher.doFinal(decodedBytes);
return new String(decryptedBytes);
}
// 获取Base64编码的公钥字符串
public String getPublicKeyString() {
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
}
// 从Base64字符串加载公钥
public static PublicKey loadPublicKey(String publicKeyBase64) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(publicKeyBase64);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(spec);
}
// 从Base64字符串加载私钥
public static PrivateKey loadPrivateKey(String privateKeyBase64) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(privateKeyBase64);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(spec);
}
}
在实际应用中,应该将RSA密钥对安全存储:
扫描二维码关注公众号,回复:
17573050 查看本文章

# application.properties 或 application.yml
app.encryption.rsa.private-key=MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBK...
app.encryption.rsa.public-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMI...
然后在服务中加载:
@Component
public class RSAEncryptionService {
private final PrivateKey privateKey;
private final PublicKey publicKey;
public RSAEncryptionService(
@Value("${app.encryption.rsa.private-key}") String privateKeyBase64,
@Value("${app.encryption.rsa.public-key}") String publicKeyBase64) throws Exception {
this.privateKey = loadPrivateKey(privateKeyBase64);
this.publicKey = loadPublicKey(publicKeyBase64);
}
// 其他方法...
}
数据摘要 - SHA-256
用于生成数据指纹,验证数据完整性。
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
import org.springframework.stereotype.Component;
@Component
public class HashService {
public String sha256Hash(String data) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = digest.digest(data.getBytes(StandardCharsets.UTF_8));
// 转换为十六进制字符串
StringBuilder hexString = new StringBuilder();
for (byte b : hashBytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
// 验证数据完整性
public boolean verifyIntegrity(String data, String expectedHash) throws Exception {
String actualHash = sha256Hash(data);
return actualHash.equals(expectedHash);
}
}
不同加密算法对比
算法类型 | 算法名称 | 安全性 | 性能 | 密钥长度 | 主要用途 |
---|---|---|---|---|---|
对称加密 | AES | 高 | 快 | 128/192/256位 | 数据加密 |
DES | 低(已破解) | 中 | 56位 | 不推荐使用 | |
3DES | 中 | 慢 | 168位 | 遗留系统 | |
非对称加密 | RSA | 高 | 慢 | 2048/4096位 | 密钥交换、数字签名 |
ECC | 高 | 中 | 256位 | 移动设备加密 | |
哈希算法 | MD5 | 低(已破解) | 快 | 输出128位 | 不用于安全场景 |
SHA-1 | 低(已破解) | 快 | 输出160位 | 不用于安全场景 | |
SHA-256 | 高 | 中 | 输出256位 | 数据完整性验证 | |
密码哈希 | BCrypt | 高 | 可调整 | N/A | 密码存储 |
PBKDF2 | 高 | 可调整 | N/A | 密码存储、密钥派生 | |
Argon2 | 非常高 | 可调整 | N/A | 高安全性密码存储 |
加密结果示例
以下是使用不同算法加密字符串"mypassword"的结果示例:
对称加密 (AES-256)
明文: mypassword
密钥: ThisIsA32ByteSecretKeyForAES256!
加密结果: 9HzYiT7qO5Hk3zGJ4Y8D/w==
非对称加密 (RSA-2048)
明文: mypassword
加密结果:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqLmkfX6NpFLQxgWOsUHM
7XCjl0XgPKiLs+AY8i9qvF0tVwuWZ0HiTVMuZ4/3L8P/xzJ3CpEkwcYx0xUH9BIH
jIYJQqQmfIKGJL+Uc8jBxQximBcKMIHZG9LKxPZxY0XPLT+QVt5v5lkGkJ4Vn4oU
5QWzO+lQ5gVZ5n8YHCHxT4fQ3Jn+5ZVgB8XxhQl0YZ/axBz/jfK/iCqHHyiHl8Iq
8qgzODcnTrGYJQH1QvhXdPiQj5YNnbp5eQ==
哈希算法
MD5 (不安全,仅作示例)
明文: mypassword
哈希结果: 34819d7beeabb9260a5c854bc85b3e44
SHA-1 (不安全,仅作示例)
明文: mypassword
哈希结果: 91dfd9ddb4198affc5c194cd8ce6d338fde470e2
SHA-256
明文: mypassword
哈希结果: 89e01536ac207279409d4de1e5253e01f4a1769e696db0d6062ca9b8f56767c8
密码哈希专用算法
BCrypt
明文: mypassword
哈希结果(强度因子10): $2a$10$dPXHNHdNgZW6OIZQe0ypzOm2NXjcPQkjACYIkbLCGkqPPZIpwM3IS
哈希结果(强度因子12): $2a$12$1InE3VZmMr5pQdQ.ECxVXOQqrVGUz.yJjFZm5UNJj2L5ej8ZyMEHy
PBKDF2 (使用HMAC-SHA256,10000次迭代)
明文: mypassword
盐值: randomsalt
哈希结果: 7987c3e6f26a0d63fbbf4a7cc7d1565bd9af1dc6a068e2a3c6cf2114ea1e7dda
Argon2id
明文: mypassword
哈希结果: $argon2id$v=19$m=65536,t=3,p=4$c2FsdHNhbHRzYWx0$3mRCDpZzXRbG4LYsN0bPHnBZnZ7lL1oGJl6j9xn8ukw
注意:每次使用BCrypt、PBKDF2和Argon2等算法生成的哈希值都会不同,因为它们会自动生成随机盐值。
选择建议
在选择加密算法时,需要根据具体应用场景和安全需求做出权衡。以下是针对不同场景的建议:
用户密码存储
- 最佳选择:BCrypt 或 Argon2
- 理由:这些算法专为密码存储设计,内置盐值和工作因子调整,能有效抵抗彩虹表攻击和暴力破解
- 实现方式:在 SpringBoot 中使用
PasswordEncoder
接口,默认实现为BCryptPasswordEncoder
敏感数据加密(数据库字段)
- 最佳选择:AES-256 (GCM模式)
- 理由:提供高安全性和良好性能的平衡,GCM模式提供认证加密
- 注意事项:密钥管理至关重要,考虑使用密钥管理服务
API通信安全
- 最佳选择:HTTPS + JWT (使用RSA或ECDSA签名)
- 理由:HTTPS保护传输层,JWT保护应用层,签名确保数据完整性和来源认证
- 实现方式:使用 SpringBoot 的
spring-boot-starter-security
和jjwt
库
数据完整性验证
- 最佳选择:SHA-256 或 SHA-3
- 理由:提供强大的抗碰撞性,生成唯一的数据指纹
- 应用场景:文件校验、数据签名、区块链
移动和资源受限设备
- 最佳选择:ECC (椭圆曲线加密)
- 理由:比RSA使用更短的密钥提供同等安全性,计算开销更小
- 应用场景:IoT设备、移动应用
实际应用建议
- 分层加密策略:对不同敏感级别的数据采用不同的加密方法
- 密钥轮换:定期更换加密密钥,减少密钥泄露风险
- 安全存储密钥:使用密钥管理服务或硬件安全模块(HSM)存储密钥
- 加密与认证结合:单纯的加密不足以保证安全,应结合认证机制
- 保持更新:关注加密算法的安全动态,及时更新到更安全的算法
最后,记住安全是一个整体过程,加密只是其中一环。完整的安全策略还应包括访问控制、安全编码实践、定期安全审计等多方面措施。