数据加密与密钥管理

一、加密方案选择

1.1 AES-GCM 模式

推荐用于用户敏感数据加密(如手机号、收货地址)

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
 
public class AesGcmEncryption {
    private static final int GCM_IV_LENGTH = 12;  // 推荐12字节
    private static final int GCM_TAG_LENGTH = 128;  // 认证标签长度
    
    // 加密
    public byte[] encrypt(byte[] data, byte[] key) throws Exception {
        byte[] iv = new byte[GCM_IV_LENGTH];
        SecureRandom random = new SecureRandom();
        random.nextBytes(iv);
        
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), spec);
        
        byte[] encrypted = cipher.doFinal(data);
        
        // IV + 密文
        byte[] result = new byte[iv.length + encrypted.length];
        System.arraycopy(iv, 0, result, 0, iv.length);
        System.arraycopy(encrypted, 0, result, iv.length, encrypted.length);
        return result;
    }
    
    // 解密
    public byte[] decrypt(byte[] encryptedData, byte[] key) throws Exception {
        byte[] iv = new byte[GCM_IV_LENGTH];
        byte[] ciphertext = new byte[encryptedData.length - GCM_IV_LENGTH];
        System.arraycopy(encryptedData, 0, iv, 0, GCM_IV_LENGTH);
        System.arraycopy(encryptedData, GCM_IV_LENGTH, ciphertext, 0, ciphertext.length);
        
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), spec);
        
        return cipher.doFinal(ciphertext);
    }
}

1.2 AES-GCM 优势

特性说明
加密+认证自带消息认证码(MAC),防止篡改
安全性高防止填充Oracle攻击,比CBC模式更安全
效率高并行加密,性能优于其他模式

1.3 IV(初始化向量)要点

  • 唯一性:每个加密操作必须使用唯一IV
  • 随机性:使用SecureRandom生成
  • 存储:IV与密文一起存储(不需要保密)
  • 长度:推荐12字节(96位),兼顾安全性和性能

二、密钥管理策略

2.1 密钥分层架构

密钥层次结构:
┌─────────────────────────────────┐
│       主密钥(KEK - Key Encryption Key)      │
│   存储在KMS/HSM中,保护DEK                     │
└─────────────────────────────────┘
                  ↓ 加密
┌─────────────────────────────────┐
│       数据加密密钥(DEK - Data Encryption Key)│
│   加密实际业务数据,由KEK保护                   │
└─────────────────────────────────┘
                  ↓ 加密
┌─────────────────────────────────┐
│           用户敏感数据                          │
│   如手机号、收货地址、身份证号等                │
└─────────────────────────────────┘

2.2 密钥管理服务(KMS)

推荐方案:

服务类型适用场景
云厂商KMSAWS KMS、阿里云KMS、腾讯云KMS
开源方案HashiCorp Vault
硬件安全模块HSM设备,最高安全级别

KMS核心功能:

  • 密钥生成与存储
  • 密钥轮换
  • 访问控制(IAM/RBAC)
  • 操作审计日志

2.3 密钥缓存策略

public class KeyCacheManager {
    private final Map<String, byte[]> keyCache = new ConcurrentHashMap<>();
    private final KmsClient kmsClient;
    private static final long CACHE_TTL = 30 * 60 * 1000;  // 30分钟
    
    public byte[] getKey(String keyId) {
        String cacheKey = keyId + "_" + getCurrentKeyVersion(keyId);
        
        return keyCache.computeIfAbsent(cacheKey, k -> {
            try {
                return kmsClient.decryptKey(keyId);
            } catch (Exception e) {
                throw new RuntimeException("Failed to get key from KMS", e);
            }
        });
    }
    
    // 定期清理过期缓存
    @Scheduled(fixedRate = 60000)
    public void cleanupCache() {
        keyCache.entrySet().removeIf(entry -> 
            isExpired(entry.getKey())
        );
    }
}

三、密钥轮换策略

3.1 双密钥期策略

密钥轮换流程:
时间线 →
┌──────────┬──────────┬──────────┐
│ 密钥v1   │ 密钥v1/v2 │ 密钥v2   │
│ 活跃期   │ 过渡期    │ 活跃期   │
└──────────┴──────────┴──────────┘
     │           │           │
     │ 新数据    │ 新数据    │ 新数据
     │ 用v1加密  │ 用v2加密  │ 用v2加密
     │           │ 旧数据    │ 旧数据
     │           │ 读时升级  │ 已全部升级
     └───────────┴───────────┘

3.2 懒重加密模式

优点:避免全量重加密的性能开销

public class LazyReEncryptionService {
    public String decryptAndUpgradeIfNeeded(String encryptedData, String currentKeyId) {
        // 1. 获取加密时使用的密钥版本
        String encryptionKeyId = extractKeyIdFromData(encryptedData);
        
        // 2. 解密数据
        byte[] key = keyManager.getKey(encryptionKeyId);
        String plaintext = decrypt(encryptedData, key);
        
        // 3. 如果不是最新密钥,进行升级
        if (!encryptionKeyId.equals(currentKeyId)) {
            byte[] newKey = keyManager.getKey(currentKeyId);
            String reEncrypted = encrypt(plaintext, newKey);
            // 异步更新数据库
            asyncUpdateData(encryptedData, reEncrypted);
        }
        
        return plaintext;
    }
}

3.3 轮换周期建议

密钥类型建议轮换周期
主密钥(KEK)90-180天
数据密钥(DEK)30-90天
临时密钥会话结束即销毁

四、国密算法支持(SM4)

国内合规要求时使用

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;
 
public class SM4Encryption {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }
    
    public byte[] encryptSM4Gcm(byte[] data, byte[] key, byte[] iv) throws Exception {
        Cipher cipher = Cipher.getInstance("SM4/GCM/NoPadding", "BC");
        GCMParameterSpec spec = new GCMParameterSpec(128, iv);
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "SM4"), spec);
        return cipher.doFinal(data);
    }
}

五、面试要点

5.1 核心概念

概念说明
对称加密加密解密使用相同密钥(AES、SM4)
非对称加密加密解密使用不同密钥(RSA)
KEK密钥加密密钥,保护DEK
DEK数据加密密钥,加密业务数据
IV/Nonce初始化向量,保证加密唯一性

5.2 安全要点

  • 禁止硬编码密钥:密钥必须存储在KMS或配置中心
  • 禁止重复使用IV:每个加密操作必须使用新IV
  • 加密前后校验:防止数据篡改
  • 密钥访问审计:记录所有密钥操作

5.3 常见面试问题

Q:为什么推荐AES-GCM而不是CBC?

A:GCM模式自带认证功能,能检测数据是否被篡改,且避免了CBC模式的填充Oracle攻击风险。

Q:密钥为什么要分层管理?

A:分层管理降低风险:即使DEK泄露,攻击者仍无法获取KEK,从而无法解密其他数据。

Q:如何处理密钥轮换期间的数据访问?

A:采用双密钥期策略,保留当前和上一代密钥,新数据用新密钥加密,旧数据在访问时懒升级。


参考链接