AES学习小结
AES是基于数据块的加密方式,即每次处理的数据是一块(16字节),当数据不是16字节的倍数时填充,这就是所谓的分组密码(区别于基于比特位的流密码),16字节是分组长度。
jce中实现了三种补码方式:NoPadding,PKCS5Padding,ISO10126Padding;不支持SSL3Padding,不支持“NONE”模式。
ECB:是一种基础的加密方式,密文被分割成分组长度相等的块(不足补齐),然后单独一个个加密,一个个输出组成密文。
CBC:是一种循环模式,前一个分组的密文和当前分组的明文异或操作后再加密,这样做的目的是增强破解难度。
CFB/OFB实际上是一种反馈模式,目的也是增强破解的难度。
ECB和CBC的加密结果是不一样的,两者的模式不同,而且CBC会在第一个密码块运算时加入一个初始化向量。
算法/模式/填充16字节加密后数据长度
不满16字节加密后长度
AES/CBC/NoPadding16
不支持
AES/CBC/PKCS5Padding32
16
AES/CBC/ISO10126Padding32
16
AES/CFB/NoPadding16
原始数据长度
AES/CFB/PKCS5Padding32
16
AES/CFB/ISO10126Padding32
16
AES/ECB/NoPadding16
不支持
AES/ECB/PKCS5Padding32
16
AES/ECB/ISO10126Padding32
16
AES/OFB/NoPadding16
原始数据长度
AES/OFB/PKCS5Padding32
16
AES/OFB/ISO10126Padding32
16
AES/PCBC/NoPadding16
不支持
AES/PCBC/PKCS5Padding32
16
AES/PCBC/ISO10126Padding32
16
AES-128-CBC方式与AES-128方式不同的地方:
AES-128-CBC可以自己定义“密钥”和“偏移量“,AES-128是jdk自动生成的“密钥”,所以AES-128-CBC更灵活。
代码实例:
package com.client; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /**AES 是一种可逆加密算法,对用户的敏感信息加密处理 * 对原始数据进行AES加密后,在进行Base64编码转化; */ public class AesCBC { /* * 加密用的Key 可以用26个字母和数字组成 * 此处使用AES-128-CBC加密模式,key需要为16位。 */ private static String sKey = "sklhdflsjfsdgdeg"; private static String ivParameter = "cfbsdfgsdfxccvd1"; private static AesCBC instance = null; private AesCBC() { } public static AesCBC getInstance() { if (instance == null) { instance = new AesCBC(); } return instance; } // 加密 public String encrypt(String sSrc, String encodingFormat, String sKey, String ivParameter) throws Exception { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); byte[] raw = sKey.getBytes(); SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes()); //使用CBC模式,需要一个向量iv,可增加加密算法的强度 cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); byte[] encrypted = cipher.doFinal(sSrc.getBytes(encodingFormat)); return new BASE64Encoder().encode(encrypted); //此处使用BASE64做转码。 } // 解密 public String decrypt(String sSrc, String encodingFormat, String sKey, String ivParameter) throws Exception { try { byte[] raw = sKey.getBytes("ASCII"); SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes()); cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc); //先用base64解密 byte[] original = cipher.doFinal(encrypted1); String originalString = new String(original, encodingFormat); return originalString; } catch (Exception ex) { return null; } } public static void main(String[] args) throws Exception { // 需要加密的字串 String cSrc = "我来自中国"; System.out.println(cSrc); // 加密 long lStart = System.currentTimeMillis(); String enString = AesCBC.getInstance() .encrypt(cSrc, "utf-8", sKey, ivParameter); System.out.println("加密后的字串是:" + enString); long lUseTime = System.currentTimeMillis() - lStart; System.out.println("加密耗时:" + lUseTime + "毫秒"); // 解密 lStart = System.currentTimeMillis(); String DeString = AesCBC.getInstance() .decrypt(enString, "utf-8", sKey, ivParameter); System.out.println("解密后的字串是:" + DeString); lUseTime = System.currentTimeMillis() - lStart; System.out.println("解密耗时:" + lUseTime + "毫秒"); } }
java 不支持PKCS7Padding,只支持PKCS5Padding 但是PKCS7Padding 和 PKCS5Padding 没有什么区别,PKCS5Padding的blocksize为8字节,而PKCS7Padding的blocksize可以为1到255字节。
要实现在java端用PKCS7Padding填充,需要用到bouncycastle组件来实现。
加密代码如下:
/** * * @author ngh * AES128 算法 * * CBC 模式 * * PKCS7Padding 填充模式 * * CBC模式需要添加一个参数iv * * 介于java 不支持PKCS7Padding,只支持PKCS5Padding 但是PKCS7Padding 和 PKCS5Padding 没有什么区别 * 要实现在java端用PKCS7Padding填充,需要用到bouncycastle组件来实现 */ public class AES { // 算法名称 final String KEY_ALGORITHM = "AES"; // 加解密算法/模式/填充方式 final String algorithmStr = "AES/CBC/PKCS7Padding"; // private Key key; private Cipher cipher; boolean isInited = false; byte[] iv = { 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, 0x30, 0x36, 0x30, 0x37, 0x30, 0x38 }; public void init(byte[] keyBytes) { // 如果密钥不足16位,那么就补足. 这个if 中的内容很重要 int base = 16; if ((keyBytes.length % base) != 0) { int groups = (keyBytes.length / base) + (((keyBytes.length % base) != 0) ? 1 : 0); byte[] temp = new byte[groups * base]; Arrays.fill(temp, (byte) 0); System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length); keyBytes = temp; } // 初始化 Security.addProvider(new BouncyCastleProvider()); // 转化成JAVA的密钥格式 key = new SecretKeySpec(keyBytes, KEY_ALGORITHM); try { // 初始化cipher cipher = Cipher.getInstance(algorithmStr, "BC"); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchPaddingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchProviderException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 加密方法 * * @param content * 要加密的字符串 * @param keyBytes * 加密密钥 * @return */ public byte[] encrypt(byte[] content, byte[] keyBytes) { byte[] encryptedText = null; init(keyBytes); System.out.println("IV:" + new String(iv)); try { cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); encryptedText = cipher.doFinal(content); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return encryptedText; } /** * 解密方法 * * @param encryptedData * 要解密的字符串 * @param keyBytes * 解密密钥 * @return */ public byte[] decrypt(byte[] encryptedData, byte[] keyBytes) { byte[] encryptedText = null; init(keyBytes); System.out.println("IV:" + new String(iv)); try { cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); encryptedText = cipher.doFinal(encryptedData); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return encryptedText; } }
测试代码如下:
public class Test { public static void main(String[] args) { AES aes = new AES(); // 加解密 密钥 byte[] keybytes = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 }; String content = "1"; // 加密字符串 System.out.println("加密前的:" + content); System.out.println("加密密钥:" + new String(keybytes)); // 加密方法 byte[] enc = aes.encrypt(content.getBytes(), keybytes); System.out.println("加密后的内容:" + new String(Hex.encode(enc))); // 解密方法 byte[] dec = aes.decrypt(enc, keybytes); System.out.println("解密后的内容:" + new String(dec)); } }