敏感数据加密存储实战
敏感数据加密存储实战
一、敏感数据概述
敏感数据是指一旦泄露可能造成安全风险的信息,需要采取加密措施保护。
1.1 敏感数据分类
| 类别 | 示例 | 加密要求 |
|---|---|---|
| 个人身份 | 身份证号、手机号 | 必须加密 |
| 金融信息 | 银行卡号、密码 | 必须加密 |
| 业务敏感 | 用户密码、Token | 必须加密 |
| 配置信息 | 密钥、证书 | 必须加密 |
1.2 加密策略
┌─────────────────────────────────────────────────────────────┐ │ 敏感数据加密流程 │ ├─────────────────────────────────────────────────────────────┤ │ 数据输入 ──▶ 明文 ──▶ 加密 ──▶ 密文 ──▶ 存储 │ │ │ │ │ │ │ ▼ │ │ └──────────────── 解密 ◀───────────┘ │ │ │ │ │ ▼ │ │ 明文输出 │ └─────────────────────────────────────────────────────────────┘二、加密算法选择
2.1 对称加密
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 非对称加密
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 混合加密方案
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 字段级加密
@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 数据库透明加密
# MySQL TDE配置 innodb_encrypt_tables = ON innodb_encryption_threads = 4 innodb_encrypt_log = ON innodb_encryption_rotate_key_age = 13.3 密码哈希
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 密钥存储
@Configuration public class KeyManagementConfig { @Bean public String secretKey() { // 从密钥管理服务获取密钥 return keyManagementService.getSecret("app-encryption-key"); } }4.2 密钥轮换
public class KeyRotationService { public void rotateKey() { // 生成新密钥 String newKey = generateRandomKey(32); // 更新密钥存储 keyManagementService.updateSecret("app-encryption-key", newKey); // 重新加密所有数据(可选) reEncryptAllData(newKey); } }4.3 使用密钥管理服务
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配置
server: port: 443 ssl: enabled: true key-store: classpath:keystore.p12 key-store-password: ${KEY_STORE_PASSWORD} key-store-type: PKCS12 key-alias: myapp5.2 TLS证书管理
# 使用Let's Encrypt获取证书 certbot certonly --webroot -w /var/www/html -d example.com # 自动续期 certbot renew --dry-run六、文件加密
6.1 文件加密工具
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 数据脱敏
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 日志脱敏
@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 访问日志
@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 数据变更审计
@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合规
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等法规要求
- 定期轮换:定期更换密钥,降低泄露风险
通过系统化的加密策略,可以有效保护敏感数据的安全。
