接口API中的敏感数据基于AES进行安全加密后返回
许久没有写博客了,有些惶恐地打开这个再熟悉不过的编辑器。
场景:要对一个涉及到敏感数据(账号、密码)的接口进行加密后返回
由于之前没有相关的经验,所以先在网上搜罗了一阵,这篇博客不错https://www.cnblogs.com/codeon/p/6123863.html给了我一些思路和启发。
先来介绍两个模糊不清,容易搞混的概念。
- Base64编码,看名字就可以知道这是一种编码方式,编码方式有很多ASCII、Unicode、UTF-8等,Base64编码会把3字节的二进制数据编码为4字节的文本数据,长度增加为原来的4/3。一定要强调一下Base64不是安全领域下的加密解密算法,虽然有时候经常看到有些博客上和变换工具上讲base64加密解密。其实base64只能算是一个编码算法,对数据内容进行编码来适合传输。虽然base64编码过后原文也变成不能看到的字符格式,但是这种方式很初级,很简单。具体了解各个编码的场景可以参照这篇博客,https://blog.csdn.net/charleslei/article/details/50993861。
- MD5摘要算法,这是一种散列函数,提取数据的特征,输出是不可逆的散列值,用于代表某信息A而又不暴露信息A的内容,一般用于数字签名场景中。
加密方式的确定:最后我的接口中的敏感明文信息通过AES进行加密,最后将密文返回给客户端。
网上的一些AES加解密示例中,很多在AES解密步骤时会出现javax.crypto.BadPaddingException: Given final block not properly padded问题,而且很多答案都说的似是而非,经过我的调试和修正后,以下代码可以正常的完成AES/DES的加解密操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | package com.test.utils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** * @Author: LeeChao * @Date: 2018/7/5 * @Describe: 加密工具类 * @Modified By: */ public class EncryptUtil { public static final Logger LOGGER = LogManager.getLogger(EncryptUtil. class ); // 此处使用AES-128-CBC加密模式,key需要为16位。 private static final String AES_ENCRYPT_MODE = "AES/CBC/PKCS5Padding" ; // 16固定的偏移向量 private static final String IV_PARAMETER = "1234567890abcdef" ; private final static String DES_ENCRYPT_MODE = "DES" ; /** * AES加密 * * @param aesKey 加密用的Key 可以用26个字母和数字组成, * @param content * @return * @throws Exception */ public static String aesEncrypt(String aesKey, String content) { // 初始化返回结果 String result = null ; try { Cipher cipher = Cipher.getInstance(AES_ENCRYPT_MODE); byte [] raw = aesKey.getBytes(); SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES" ); // 使用CBC模式,需要一个向量iv,可增加加密算法的强度 IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes()); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); byte [] encrypted = cipher.doFinal(content.getBytes( "utf-8" )); // 此处使用BASE64做转码。 result = new BASE64Encoder().encode(encrypted); // Base64加密后的换行符去掉 result = result.replaceAll( "\\r\\n" , "" ).replaceAll( "\r" , "" ).replaceAll( "\n" , "" ); } catch (Exception e) { LOGGER.error( "AES加密异常" + e); } return result; } /** * AES解密 * * @param aesKey * @param content * @return * @throws Exception */ public static String aesDecrypt(String aesKey, String content) { // 初始化返回结果 String result = null ; try { byte [] raw = aesKey.getBytes( "ASCII" ); SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES" ); Cipher cipher = Cipher.getInstance(AES_ENCRYPT_MODE); IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes()); cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); byte [] encrypted1 = new BASE64Decoder().decodeBuffer(content); //先用base64解密 byte [] original = cipher.doFinal(encrypted1); result = new String(original, "utf-8" ); } catch (Exception e) { LOGGER.error( "AES解密异常" + e); } return result; } /** * MD5散列为16位定长输出 * * @param sourceStr * @return */ public static String MD5(String sourceStr) { String result16 = "" ; try { MessageDigest md = MessageDigest.getInstance( "MD5" ); md.update(sourceStr.getBytes()); byte b[] = md.digest(); int i; StringBuffer buf = new StringBuffer( "" ); for ( int offset = 0 ; offset < b.length; offset++) { i = b[offset]; if (i < 0 ) i += 256 ; if (i < 16 ) buf.append( "0" ); buf.append(Integer.toHexString(i)); } // 32位截取为16位 result16 = buf.toString().substring( 8 , 24 ); } catch (NoSuchAlgorithmException e) { LOGGER.error(e); } return result16; } /********************DES加密方式***********************/ public static byte [] desEncrypt( byte [] src, byte [] key) throws Exception { // DES算法要求有一个可信任的随机数源 SecureRandom sr = new SecureRandom(); // 从原始密匙数据创建DESKeySpec对象 DESKeySpec dks = new DESKeySpec(key); // 创建一个密匙工厂,然后用它把DESKeySpec转换成一个SecretKey对象 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES_ENCRYPT_MODE); SecretKey securekey = keyFactory.generateSecret(dks); // Cipher对象实际完成加密操作 Cipher cipher = Cipher.getInstance(DES_ENCRYPT_MODE); // 用密匙初始化Cipher对象 cipher.init(Cipher.ENCRYPT_MODE, securekey, sr); // 正式执行加密操作 return cipher.doFinal(src); } /** * @param password 密码 * @param key 加密字符串 * @return */ public final static String desEncrypt(String password, String key) { try { return byte2String(desEncrypt(password.getBytes(), key.getBytes())); } catch (Exception e) { } return null ; } public static String byte2String( byte [] b) { String hs = "" ; String stmp = "" ; for ( int n = 0 ; n < b.length; n++) { stmp = (java.lang.Integer.toHexString(b[n] & 0XFF )); if (stmp.length() == 1 ) hs = hs + "0" + stmp; else hs = hs + stmp; } return hs.toUpperCase(); } /** * @param src 数据源 * @param key 密钥,长度必须是8的倍数 * @return * @throws Exception */ public static byte [] desDecrypt( byte [] src, byte [] key) throws Exception { // DES算法要求有一个可信任的随机数源 SecureRandom sr = new SecureRandom(); // 从原始密匙数据创建一个DESKeySpec对象 DESKeySpec dks = new DESKeySpec(key); // 创建一个密匙工厂,然后用它把DESKeySpec对象转换成一个SecretKey对象 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES_ENCRYPT_MODE); SecretKey securekey = keyFactory.generateSecret(dks); // Cipher对象实际完成解密操作 Cipher cipher = Cipher.getInstance(DES_ENCRYPT_MODE); // 用密匙初始化Cipher对象 cipher.init(Cipher.DECRYPT_MODE, securekey, sr); // 正式执行解密操作 return cipher.doFinal(src); } public final static String desDecrypt(String data, String key) { try { return new String(desDecrypt(String2byte(data.getBytes()), key.getBytes())); } catch (Exception e) { e.printStackTrace(); } return null ; } public static byte [] String2byte( byte [] b) { if ((b.length % 2 ) != 0 ) throw new IllegalArgumentException( "长度不是偶数" ); byte [] b2 = new byte [b.length / 2 ]; for ( int n = 0 ; n < b.length; n += 2 ) { String item = new String(b, n, 2 ); b2[n / 2 ] = ( byte ) Integer.parseInt(item, 16 ); } return b2; } private static String testDecryptAES(String authCode, String cipherText) { System.out.println( "=====================AES解密=====================" ); String key = MD5(authCode); // md5散列出16位固定长度的秘钥 System.out.println( "md5-16 :" + key); long encryptTime = System.currentTimeMillis(); String deString = aesDecrypt(key, cipherText); long endTime = System.currentTimeMillis(); System.out.println( "解密耗时:" + (endTime - encryptTime) + "毫秒" ); System.out.println( "解密后的字串是:" + deString); return deString; } public static void main(String[] args) throws Exception { System.out.println( "=====================AES加密=====================" ); String key = MD5( "8373964002824192" ); // md5散列出16位固定长度的秘钥 System.out.println( "md5-16 :" + key); // 需要加密的字串 String cSrc = "TestAESEncrypt" ; // 加密 long lStart = System.currentTimeMillis(); String enString = aesEncrypt(key, cSrc); System.out.println( "加密后的字串是:" + enString); long lUseTime = System.currentTimeMillis() - lStart; System.out.println( "加密耗时:" + lUseTime + "毫秒" ); // 解密 lStart = System.currentTimeMillis(); String DeString = aesDecrypt(key, enString); System.out.println( "解密后的字串是:" + DeString); lUseTime = System.currentTimeMillis() - lStart; System.out.println( "解密耗时:" + lUseTime + "毫秒" ); System.out.println( "=====================DES加密=====================" ); String password = "test中英文杂七烂八混搭@123654{" ; String info = "TestDESEncrypt" ; long desStart = System.currentTimeMillis(); String encryptString = desEncrypt(info, password); System.out.println( "DES加密后的字符串" + encryptString); System.out.println( "DES加密耗时" + (System.currentTimeMillis() - desStart) + "毫秒" ); desStart = System.currentTimeMillis(); String desencryptString = desDecrypt(encryptString, password); System.out.println( "DES解密后的字符串" + desencryptString); System.out.println( "DES解密耗时" + (System.currentTimeMillis() - desStart) + "毫秒" ); } } |
代码执行后的输出:
出处:http://www.cnblogs.com/lingyejun/
若本文如对您有帮助,不妨点击一下右下角的【推荐】。
如果您喜欢或希望看到更多我的文章,可扫描二维码关注我的微信公众号《翎野君》。
转载文章请务必保留出处和署名,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2017-07-05 JVM内存—堆(heap)栈(stack)方法区(method) (转)