package com.talentcard.server.common; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.SecureRandom; import java.util.Arrays; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class SensitiveDataCrypto { private static final SecureRandom RANDOM = new SecureRandom(); private static final String VERSION_PREFIX = "v1:"; private final byte[] key; public SensitiveDataCrypto(@Value("${app.privacy.encryption-key}") String encryptionKey) { try { this.key = MessageDigest.getInstance("SHA-256").digest(encryptionKey.getBytes(StandardCharsets.UTF_8)); } catch (Exception exception) { throw new IllegalStateException("Unable to initialize sensitive-data encryption", exception); } } public String encrypt(String plainText) { if (plainText == null || plainText.isBlank()) return ""; try { byte[] iv = new byte[12]; RANDOM.nextBytes(iv); Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, iv)); byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); byte[] payload = new byte[iv.length + encrypted.length]; System.arraycopy(iv, 0, payload, 0, iv.length); System.arraycopy(encrypted, 0, payload, iv.length, encrypted.length); return VERSION_PREFIX + Base64.getUrlEncoder().withoutPadding().encodeToString(payload); } catch (Exception exception) { throw new BusinessException(ErrorCode.INTERNAL_ERROR, "敏感信息加密失败"); } } public String decrypt(String cipherText) { if (cipherText == null || cipherText.isBlank()) return ""; if (!cipherText.startsWith(VERSION_PREFIX)) return cipherText; try { byte[] payload = Base64.getUrlDecoder().decode(cipherText.substring(VERSION_PREFIX.length())); if (payload.length <= 12) { throw new IllegalArgumentException("Invalid encrypted payload"); } byte[] iv = Arrays.copyOfRange(payload, 0, 12); byte[] encrypted = Arrays.copyOfRange(payload, 12, payload.length); Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, iv)); return new String(cipher.doFinal(encrypted), StandardCharsets.UTF_8); } catch (Exception exception) { throw new BusinessException(ErrorCode.INTERNAL_ERROR, "敏感信息解密失败"); } } }