目录
一、什么是认证加密?
在消息的传递过程中,既要保持数据的机密性,也要保持数据的完整性。机密性可以依靠信息加密来解决,完整性可以通过消息验证码来检查。Authenticated Encryption (AE,认证加密) 就是这样一种同时解决数据的机密性和完整性的方法。
常用的3种加密和验证组合方案
方案 | 描述 | 典型应用 |
---|---|---|
加密后验证 |
生成两个密钥:加密密钥和MAC密钥 使用加密密钥,加密明文数据,得到密文数据; 使用MAC密钥,计算密文数据的消息验证码; 输出:密文数据和验证码。 |
IPSec协议 |
验证后加密 |
生成两个密钥:MAC密钥和加密密钥 使用MAC密钥,计算明文数据的消息验证码; 使用加密密钥,加密明文数据和验证码,得到密文数据; 输出:密文数据。 |
SSL协议 |
加密并验证 |
生成两个密钥:加密密钥和MAC密钥 使用加密密钥,加密明文数据,得到密文数据; 使用MAC密钥,计算明文数据的消息验证码; 输出:密文数据和验证码 |
SSH协议 |
虽然这些方法有重要协议的支持,但并不意味着它们就是安全的。这些通过组合加密和认证算法的方案存在一些安全问题。相对来说,“加密后验证”方案的安全性要高一些。
二、什么是带关联数据的认证加密?
RFC5116
中定义,Authenticated Encryption with Associated Data (AEAD) 加入了对关联数据的完整性、真实性的检查。
Authenticated Encryption with Associated Data, or AEAD [R02], adds the ability to check the integrity and authenticity of some Associated Data (AD), also called “additional authenticated data”, that is not encrypted.
AEAD算法作为单一密码算法,可以替代加密算法和验证算法,提供多种安全服务。它包括两种操作:认证加密、认证解密。
认证加密
输入
明文数据
密钥:
密钥不得包含在任何其他输入中
仅使用一次的任意或非重复的随机数值
关联数据
输出
密文数据 或 提示:请求的加密操作不能执行
认证解密
输入
密文数据
密钥:
密钥不得包含在任何其他输入中
仅使用一次的任意或非重复的随机数值
关联数据
输出
明文数据 或 失败标识符FAIL
常用AEAD算法
AEAD_AES_GCM
A
dvanced
E
ncryption
S
tandard
(AES)
algorithm in
Galois Counter Mode (GCM)
基于AES 算法GCM模式,根据密钥长度,可分为:AEAD_AES_128_GCM、AEAD_AES_256_GCM等。
AEAD_AES_CCM
A
dvanced
E
ncryption
S
tandard
(AES)
algorithm in
Counter and CBC MAC Mode (CCM)
: 基于AES算法CCM模式,根据密钥长度,可分为:AEAD_AES_128_CCM、AEAD_AES_256_CCM等。
三、AEAD_AES_256_GCM加解密Java实现
Java 实现
import org.apache.commons.codec.binary.Base64;
import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
public class AEADTest {
//字节数组转换成十六进制字符串
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
//加密函数
public static byte[] encrypt(byte[] plainText, SecretKey key, byte[] nonce, byte[] AAD) throws Exception {
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORM);
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), ALGORITHM_AES);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
cipher.updateAAD(AAD);
byte[] cipherText = cipher.doFinal(plainText);
return cipherText;
}
//解密函数
public static byte[] decrypt(byte[] cipherText, SecretKey key, byte[] nonce, byte[] AAD) throws Exception{
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORM);
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), ALGORITHM_AES);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
cipher.updateAAD(AAD);
byte[] plainText = cipher.doFinal(cipherText);
return plainText;
}
public static void main(String[] args) {
try {
String originalText = "Plain text to be encrypted by AEAD-AES-256-GCM in Java";
String AADTest = "Associated Data";
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM_AES);
keyGenerator.init(AES_KEY_SIZE);
SecretKey key = keyGenerator.generateKey();
System.out.println(String.format("Generated key: " + Base64.encodeBase64String(key.getEncoded())));
byte[] nonce = new byte[GCM_IV_LENGTH];
SecureRandom sRandom = new SecureRandom();
sRandom.nextBytes(nonce);
System.out.println("Original Text: " + originalText);
byte[] cipherText = encrypt(originalText.getBytes(), key, nonce, AADTest.getBytes());
System.out.println("Encrypted Text: " + bytesToHex(cipherText));
byte[] plainText = decrypt(cipherText, key, nonce, AADTest.getBytes());
System.out.println("Decrypted Text: " + new String(plainText));
} catch (Exception e) {
e.printStackTrace();
}
}
public static final int GCM_TAG_LENGTH = 16;
public static final int AES_KEY_SIZE = 256;
public static final int GCM_IV_LENGTH = 12;
public static final String CIPHER_TRANSFORM = "AES/GCM/NoPadding";
public static final String ALGORITHM_AES = "AES";
}
生成256位的密钥。
从 Java 版本 8u151 开始,JRE 提供了无限和有限强度策略文件。如果使用的版本是8u151 之前的,需要先安装JCE 无限强度策略文件。
KeyGenerator keyGenerator = KeyGenerator.getInstance(“AES”);
keyGenerator.init(
256
);
SecretKey key = keyGenerator.generateKey();
从版本8u151开始,JAVE_HOME\jre\lib\security\policy目录下有两个子目录,分别存放不同强度策略文件。
测试结果