AEAD加密算法Java实现

  • Post author:
  • Post category:java



目录


一、什么是认证加密?


二、什么是带关联数据的认证加密?


认证加密


认证解密


常用AEAD算法


三、AEAD_AES_256_GCM加解密Java实现


一、什么是认证加密?

在消息的传递过程中,既要保持数据的机密性,也要保持数据的完整性。机密性可以依靠信息加密来解决,完整性可以通过消息验证码来检查。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目录下有两个子目录,分别存放不同强度策略文件。

测试结果



版权声明:本文为xinyu10001原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。