敏感数据加密存储实战
一、敏感数据概述
敏感数据是指一旦泄露可能造成安全风险的信息,需要采取加密措施保护。
1.1 敏感数据分类
| 类别 | 示例 | 加密要求 |
|---|---|---|
| 个人身份 | 身份证号、手机号 | 必须加密 |
| 金融信息 | 银行卡号、密码 | 必须加密 |
| 业务敏感 | 用户密码、Token | 必须加密 |
| 配置信息 | 密钥、证书 | 必须加密 |
1.2 加密策略
┌─────────────────────────────────────────────────────────────┐
│ 敏感数据加密流程 │
├─────────────────────────────────────────────────────────────┤
│ 数据输入 ──▶ 明文 ──▶ 加密 ──▶ 密文 ──▶ 存储 │
│ │ │ │
│ │ ▼ │
│ └──────────────── 解密 ◀───────────┘ │
│ │ │
│ ▼ │
│ 明文输出 │
└─────────────────────────────────────────────────────────────┘
二、加密算法选择
2.1 对称加密
java
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class SymmetricEncryption {
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";
public static String encrypt(String plainText, String key) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encrypted = cipher.doFinal(plainText.getBytes());
return Base64.getEncoder().encodeToString(encrypted);
}
public static String decrypt(String cipherText, String key) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(cipherText));
return new String(decrypted);
}
}
2.2 非对称加密
java
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import javax.crypto.Cipher;
public class AsymmetricEncryption {
private static final String ALGORITHM = "RSA";
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM);
generator.initialize(2048);
return generator.generateKeyPair();
}
public static byte[] encrypt(byte[] data, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
public static byte[] decrypt(byte[] data, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
}
2.3 混合加密方案
java
public class HybridEncryption {
public static EncryptedData encrypt(String plainText, PublicKey publicKey) throws Exception {
// 生成对称密钥
String symmetricKey = generateRandomKey(16);
// 使用对称密钥加密数据
String encryptedData = SymmetricEncryption.encrypt(plainText, symmetricKey);
// 使用公钥加密对称密钥
byte[] encryptedKey = AsymmetricEncryption.encrypt(symmetricKey.getBytes(), publicKey);
return new EncryptedData(encryptedData, encryptedKey);
}
public static String decrypt(EncryptedData encryptedData, PrivateKey privateKey) throws Exception {
// 使用私钥解密对称密钥
byte[] symmetricKey = AsymmetricEncryption.decrypt(encryptedData.getEncryptedKey(), privateKey);
// 使用对称密钥解密数据
return SymmetricEncryption.decrypt(encryptedData.getEncryptedData(), new String(symmetricKey));
}
}
三、数据库加密
3.1 字段级加密
java
@Entity
public class User {
@Id
private Long id;
@Column(name = "username")
private String username;
@Column(name = "password")
@Convert(converter = EncryptedStringConverter.class)
private String password;
@Column(name = "email")
@Convert(converter = EncryptedStringConverter.class)
private String email;
}
@Converter
public class EncryptedStringConverter implements AttributeConverter<String, String> {
@Override
public String convertToDatabaseColumn(String attribute) {
if (attribute == null) return null;
try {
return SymmetricEncryption.encrypt(attribute, getSecretKey());
} catch (Exception e) {
throw new RuntimeException("Encryption failed", e);
}
}
@Override
public String convertToEntityAttribute(String dbData) {
if (dbData == null) return null;
try {
return SymmetricEncryption.decrypt(dbData, getSecretKey());
} catch (Exception e) {
throw new RuntimeException("Decryption failed", e);
}
}
}
3.2 数据库透明加密
yaml
# MySQL TDE配置
innodb_encrypt_tables = ON
innodb_encryption_threads = 4
innodb_encrypt_log = ON
innodb_encryption_rotate_key_age = 1
3.3 密码哈希
java
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class PasswordUtil {
private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
public static String hashPassword(String rawPassword) {
return encoder.encode(rawPassword);
}
public static boolean verifyPassword(String rawPassword, String encodedPassword) {
return encoder.matches(rawPassword, encodedPassword);
}
}
四、密钥管理
4.1 密钥存储
java
@Configuration
public class KeyManagementConfig {
@Bean
public String secretKey() {
// 从密钥管理服务获取密钥
return keyManagementService.getSecret("app-encryption-key");
}
}
4.2 密钥轮换
java
public class KeyRotationService {
public void rotateKey() {
// 生成新密钥
String newKey = generateRandomKey(32);
// 更新密钥存储
keyManagementService.updateSecret("app-encryption-key", newKey);
// 重新加密所有数据(可选)
reEncryptAllData(newKey);
}
}
4.3 使用密钥管理服务
java
public class CloudKeyManager {
private final AWSKMS kmsClient;
public byte[] encryptWithKms(byte[] data) {
EncryptRequest request = EncryptRequest.builder()
.keyId("arn:aws:kms:us-east-1:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab")
.plaintext(data)
.build();
return kmsClient.encrypt(request).ciphertextBlob().array();
}
}
五、安全传输
5.1 HTTPS配置
yaml
server:
port: 443
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-password: ${KEY_STORE_PASSWORD}
key-store-type: PKCS12
key-alias: myapp
5.2 TLS证书管理
bash
# 使用Let's Encrypt获取证书
certbot certonly --webroot -w /var/www/html -d example.com
# 自动续期
certbot renew --dry-run
六、文件加密
6.1 文件加密工具
java
public class FileEncryption {
public static void encryptFile(String inputPath, String outputPath, String key) throws Exception {
try (FileInputStream fis = new FileInputStream(inputPath);
FileOutputStream fos = new FileOutputStream(outputPath);
CipherOutputStream cos = new CipherOutputStream(fos, getCipher(Cipher.ENCRYPT_MODE, key))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
cos.write(buffer, 0, bytesRead);
}
}
}
public static void decryptFile(String inputPath, String outputPath, String key) throws Exception {
try (FileInputStream fis = new FileInputStream(inputPath);
CipherInputStream cis = new CipherInputStream(fis, getCipher(Cipher.DECRYPT_MODE, key));
FileOutputStream fos = new FileOutputStream(outputPath)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = cis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
}
}
七、脱敏处理
7.1 数据脱敏
java
public class DataMaskingUtil {
public static String maskPhone(String phone) {
if (phone == null || phone.length() < 11) return phone;
return phone.substring(0, 3) + "****" + phone.substring(7);
}
public static String maskIdCard(String idCard) {
if (idCard == null || idCard.length() < 18) return idCard;
return idCard.substring(0, 4) + "**********" + idCard.substring(14);
}
public static String maskEmail(String email) {
if (email == null || !email.contains("@")) return email;
String[] parts = email.split("@");
if (parts[0].length() <= 2) return email;
return parts[0].substring(0, 2) + "****@" + parts[1];
}
}
7.2 日志脱敏
java
@Aspect
public class LogMaskingAspect {
@Around("execution(* com.example..*.*(..))")
public Object maskLog(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
// 对日志输出进行脱敏处理
maskSensitiveData(result);
return result;
}
}
八、安全审计
8.1 访问日志
java
@Configuration
public class AuditLogConfig {
@Bean
public Filter auditFilter() {
return (request, response, chain) -> {
HttpServletRequest httpRequest = (HttpServletRequest) request;
AuditLog log = AuditLog.builder()
.userId(getUserId(httpRequest))
.action(httpRequest.getRequestURI())
.method(httpRequest.getMethod())
.ip(getClientIp(httpRequest))
.timestamp(LocalDateTime.now())
.build();
auditLogRepository.save(log);
chain.doFilter(request, response);
};
}
}
8.2 数据变更审计
java
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public abstract class Auditable {
@CreatedBy
@Column(name = "created_by")
private String createdBy;
@CreatedDate
@Column(name = "created_date")
private LocalDateTime createdDate;
@LastModifiedBy
@Column(name = "last_modified_by")
private String lastModifiedBy;
@LastModifiedDate
@Column(name = "last_modified_date")
private LocalDateTime lastModifiedDate;
}
九、合规要求
9.1 GDPR合规
java
public class GdprService {
public void rightToBeForgotten(Long userId) {
// 删除用户所有数据
userRepository.deleteById(userId);
// 删除相关日志
auditLogRepository.deleteByUserId(userId);
// 删除缓存数据
redisTemplate.delete("user:" + userId);
}
public void dataPortability(Long userId) {
// 导出用户所有数据
UserData data = exportUserData(userId);
// 提供下载
sendDataExport(data);
}
}
9.2 数据分类分级
| 级别 | 描述 | 保护措施 |
|---|---|---|
| L1 | 公开数据 | 无需加密 |
| L2 | 内部数据 | 访问控制 |
| L3 | 敏感数据 | 加密存储 |
| L4 | 机密数据 | 加密+访问审计 |
十、总结
敏感数据加密存储需要综合考虑:
- 选择合适算法:对称加密用于数据,非对称加密用于密钥
- 密钥安全管理:使用专业密钥管理服务
- 多层防护:传输加密、存储加密、访问控制
- 合规审计:满足GDPR等法规要求
- 定期轮换:定期更换密钥,降低泄露风险
通过系统化的加密策略,可以有效保护敏感数据的安全。