跳到主要内容

加密与数据保护

数据加密是保护敏感信息的核心技术。本章将介绍如何在 Web 应用中正确实施加密措施,保护数据的机密性和完整性。

为什么要加密?

在以下场景中需要加密:

  1. 数据传输 - 防止网络传输过程中的窃听
  2. 数据存储 - 防止数据库泄露后的数据暴露
  3. 密码存储 - 保护用户密码不被泄露
  4. 敏感信息 - 保护身份证号、银行卡号等

加密算法分类

对称加密

加密和解密使用相同的密钥:

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;

public class AesEncryption {
private static final int GCM_IV_LENGTH = 12;
private static final int GCM_TAG_LENGTH = 128;

// 使用 AES-GCM 加密
public byte[] encrypt(byte[] plaintext, byte[] key) throws Exception {
// 生成随机 IV
byte[] iv = new byte[GCM_IV_LENGTH];
new SecureRandom().nextBytes(iv);

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);

cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);
byte[] ciphertext = cipher.doFinal(plaintext);

// 组合 IV 和密文
byte[] result = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(ciphertext, 0, result, iv.length, ciphertext.length);

return result;
}

// 解密
public byte[] decrypt(byte[] encrypted, byte[] key) throws Exception {
// 分离 IV 和密文
byte[] iv = new byte[GCM_IV_LENGTH];
byte[] ciphertext = new byte[encrypted.length - GCM_IV_LENGTH];
System.arraycopy(encrypted, 0, iv, 0, iv.length);
System.arraycopy(encrypted, iv.length, ciphertext, 0, ciphertext.length);

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);

cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);
return cipher.doFinal(ciphertext);
}
}

非对称加密

使用公钥和私钥对:

import java.security.KeyPair;
import java.security.KeyPairGenerator;

public class RsaEncryption {

// 生成密钥对
public KeyPair generateKeyPair() throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048);
return generator.generateKeyPair();
}

// 使用公钥加密
public byte[] encrypt(byte[] plaintext, java.security.PublicKey publicKey)
throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(plaintext);
}

// 使用私钥解密
public byte[] decrypt(byte[] ciphertext, java.security.PrivateKey privateKey)
throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(ciphertext);
}
}

哈希算法

用于密码存储和数据完整性验证:

import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Base64;

public class HashExample {

// 不推荐:简单哈希
public String simpleHash(String input) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(input.getBytes());
return Base64.getEncoder().encodeToString(hash);
}

// 推荐:使用 BCrypt 进行密码哈希
// BCrypt 自动处理盐值,成本因子可调整
public String hashPassword(String password) {
// Spring Security BCrypt
// 见认证章节
}
}

传输层安全(TLS)

HTTPS 配置

// Spring Boot 配置
server:
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-password: changeit
key-store-type: PKCS12
key-alias: myapp

Tomcat HTTPS 配置

<!-- server.xml -->
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
keystoreFile="/path/to/keystore.jks"
keystorePass="changeit"
clientAuth="false" sslProtocol="TLS" />

强制使用 HTTPS

// Spring Security 配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requiresChannel()
.antMatchers("/api/**").requiresSecure()
.antMatchers("/login").requiresSecure()
.anyRequest().requiresSecure();
}
}

HSTS 配置

// 添加 HSTS 响应头
@Component
public class HstsFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) {
response.setHeader("Strict-Transport-Security",
"max-age=31536000; includeSubDomains");
chain.doFilter(request, response);
}
}

数据脱敏

常用脱敏规则

public class DataMasking {

// 手机号脱敏:138****5678
public static String maskPhone(String phone) {
if (phone == null || phone.length() < 11) return phone;
return phone.substring(0, 3) + "****" + phone.substring(7);
}

// 身份证脱敏:310***********1234
public static String maskIdCard(String idCard) {
if (idCard == null || idCard.length() < 18) return idCard;
return idCard.substring(0, 3) + "***********" + idCard.substring(14);
}

// 银行卡脱敏:6222 **** **** 1234
public static String maskBankCard(String card) {
if (card == null || card.length() < 16) return card;
return card.substring(0, 4) + " **** **** " + card.substring(12);
}

// 邮箱脱敏:a***@example.com
public static String maskEmail(String email) {
if (email == null || !email.contains("@")) return email;
String[] parts = email.split("@");
if (parts[0].length() <= 2) return "**@" + parts[1];
return parts[0].charAt(0) + "***" + parts[0].charAt(parts[0].length()-1)
+ "@" + parts[1];
}
}

密钥管理

密钥存储

// 不安全:密钥硬编码
private static final String ENCRYPTION_KEY = "my-secret-key";

// 安全:使用配置中心或密钥管理服务
@Configuration
public class KeyConfig {
@Value("${encryption.key}")
private String encryptionKey;
}

环境变量

# 使用环境变量
export ENCRYPTION_KEY="your-secure-key-here"

密钥管理服务

// AWS KMS 示例
import software.amazon.awssdk.services.kms.KmsClient;
import software.amazon.awssdk.services.kms.model.DecryptRequest;

public class KmsService {
private KmsClient kmsClient = KmsClient.builder().build();

public String decrypt(byte[] encryptedKey) {
DecryptRequest request = DecryptRequest.builder()
.ciphertextBlob(encryptedKey)
.build();

return kmsClient.decrypt(request).plaintext().utf8();
}
}

敏感数据保护

识别敏感数据

// 数据分类注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Sensitive {
SensitiveType value() default SensitiveType.GENERAL;
}

enum SensitiveType {
PASSWORD, // 密码
PHONE, // 手机号
ID_CARD, // 身份证
BANK_CARD, // 银行卡
EMAIL, // 邮箱
GENERAL // 一般敏感
}

// 使用
public class User {
@Sensitive(SensitiveType.GENERAL)
private String name;

@Sensitive(SensitiveType.PHONE)
private String phone;

@Sensitive(SensitiveType.ID_CARD)
private String idCard;
}

数据库加密

// 字段级加密
@Entity
public class User {
@Column(name = "phone_encrypted")
@Convert(converter = PhoneEncryptConverter.class)
private String phone;
}

// 加密转换器
@Converter
public class PhoneEncryptConverter implements AttributeConverter<String, String> {
private final AesEncryption encryption = new AesEncryption();
private final byte[] key = getEncryptionKey();

@Override
public String convertToDatabaseColumn(String attribute) {
if (attribute == null) return null;
try {
return Base64.getEncoder().encodeToString(
encryption.encrypt(attribute.getBytes(), key)
);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

@Override
public String convertToEntityAttribute(String dbData) {
if (dbData == null) return null;
try {
return new String(
encryption.decrypt(Base64.getDecoder().decode(dbData), key)
);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

最佳实践

加密算法选择

场景推荐算法
密码存储BCrypt, Argon2
对称加密AES-GCM
非对称加密RSA-2048+, ECDH
哈希SHA-256+
签名ECDSA, RSA-PSS

安全清单

  1. 所有敏感数据传输使用 HTTPS
  2. 启用 HSTS
  3. 密码使用 BCrypt/Argon2 存储
  4. 敏感数据字段级加密
  5. 密钥安全存储(不用硬编码)
  6. 旧数据定期重新加密
  7. 密钥定期轮换

常见错误

// 错误 1:使用弱加密算法
Cipher cipher = Cipher.getInstance("DES"); // 已破解

// 错误 2:ECB 模式(不安全)
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");

// 错误 3:密钥硬编码
private static final String KEY = "secret";

// 错误 4:不验证证书
// 允许所有证书(危险)
trustAllCertificates();

小结

数据保护的关键要点:

  1. 传输加密

    • 使用 TLS 1.2+
    • 启用 HSTS
    • 验证证书有效性
  2. 存储加密

    • 敏感数据字段级加密
    • 使用强算法(AES-GCM)
    • 密钥安全存储
  3. 密码存储

    • 使用 BCrypt/Argon2
    • 唯一盐值
    • 适当的成本因子
  4. 密钥管理

    • 不使用硬编码
    • 使用密钥管理服务
    • 定期轮换

记住:加密是保护数据的最后一道防线,首先应该尽量减少敏感数据的收集和存储!