import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; public class AesEncryptUtil { private static final String encode = "UTF-8"; private static final String mode = "AES/CBC/PKCS5Padding"; /** * JDK只支持AES-128加密,也就是密钥长度必须是128bit;参数为密钥key,key的长度小于16字符时用"0"补充,key长度大于16字符时截取前16位 **/ private static SecretKeySpec get128BitsKey(String key) { if (key == null) { key = ""; } byte[] data = null; StringBuffer buffer = new StringBuffer(16); buffer.append(key); //小于16后面补0 while (buffer.length() < 16) { buffer.append("0"); } //大于16,截取前16个字符 if (buffer.length() > 16) { buffer.setLength(16); } try { data = buffer.toString().getBytes(encode); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return new SecretKeySpec(data, "AES"); } /** * 创建128位的偏移量,iv的长度小于16时后面补0,大于16,截取前16个字符; * * @param iv * @return */ private static IvParameterSpec get128BitsIV(String iv) { if (iv == null) { iv = ""; } byte[] data = null; StringBuffer buffer = new StringBuffer(16); buffer.append(iv); while (buffer.length() < 16) { buffer.append("0"); } if (buffer.length() > 16) { buffer.setLength(16); } try { data = buffer.toString().getBytes(encode); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return new IvParameterSpec(data); } /** * 填充方式为Pkcs5Padding的加密函数 * 填充方式为Pkcs5Padding时,最后一个块需要填充χ个字节,填充的值就是χ,也就是填充内容由JDK确定 * * @param srcContent: 明文 * @param password: 加密密钥(不足128bits时,填"0"补足) * @param iv: 初始向量(不足128bits时,填"0"补足) * * @return 密文(16进制表示) * */ public static String encrypt(String srcContent, String password, String iv) { SecretKeySpec key = get128BitsKey(password); IvParameterSpec ivParameterSpec = get128BitsIV(iv); try { Cipher cipher = Cipher.getInstance(mode); cipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec); byte[] byteContent = srcContent.getBytes(encode); byte[] encryptedContent = cipher.doFinal(byteContent); String result = HexUtil.byte2HexStr(encryptedContent); return result; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 填充方式为Pkcs5Padding的解密函数 * 填充方式为Pkcs5Padding时,最后一个块需要填充χ个字节,填充的值就是χ,也就是填充内容由JDK确定 * * @param encryptedContent: 密文 * @param password: 加密密钥(不足128bits时,填"0"补足) * @param iv: 初始向量(不足128bits时,填"0"补足) * * @return 密文(16进制表示) * */ public static String decrypt(String encryptedContent, String password, String iv) { SecretKeySpec key = get128BitsKey(password); IvParameterSpec ivParameterSpec = get128BitsIV(iv); try { byte[] content = HexUtil.hexStr2Byte(encryptedContent); Cipher cipher = Cipher.getInstance(mode); cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec); byte[] decryptedContent = cipher.doFinal(content); String result = new String(decryptedContent); return result; } catch (Exception e) { e.printStackTrace(); } return null; } } import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; import java.security.Key; import java.util.Date; import java.util.HashMap; import java.util.Map; public class JWTUtil { /** * 生成JWT签名字符串 * * @param publicClaims 无需加密保持明文方式的JWT claims * @param privateClaims: 需要加密的JWT claims * @param ttlMillis: JWT过期时长(毫秒) * @param key: JWT HS256签名的密钥,也是AES加密的密钥 * * @return JWT字符串 * */ public static String createJWT(Map publicClaims, Map privateClaims,long ttlMillis, String key) { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; long nowMillis = System.currentTimeMillis(); byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(key); Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); if(null != privateClaims && !privateClaims.isEmpty()) { String jsonStr = JsonUtil.map2JsonStr(privateClaims); //使用同一个密钥对私有声明进行加密 String encrypteClaims = AesEncryptUtil.encrypt(jsonStr, key, key); if(null != encrypteClaims) { if(null == publicClaims) { publicClaims = new HashMap<>(); } publicClaims.put("privateClaims", encrypteClaims); } } JwtBuilder builder = Jwts.builder().signWith(signatureAlgorithm, signingKey); if(null != publicClaims && publicClaims.size() > 0) { builder.setClaims(publicClaims); } if (ttlMillis >= 0) { long expMillis = nowMillis + ttlMillis; Date exp = new Date(expMillis); builder.setExpiration(exp); } return builder.compact(); } /** * 解析并校验JWT, 校验过程是JJWT内部实现的,会校验JWT是否过期,是否被篡改。 * * @param jwt JWT字符串 * @param key: JWT HS256签名的密钥,也是AES加密的密钥 * * @return JWT claims的Map对象 * */ public static Map parseJWT(String jwt, String key){ Map privateMap = null; //parser函数会在参数缺失、校验失败、token过期等情况下抛出runtime异常,所以调用者需要捕获该runtime异常 Map originalMap = Jwts.parser() .setSigningKey(DatatypeConverter.parseBase64Binary(key)) .parseClaimsJws(jwt).getBody(); //解析私有声明 if(null != originalMap && originalMap.containsKey("privateClaims")) { String encryptedStr = (String)originalMap.get("privateClaims"); if(null != encryptedStr && !encryptedStr.isEmpty()) { String decryptedStr = AesEncryptUtil.decrypt(encryptedStr, key, key); if(null != decryptedStr && !decryptedStr.isEmpty()) { privateMap = JsonUtil.jsonStr2Map(decryptedStr); } } originalMap.remove("privateClaims"); if(null != privateMap && privateMap.size() > 0) { originalMap.putAll(privateMap); } } return originalMap; } }