/* * Copyright 2025 mingliqiye * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ProjectName mingli-utils * ModuleName mingli-utils.main * CurrentFile AesUtils.java * LastUpdate 2025-09-09 08:37:33 * UpdateUser MingLiPro */ package com.mingliqiye.utils.aes; import com.mingliqiye.utils.base64.Base64Utils; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; public class AesUtils { private static final String ALGORITHM = "AES"; private static final String TRANSFORMATION = "AES/GCM/NoPadding"; private static final int GCM_IV_LENGTH = 12; private static final int GCM_TAG_LENGTH = 16; private static final SecureRandom SECURE_RANDOM = new SecureRandom(); /** * AES加密方法(使用GCM模式) * @param sSrc 待加密的字符串 * @param sKey 加密密钥 * @return 加密后的字符串,格式为 IV:EncryptedData+Tag(均为Base64编码) * @throws GeneralSecurityException 加密错误 */ public static String encrypt(String sSrc, String sKey) throws GeneralSecurityException { if (sKey == null) { return null; } // 生成密钥 SecretKeySpec secretKeySpec = createSecretKey(sKey); // 生成安全随机IV byte[] iv = new byte[GCM_IV_LENGTH]; SECURE_RANDOM.nextBytes(iv); // 初始化加密器 Cipher cipher = Cipher.getInstance(TRANSFORMATION); GCMParameterSpec gcmParameterSpec = new GCMParameterSpec( GCM_TAG_LENGTH * 8, iv ); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec); // 执行加密 byte[] encrypted = cipher.doFinal( sSrc.getBytes(StandardCharsets.UTF_8) ); // 将IV和加密数据(包含认证标签)组合返回 return Base64Utils.encode( (Base64Utils.encode(iv) + ":" + Base64Utils.encode(encrypted)).getBytes() ); } /** * AES解密方法(使用GCM模式) * @param sSrc 待解密的字符串,格式为 IV:EncryptedData+Tag(均为Base64编码) * @param sKey 解密密钥 * @return 解密后的原始字符串 */ public static String decrypt(String sSrc, String sKey) { if (sKey == null) { return null; } try { // 分割IV和加密数据 String sSrcs = new String(Base64Utils.decode(sSrc)); String[] parts = sSrcs.split(":", 2); if (parts.length != 2) { return null; } byte[] iv = Base64Utils.decode(parts[0]); byte[] encryptedData = Base64Utils.decode(parts[1]); if (iv.length != GCM_IV_LENGTH) { return null; } SecretKeySpec secretKeySpec = createSecretKey(sKey); Cipher cipher = Cipher.getInstance(TRANSFORMATION); GCMParameterSpec gcmParameterSpec = new GCMParameterSpec( GCM_TAG_LENGTH * 8, iv ); cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec); byte[] original = cipher.doFinal(encryptedData); return new String(original, StandardCharsets.UTF_8); } catch (Exception e) { return null; } } /** * 创建AES密钥,支持任意长度的密钥 * @param sKey 字符串密钥 * @return SecretKeySpec对象 * @throws Exception 可能抛出的异常 */ private static SecretKeySpec createSecretKey(String sKey) throws GeneralSecurityException { byte[] key = sKey.getBytes(StandardCharsets.UTF_8); MessageDigest md = MessageDigest.getInstance("MD5"); byte[] digest = md.digest(key); return new SecretKeySpec(digest, ALGORITHM); } }