.前言

AES(Advanced Encryption Standard),高级加密标准,是美国政府用于替换DES的一种加密算法标准,Java SDK中包含了部分AES的实现,但javadoc对于算法的描述非常少,本文将解释Java AES实现的使用和原理。

.示例代码

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class AesECB {
    public static byte[] Encrypt(byte[] text, byte[] key) throws Exception {
        SecretKeySpec aesKey = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // default, same as "AES"
        // Cipher cipher = Cipher.getInstance("AES"); // same as above
        cipher.init(Cipher.ENCRYPT_MODE, aesKey);
        return cipher.doFinal(text);
    }
    public static byte[] Decrypt(byte[] text, byte[] key) throws Exception {
        SecretKeySpec aesKey = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, aesKey);
        return cipher.doFinal(text);
    }
    public static String bytes2hex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String temp = (Integer.toHexString(bytes[i] & 0XFF));
            if (temp.length() == 1) {
                temp = "0" + temp;
            }
            sb.append(temp);
            sb.append(" ");
        }
        return sb.toString().toUpperCase();
    }
    public static void main(String[] args) {
        try {
            String key = args[0];
            String text = args[1];
            System.out.printf("text   : %s\n", bytes2hex(text.getBytes()));
            byte[] enc = AesECB.Encrypt(text.getBytes(), key.getBytes());
            System.out.printf("encrypt: %s\n", bytes2hex(enc));
            byte[] dec =  AesECB.Decrypt(enc, key.getBytes());
            System.out.printf("decrypt: %s\n", bytes2hex(dec));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

.代码分析

JDK AES加密主要是几个步骤:

1.key变换

将传入的明文key做变换,AESkey固定为128bit,但变换后的key为加密位数的一倍,128bit的加密变换后key256bit。注意加密和解密的key变换不一样。

2.加密解密

选择加密的模式和补齐填充方法生成加密实例,加密得到密文。

.密码块工作模式

块密码工作模式(Block cipher mode of operation),是对于按块处理密码的加密方式的一种扩充,不仅仅适用于AES,包括DES, RSA等加密方法同样适用。

名称

英文

全名

方法

优点

缺点

ECB

Electronic codebook

电子密码本

每块独立加密

1.分块可以并行处理

1.同样的原文得到相同的密文,容易被***

CBC

Cipher-block chaining

密码分组链接

每块加密依赖于前一块的密文

1.同样的原文得到不同的密文
2.
原文微小改动影响后面全部密文

1.加密需要串行处理
2.
误差传递

PCBC

Propagating cipher-block chaining

填充密码块链接

CBC的扩种,较少使用

1.同样的原文得到不同的密文
2.
互换两个邻接的密文块不会对后续块的解密造成影响

1.加密需要串行处理

CFB

Cipher feedback

密文反馈




OFB

Output feedback

输出反馈模式

加密后密文与原文异或XOR


1.能够对密文进行校验

CTR

Counter mode

计数器模式

增加一个序列函数对所有密文快做XOR



.填充

填充(Padding),是对需要按块处理的数据,当数据长度不符合块处理需求时,按照一定方法填充满块长的一种规则。

名称

方法

示例

Zero padding

最常见的方式,全填充0x00

AA AA AA AA 00 00 00 00

ANSI X.923

Zero的改进,最后一个字节为填充字节个数

AA AA AA AA 00 00 00 04

ISO 10126

随机填充

AA AA AA AA 81 A6 23 04

PKCS7

ANSI X.923的变体
填充1个字符就全0x01
填充2个字符就全0x02
不需要填充就增加一个块,填充块长度,块长为8就填充0x08,块长为16就填充0x10

AA AA AA AA AA AA AA 01
AA AA AA AA 04 04 04 04
AA AA AA AA AA AA AA AA 08 08 08 08 08 08 08 08

ISO/IEC 7816-4

0x80开始作为填充开始标记,后续全填充0x00

AA AA AA AA AA AA AA 80
AA AA AA AA 80 00 00 00

.JDK AES实现

1.实现支持

AES理论上支持128,192,256三种长度的密钥,几乎全部密码块工作模式和填充方法,但JDK 7中只实现如下四种AES加密算法:

(1)AES/CBC/NoPadding (128)
(2)AES/CBC/PKCS5Padding (128)
(3)AES/ECB/NoPadding (128)
(4)AES/ECB/PKCS5Padding (128)

2.使用须知

(1)缺省模式和填充为“AES/ECB/PKCS5Padding”Cipher.getInstance(“AES”)Cipher.getInstance(“AES/ECB/PKCS5Padding”)等效。

(2)JDKPKCS5Padding实际是上述的PKCS7的实现。

(3)由于AES是按照16Byte为块进行处理,对于NoPadding而言,如果需要加密的原文长度不是16Byte的倍数,将无法处理抛出异常,其实是由用户自己选择Padding的算法。密文则必然是16Byte的倍数,否则密文肯定异常。

(4)如果加密为PKCS5Padding,解密可以选择NoPadding,也能解密成功,内容为原文加上PKCS5Padding之后的结果。

(5)如果原文最后一个字符为>=0x00&&<=0x10的内容,PKCS5Padding的解密将会出现异常,要么是符合PKCS5Padding,最后的内容被删除,要么不符合,则解密失败抛出异常。对此有两种思路,一是原文通过Base64编码为可见字符,二是原文自带长度使用NoPadding解密。

.参考

(1)AES(Wiki)
(2)
Block cipher mode of operation(Wiki)
(3)块密码的工作模(Wiki)
(4)
Padding(Wiki)
(5)
PKCS(Wiki)
(6)
JAVA 7 Cipher