AES加密

AES加密的五种模式

  1. ECB(Electronic Codebook Book) 电码本模式
    将明文分成若干段相同的小段, 不足补齐, 然后依次对每一小段进行加密后输出密文. ECB模式的弱点在于, 相同的明文会产生相同的密文, 容易遭到字典攻击, 安全性不够高 

  2. CBC(Cipher Block Chaining) 密码分组链接模式
    先明文切分成若干小段, 每一小段与初始块或者上一段的密文段进行异或运算后, 再与密钥进行加密, 这样做的目的是增强破解难度. 相对于ECB模式来说, CBC模式较安全, 同时CBC适合于传输长度较长的报文

  3. CTR(Counter) 计算器模式
    在CTR模式中有一个自增的算子, 这个算子用密钥加密之后的输出和明文异或的结果得到密文, 相当于一次一密. 这种加密方式简单快速安全可靠, 而且可以并行加密. 但是在计算器不能维持很长的情况下, 密钥只能使用一次

  4. CFB(Cipher FeedBack) 密码反馈模式

  5. OFB(Output FeedBack) 输出反馈模式

AES ECB和CBC在代码实现中的区别

ECB模式的加解密, 只需要一个passcode参数

public static String aesDecrypt(String hexString, String passcode) {
    try {
        byte[] raw = passcode.getBytes(StandardCharsets.UTF_8);
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        byte[] encrypted = CodecUtil.hexStringToBytes(hexString);
        byte[] original = cipher.doFinal(encrypted);
        return new String(original, StandardCharsets.UTF_8);
    } catch (Exception e) {
        LOG.error(e.getMessage(), e);
        return null;
    }
}

public static String aesEncrypt(String plainText, String passcode) {
    try {
        byte[] raw = passcode.getBytes(StandardCharsets.UTF_8);
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        return CodecUtil.hexBytesToString(encrypted);
    } catch (Exception e) {
        LOG.error(e.getMessage(), e);
        return null;
    }
}

CBC模式则需要增加两个参数, salt 和 iv (initialization vector) 

/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

 其中65536是循环次数, 256是位数. 在CBC模式下, 对应每个文本会生成一个随机的初始化向量, 所以即使文本相同, 产生的密文也不同, 每次加密输出的结果包含两部分: 密文和初始化向量.

/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes("UTF-8"));

 保存密文和初始化向量后, 解密时密钥的生成还是一样的, 使用相同的口令 + salt + 循环次数, 但是cipher需要用和密文一起保存的初始化向量来初始化. 

/* Decrypt the message, given derived key and initialization vector. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
String plaintext = new String(cipher.doFinal(ciphertext), "UTF-8");
System.out.println(plaintext);

如果需要简化输入, 可以将口令同时用于生成初始化向量.

AES加密时的 java.security.InvalidKeyException: Illegal key size 异常

程序代码

            // 设置加密模式为AES的CBC模式
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
            IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);

            // 加密
            byte[] encrypted = cipher.doFinal(unencrypted);
...

当执行到

cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);

时, 如果密钥大于128, 会抛出java.security.InvalidKeyException: Illegal key size 异常. 因为密钥长度是受限制的, java运行时环境读到的是受限的policy文件. 文件位于${java_home}/jre/lib/security, 这种限制是因为美国对软件出口的控制.

处理办法: 在官方网站下载JCE无限制权限策略文件

JDK7的下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
JDK8的下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt
如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件
如果安装了JDK,还要将两个jar文件也放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件

posted on 2015-12-19 02:06  Milton  阅读(9059)  评论(1编辑  收藏  举报

导航