|
@@ -0,0 +1,117 @@
|
|
|
+package com.ruoyi.common.utils;
|
|
|
+
|
|
|
+import javax.crypto.Cipher;
|
|
|
+import javax.crypto.spec.GCMParameterSpec;
|
|
|
+import javax.crypto.spec.SecretKeySpec;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.security.NoSuchAlgorithmException;
|
|
|
+import java.security.SecureRandom;
|
|
|
+import java.util.Base64;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 敏感信息加密工具类(符合国密标准)
|
|
|
+ * - 身份证号:SM3/SHA-256 不可逆哈希
|
|
|
+ * - 银行账号:AES-256-GCM 可逆加密
|
|
|
+ */
|
|
|
+public class SecureSensitiveUtils {
|
|
|
+
|
|
|
+ // ==================== 加密(可逆) ====================
|
|
|
+ private static final int AES_KEY_LENGTH = 32; // 256-bit
|
|
|
+ private static final int GCM_IV_LENGTH = 12; // 12 bytes
|
|
|
+ private static final int GCM_TAG_LENGTH = 16; // 128-bit
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成AES密钥(实际应从KMS获取)
|
|
|
+ */
|
|
|
+ public static String generateAesKey() throws NoSuchAlgorithmException {
|
|
|
+ String key = "B1a4z9jXeD7vGmKsPq2t5w8y/A6C0bRf";
|
|
|
+ return Base64.getEncoder().encodeToString(key.getBytes(StandardCharsets.UTF_8));
|
|
|
+// KeyGenerator keyGen = KeyGenerator.getInstance("AES");
|
|
|
+// keyGen.init(AES_KEY_LENGTH * 8); // 256-bit
|
|
|
+// return Base64.getEncoder().encodeToString(keyGen.generateKey().getEncoded());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * AES-GCM加密银行账号
|
|
|
+ */
|
|
|
+ public static String encrypt(String bankAccount, String base64Key) throws Exception {
|
|
|
+ // 解码密钥
|
|
|
+ byte[] key = Base64.getDecoder().decode(base64Key);
|
|
|
+ SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
|
|
|
+
|
|
|
+ // 生成IV
|
|
|
+ byte[] iv = new byte[GCM_IV_LENGTH];
|
|
|
+ SecureRandom random = new SecureRandom();
|
|
|
+ random.nextBytes(iv);
|
|
|
+
|
|
|
+ // 配置加密器
|
|
|
+ Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
|
|
+ GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
|
|
|
+ cipher.init(Cipher.ENCRYPT_MODE, keySpec, parameterSpec);
|
|
|
+
|
|
|
+ // 加密数据
|
|
|
+ byte[] ciphertext = cipher.doFinal(bankAccount.getBytes(StandardCharsets.UTF_8));
|
|
|
+
|
|
|
+ // 组合IV+密文
|
|
|
+ byte[] combined = new byte[iv.length + ciphertext.length];
|
|
|
+ System.arraycopy(iv, 0, combined, 0, iv.length);
|
|
|
+ System.arraycopy(ciphertext, 0, combined, iv.length, ciphertext.length);
|
|
|
+
|
|
|
+ return Base64.getEncoder().encodeToString(combined);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * AES-GCM解密银行账号
|
|
|
+ */
|
|
|
+ public static String decrypt(String encrypted, String base64Key) throws Exception {
|
|
|
+ // 解码密钥
|
|
|
+ byte[] key = Base64.getDecoder().decode(base64Key);
|
|
|
+ SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
|
|
|
+
|
|
|
+ // 解码数据
|
|
|
+ byte[] combined = Base64.getDecoder().decode(encrypted);
|
|
|
+ byte[] iv = new byte[GCM_IV_LENGTH];
|
|
|
+ System.arraycopy(combined, 0, iv, 0, iv.length);
|
|
|
+
|
|
|
+ // 配置解密器
|
|
|
+ Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
|
|
+ GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
|
|
|
+ cipher.init(Cipher.DECRYPT_MODE, keySpec, parameterSpec);
|
|
|
+
|
|
|
+ // 解密数据
|
|
|
+ byte[] ciphertext = new byte[combined.length - GCM_IV_LENGTH];
|
|
|
+ System.arraycopy(combined, GCM_IV_LENGTH, ciphertext, 0, ciphertext.length);
|
|
|
+ return new String(cipher.doFinal(ciphertext), StandardCharsets.UTF_8);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 辅助方法 ====================
|
|
|
+ /**
|
|
|
+ * 银行账号脱敏显示(如:6217****1234)
|
|
|
+ */
|
|
|
+ public static String maskBankAccount(String fullAccount) {
|
|
|
+ if (fullAccount == null || fullAccount.length() < 8) {
|
|
|
+ return fullAccount;
|
|
|
+ }
|
|
|
+ return fullAccount.substring(0, 4) + "****" + fullAccount.substring(fullAccount.length() - 4);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 身份证号脱敏显示(如:500108********71**)
|
|
|
+ */
|
|
|
+ public static String maskIdCard(String idCard) {
|
|
|
+ if (idCard == null || idCard.length() < 15) {
|
|
|
+ return idCard;
|
|
|
+ }
|
|
|
+ return idCard.substring(0, 6) + "********" + idCard.substring(14, 16) + "**";
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 手机号脱敏显示(如:139****1324)
|
|
|
+ */
|
|
|
+ public static String maskPhone(String phone) {
|
|
|
+ if (phone == null || phone.length() < 11) {
|
|
|
+ return phone;
|
|
|
+ }
|
|
|
+ return phone.substring(0, 3) + "****" + phone.substring(phone.length() - 4);
|
|
|
+ }
|
|
|
+}
|