jce中提供了加解密的api:
1、首先应该明白AES是基于数据块的加密方式,也就是说,每次处理的数据是一块(16字节),当数据不是16字节的倍数时填充,这就是所谓的分组密码(区别于基于比特位的流密码),16字节是分组长度
AES支持五种模式:CBC,CFB,ECB,OFB,PCBC,
jce中实现了三种补码方式:NoPadding,PKCS5Padding,ISO10126Padding;不支持SSL3Padding,不支持“NONE”模式。
ECB:是一种基础的加密方式,密文被分割成分组长度相等的块(不足补齐),然后单独一个个加密,一个个输出组成密文。
CBC:是一种循环模式,前一个分组的密文和当前分组的明文异或操作后再加密,这样做的目的是增强破解难度。
CFB/OFB实际上是一种反馈模式,目的也是增强破解的难度。
ECB和CBC的加密结果是不一样的,两者的模式不同,而且CBC会在第一个密码块运算时加入一个初始化向量。
算法/模式/填充 16字节加密后数据长度 不满16字节加密后长度
AES/CBC/NoPadding 16 不支持
AES/CBC/PKCS5Padding 32 16
AES/CBC/ISO10126Padding 32 16
AES/CFB/NoPadding 16 原始数据长度
AES/CFB/PKCS5Padding 32 16
AES/CFB/ISO10126Padding 32 16
AES/ECB/NoPadding 16 不支持
AES/ECB/PKCS5Padding 32 16
AES/ECB/ISO10126Padding 32 16
AES/OFB/NoPadding 16 原始数据长度
AES/OFB/PKCS5Padding 32 16
AES/OFB/ISO10126Padding 32 16
AES/PCBC/NoPadding 16 不支持
AES/PCBC/PKCS5Padding 32 16
AES/PCBC/ISO10126Padding 32 16
可 以看到,在原始数据长度为16的整数倍时,假如原始数据长度等于16*n,则使用NoPadding时加密后数据长度等于16*n,其它情况下加密数据长 度等于16*(n+1)。在不足16的整数倍的情况下,假如原始数据长度等于16*n+m[其中m小于16],除了NoPadding填充之外的任何方 式,加密数据长度都等于16*(n+1);NoPadding填充情况下,CBC、ECB和PCBC三种模式是不支持的,CFB、OFB两种模式下则加密 数据长度等于原始数据长度。
Demo:
import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AESCoder { private static final Logger log=LoggerFactory.getLogger(AESCoder.class); /** * 加密 * hexStr和hexKey都须为16进制表示的字符串 * 加密后返回16进制表示的字符串*/ public static String ecbEnc(String hexStr, String hexKey){ String rs=null; try { byte[] inBytes = HexUtil.hexToBytes(hexStr); byte[] keyBytes = HexUtil.hexToBytes(hexKey); SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");// "算法/模式/补码方式" cipher.init(Cipher.ENCRYPT_MODE, skeySpec); byte[] encrypted = cipher.doFinal(inBytes); rs=HexUtil.bytesToHex(encrypted); } catch (Exception e) { log.error("加密异常",e); log.error("输入参数为hexStr:{},hexKey:{}",hexStr,hexKey); } return rs; } /** * 解密 * hexStr和hexKey都须为16进制 * 加密后返回16进制的字符串*/ public static String ecbDec(String hexStr,String hexKey){ String rs=null; try { byte[] outBytes = HexUtil.hexToBytes(hexStr); byte[] keyBytes = HexUtil.hexToBytes(hexKey); SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");// "算法/模式/补码方式" cipher.init(Cipher.DECRYPT_MODE, skeySpec); byte[] decBytes = cipher.doFinal(outBytes); rs=HexUtil.bytesToHex(decBytes); } catch (Exception e) { log.error("解密异常",e); log.error("输入参数为hexStr:{},hexKey:{}",hexStr,hexKey); } return rs; } }
public class HexUtil { /** * 将普通字符串用16进制描述 * 如"WAZX-B55SY6-S6DT5" 描述为:"57415a582d4235355359362d5336445435" * */ public static String strToHex(String str){ byte[] bytes = str.getBytes(); return bytesToHex(bytes); } /**将16进制描述的字符串还原为普通字符串 * 如"57415a582d4235355359362d5336445435" 还原为:"WAZX-B55SY6-S6DT5" * */ public static String hexToStr(String hex){ byte[] bytes=hexToBytes(hex); return new String(bytes); } /**16进制转byte[]*/ public static byte[] hexToBytes(String hex){ int length = hex.length() / 2; byte[] bytes=new byte[length]; for(int i=0;i<length;i++){ String tempStr=hex.substring(2*i, 2*i+2);//byte:8bit=4bit+4bit=十六进制位+十六进制位 bytes[i]=(byte) Integer.parseInt(tempStr, 16); } return bytes; } /**byte[]转16进制*/ public static String bytesToHex(byte[] bytes){ StringBuilder sb=new StringBuilder(); for(int i=0;i<bytes.length;i++){ int tempI=bytes[i] & 0xFF;//byte:8bit,int:32bit;高位相与. String str = Integer.toHexString(tempI); if(str.length()<2){ sb.append(0).append(str);//长度不足两位,补齐:如16进制的d,用0d表示。 }else{ sb.append(str); } } return sb.toString(); } }
public class AESTest { private static String key="2b7e151628aed2a6abf7158809cf4f3c"; @Test public void test_all(){ String enOri="000000000000000WAZX-B55SY6-S6DT5"; String enHex=HexUtil.strToHex(enOri); String enRS=AESCoder.ecbEnc(enHex,key); System.out.println("加密结果为:"+enRS); String deHex="7312560ccb30ad9b445ee94b426c8a2bdf75d11ded50f053568ec08bf3f9be04"; String deRS=AESCoder.ecbDec(deHex,key); String deOri=HexUtil.hexToStr(deRS); System.out.println("解密结果为:"+deOri); } @Test public void test_enc(){ String enStr ="6bc1bee22e409f96e93d7e117393172a"; String enRS=AESCoder.ecbEnc(enStr,key); System.out.println(enRS); } @Test public void test_dec(){ String deStr ="3ad77bb40d7a3660a89ecaf32466ef97"; String enRS=AESCoder.ecbDec(deStr,key); System.out.println(enRS); } /** * 和后台联调时使用 * 该函数用于讲16进制数组转成String * 如密钥key为 * uint8_t key[] = * {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, * 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c} * 则格式化之后为"2b7e151628aed2a6abf7158809cf4f3c" * */ public static String convertStr(String hexStr) { String[] kStrs = hexStr.split(","); String[] keyStrs = new String[kStrs.length]; for (int i = 0; i < kStrs.length; i++) { String str = kStrs[i].trim().substring(2); keyStrs[i] = str; } StringBuffer sb = new StringBuffer(); for (String str : keyStrs) { sb.append(str); } return sb.toString().toUpperCase(); } }
AESTest运行结果:
加密结果为:7312560ccb30ad9b445ee94b426c8a2bdf75d11ded50f053568ec08bf3f9be04
解密结果为:000000000000000WAZX-B55SY6-S6DT5
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------此外,还有另外一个写法-----------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.SecretKeySpec; public class AESHelper { public static String encrypt(String content, String pwd,String charSet) { try { byte[] byteContent = content.getBytes(charSet); KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(128, new SecureRandom(pwd.getBytes())); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES")); byte[] buf = cipher.doFinal(byteContent); //byte[]转16进制 StringBuffer sb = new StringBuffer(); for (int i = 0; i < buf.length; i++) { String hex = Integer.toHexString(buf[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } sb.append(hex.toUpperCase()); } return sb.toString(); } catch (Exception e) { e.printStackTrace(); } return null; } /*** * * @param content 16进制的字符串 * @param pwd * @param charSet * @return */ public static String decrypt(String content, String pwd,String charSet) { try { byte[] byteContent = new byte[content.length() / 2]; if (content.length() < 1){ return null; } //将16进制转换为二进制 for (int i = 0; i < content.length() / 2; i++) { int high = Integer.parseInt(content.substring(i * 2, i * 2 + 1), 16); int low = Integer.parseInt(content.substring(i * 2 + 1, i * 2 + 2), 16); byteContent[i] = (byte) (high * 16 + low); } KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(128, new SecureRandom(pwd.getBytes())); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES")); byte[] buf = cipher.doFinal(byteContent); return new String(buf,charSet); } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) { String layer = AESHelper.encrypt("编程ABCDefgh~!@#$%^&*()|'?>.<;", "123","utf-8"); System.out.println(layer); String plain = AESHelper.decrypt(layer,"123","utf-8"); System.out.println(plain); } }
据说要加上要两句话,不然,跨操作系统加解密可能有问题:
SecureRandom secureRandom=SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(pwd.getBytes());
最终写法:
import java.security.Key; import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; public class AESHelper { public static String encrypt(String content) { return encrypt(content, null, "utf-8"); } /*** * 加密 * * @param plain * @param keySeed * @param charSet * @return 输出密文 16进制 */ public static String encrypt(String plain, String keySeed, String charSet) { try { byte[] byteContent = plain.getBytes(charSet); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, getKey(keySeed)); byte[] buf = cipher.doFinal(byteContent); // byte[]转16进制 StringBuffer sb = new StringBuffer(); for (int i = 0; i < buf.length; i++) { String hex = Integer.toHexString(buf[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } sb.append(hex.toUpperCase()); } return sb.toString(); } catch (Exception e) { e.printStackTrace(); } return null; } public static String decrypt(String content) { return decrypt(content, null, "utf-8"); } /*** * 解密 输入16进制的字符串 * * @param layer * @param keySeed * @param charSet * @return 原文 */ public static String decrypt(String layer, String keySeed, String charSet) { try { byte[] byteContent = new byte[layer.length() / 2]; if (layer.length() < 1) { return null; } // 将16进制转换为二进制 for (int i = 0; i < layer.length() / 2; i++) { int high = Integer.parseInt(layer.substring(i * 2, i * 2 + 1), 16); int low = Integer.parseInt(layer.substring(i * 2 + 1, i * 2 + 2), 16); byteContent[i] = (byte) (high * 16 + low); } Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, getKey(keySeed)); byte[] buf = cipher.doFinal(byteContent); return new String(buf, charSet); } catch (Exception e) { e.printStackTrace(); } return null; } public static Key getKey(String keySeed) { if (keySeed == null) { keySeed = System.getenv("AES_SYS_KEY"); } if (keySeed == null) { keySeed = System.getProperty("AES_SYS_KEY"); } if (keySeed == null || keySeed.trim().length() == 0) { keySeed = "abcd1234!@#$";// 默认种子 } try { SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); secureRandom.setSeed(keySeed.getBytes()); KeyGenerator generator = KeyGenerator.getInstance("AES"); generator.init(secureRandom); return generator.generateKey(); } catch (Exception e) { throw new RuntimeException(e); } } public static void main(String[] args) { if(null==args||args.length==0){ args=new String[1]; args[0]="编程ABCDefgh~!@#$%^&*()|'?>.<;1234567823401"; } String layer = AESHelper.encrypt(args[0]); System.out.println("密文:"+layer); String plain = AESHelper.decrypt(layer); System.out.println("原文:"+plain); } }
linux 运行该方法:
[zhangbin@WEB01 test]$ java AESHelper "jwe223489430214aefhasduf3423"
密文:49336565BD95CB2E70297F4F89DEE2D37EA6A7C14A320194848CF17E930B1DB5
原文:jwe223489430214aefhasduf3423