概念
高级加密标准(Advanced Encryption Standard
:AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。
这个标准用来替代原先的DES。
该算法为比利时密码学家Joan Daemen和Vincent Rijmen所设计,结合两位作者的名字,以Rijndael为名投稿高级加密标准的甄选流程。
严格地说,AES和Rijndael加密法并不完全一样(虽然在实际应用中两者可以互换),因为Rijndael加密法可以支持更大范围的区块和密钥长度:AES的区块长度
固定为128比特,密钥长度
则可以是128,192或256比特;而Rijndael使用的密钥和区块长度
均可以是128,192或256比特。加密过程中使用的密钥是由Rijndael密钥生成方案产生。
大多数AES计算是在一个特别的有限域
完成的。
AES加密过程
AES加密过程是在一个4×4的字节矩阵上运作,这个矩阵又称为“体(state)”,其初值就是一个明文区块(矩阵中一个元素大小就是明文区块中的一个Byte)。
(Rijndael加密法因支持更大的区块,其矩阵的“列数(Row number)”可视情况增加)加密时,各轮AES加密循环(除最后一轮外)均包含4个步骤:
- AddRoundKey 步骤
矩阵中的每一个字节都与该次回合密钥(round key)
做XOR运算
;每个子密钥由密钥生成方案产生。
回合密钥
将会与原矩阵合并。在每次的加密循环中,都会由主密钥产生一把回合密钥(透过Rijndael密钥生成方案产生),这把密钥大小会跟原矩阵一样,以与原矩阵中每个对应的字节作异或(⊕)加法。
- SubBytes 步骤
透过一个非线性的替换函数,用查找表
的方式把每个字节替换成对应的字节。
在SubBytes步骤中,矩阵中的各字节透过一个8位的S-box
进行转换。这个步骤提供了加密法
非线性的变换能力。
S-box与GF(2^8)上的乘法反元素
有关,已知具有良好的非线性
特性。为了避免简单代数性质的攻击,S-box结合了乘法反元素及一个可逆的仿射变换
矩阵建构而成。
此外在建构S-box时,刻意避开了不动点
与反不动点
,即以S-box替换字节的结果会相当于错排的结果。Rijndael S-box条目有针对S-box的详细描述。
- ShiftRows步骤
将矩阵中的每个横列进行循环式移位。
ShiftRows描述矩阵的行操作。在此步骤中,每一行都向左循环位移某个偏移量
。在AES中(区块大小128位),第一行维持不变,第二行里的每个字节都向左循环移动一格。
同理,第三行及第四行向左循环位移的偏移量就分别是2和3。128位和192比特的区块在此步骤的循环位移的模式相同。
经过ShiftRows之后,矩阵中每一竖列,都是由输入矩阵中的每个不同列中的元素组成。
Rijndael算法的版本中,偏移量和AES有少许不同;对于长度256比特的区块,第一行仍然维持不变,第二行、第三行、第四行的偏移量分别是1字节、2字节、3字节。
除此之外,ShiftRows操作步骤在Rijndael和AES中完全相同。
- MixColumns步骤
为了充分混合矩阵中各个直行的操作。这个步骤使用线性转换
来混合每内联的四个字节。最后一个加密循环中省略MixColumns步骤,而以另一个AddRoundKey取代。
每一列的四个字节透过线性变换互相结合。每一列的四个元素分别当作 1 , x , x^2 , x^3 的系数,合并即为 GF(2^8) 中的一个多项式,接着将此多项式和一个固定的多项式 c(x)=3x3+x2+x+2 在模 x^4+1 下相乘。此步骤亦可视为 Rijndael 有限域
之下的矩阵乘法。MixColumns 函数接受4个字节的输入,输出4个字节,每一个输入的字节都会对输出的四个字节造成影响。因此 ShiftRows 和 MixColumns 两步骤为这个密码系统提供了扩散性。
java jdk 实现
package crypto.aes;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
/**
* 对称加密 AES算法 jdk实现
*/
public class AesUtils {
private static final String ALGORITHM = "AES";
public enum MODE {
ECB,
CBC,
PCBC,
CTR,
CFB,
OFB
}
public enum PADDING {
NoPadding,
PKCS5Padding,
ISO10126Padding
}
public static byte[] initKey() throws NoSuchAlgorithmException, NoSuchProviderException {
return initKey(128);
}
/**
* @param keySIze 128 192 256
* @return 密钥
* @throws NoSuchAlgorithmException
*/
public static byte[] initKey(int keySIze) throws NoSuchAlgorithmException, NoSuchProviderException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
keyGenerator.init(keySIze);// 密钥长度
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
private static Key buildKey(byte[] key) {
return new SecretKeySpec(key, ALGORITHM);
}
public static byte[] encrypt(byte[] key, byte[] data) throws Exception {
return encrypt(key, data, MODE.ECB.name(), PADDING.PKCS5Padding.name());
}
/**
* @param key 密钥
* @param data 需要加密的数据
* @param mode 工作模式
* @param pad 填充方式
* @return 加密后的数据
* @throws Exception
*/
public static byte[] encrypt(byte[] key, byte[] data, String mode, String pad) throws Exception {
Cipher cipher = Cipher.getInstance(String.format("%s/%s/%s", ALGORITHM, mode, pad));
cipher.init(Cipher.ENCRYPT_MODE, buildKey(key));
return cipher.doFinal(data);
}
public static byte[] decrypt(byte[] key, byte[] data) throws Exception {
return decrypt(key, data, MODE.ECB.name(), PADDING.PKCS5Padding.name());
}
/**
* @param key 密钥
* @param data 需要解密的数据
* @param mode 工作模式
* @param pad 填充方式
* @return 解密后的数据
* @throws Exception
*/
public static byte[] decrypt(byte[] key, byte[] data, String mode, String pad) throws Exception {
Cipher cipher = Cipher.getInstance(String.format("%s/%s/%s", ALGORITHM, mode, pad));
cipher.init(Cipher.DECRYPT_MODE, buildKey(key));
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
byte[] key = AesUtils.initKey(128);
System.out.println("密钥:" + Base64.encodeBase64String(key));
byte[] data = AesUtils.encrypt(key, ("高级加密标准(`Advanced Encryption Standard`:AES),又称Rijndael加密法," +
"是美国联邦政府采用的一种区块加密标准").getBytes());
System.out.println("密文:" + Base64.encodeBase64String(data));
byte[] result = AesUtils.decrypt(key, data);
System.out.println("明文:" + new String(result));
}
}
java bc 实现
package crypto.aes;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
/**
* 对称加密 AES算法 bouncycastle实现
*/
public class AesByBcUtils extends AesUtils {
static {
Security.addProvider(new BouncyCastleProvider());
}
private static final String ALGORITHM = "AES";
private static final String PROVIDER = "BC";
public static byte[] initKey() throws NoSuchAlgorithmException, NoSuchProviderException {
return initKey(128);
}
/**
* 初始化密钥
*
* @param keySIze 128 192 256
*/
public static byte[] initKey(int keySIze) throws NoSuchAlgorithmException, NoSuchProviderException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM, PROVIDER);
keyGenerator.init(keySIze);// 密钥长度
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
public static void main(String[] args) throws Exception {
byte[] key = AesByBcUtils.initKey(128);
System.out.println("密钥:" + Base64.encodeBase64String(key));
byte[] data = AesByBcUtils.encrypt(key, "你好 world".getBytes());
System.out.println("密文:" + Base64.encodeBase64String(data));
byte[] result = AesByBcUtils.decrypt(key, data);
System.out.println("明文:" + new String(result));
}
}