AES-GCM模式代码演示

2022-12-19  本文已影响0人  滨岩

对称加密与加密模式相关API

image.png

代码演示

GCMAes

package com.deepway.cryptology;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import java.security.*;

/**
 * AES-GCM 
 * @author huyanbing
 * @create 2022/12/18 1:40 下午
 */
public class GCMAes extends BaseCipher {


    // AES-GCM parameters
    /**
     * The length in bytes of IV
     * ivLen>=1
     * But 96-bit IV  values is recommended
     */
    // 12 bytes
    private static final int GCM_NONCE_LENGTH = 96 >> 3;


    /**
     * The length
     * in bits
     * of Tag <code>T</code>;
     * tLen must
     * be one  of {
     * 128, 120, 112, 104, 96
     * }
     * <p>
     * GCM tag 长度 我们一般取最长 128 也是比较推荐的
     */
    private static final int GCM_TAG_LENGTH = 128;// 16 bytes


    private Cipher cipher;

    //GCM的 IV 比较特殊
    private GCMParameterSpec iv;


    /**
     * 构造函数
     *
     * @param keyLen
     * @param plainKey
     */
    public GCMAes(int keyLen, byte[] plainKey) {
        super(keyLen, plainKey);
    }


    /**
     * 初始化
     *
     * @param mode
     * @param nonce
     * @throws GeneralSecurityException
     */
    public void initialize(int mode, byte[] nonce) throws GeneralSecurityException {
        //如果用户没有给 nonce随机数的值,就自己生产初始化的随机值
        if (nonce == null) {
            nonce = new byte[GCM_NONCE_LENGTH];
            new SecureRandom().nextBytes(nonce);
        }
        //构造GCMParameterSpec
        //一个是 tag 长度 ,一个是初始化向量随机数 nonce  然后构成出IV
        //GCM模式只能使用GCMParameterSpec
        iv = new GCMParameterSpec(GCM_TAG_LENGTH, nonce);

        try {
            this.cipher = Cipher.getInstance("AES/GCM/PKCS5PADDING");

            //3-args  初始化 三个参数  mode(加密模式、解密模式)  key:密钥  iv:初始向量
            this.cipher.init(mode, key, iv);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 加密
     * <p>
     * <p>
     * 两个参数 Input  消息本体
     * aad 附加认证数据 aad 数据
     *
     * @param input
     * @param aad
     * @return
     * @throws Exception
     */
    public byte[] encrypt(byte[] input, byte[] aad) throws Exception {
        //first aad
        if (aad != null) {
            //如果有aad 认证数据,需要先添加aad认证数据,然后才能加密消息本体数据
            cipher.updateAAD(aad);
        }
        // cipher.update(input);
        //如果 input 很小,直接调用cipher.doFinal(input) 就可以了
        //jdk9 返回的   gcm->cipher-text,Tag  两个值 一个是加密密文,一个是认证的 128位的tag
        return cipher.doFinal(input);
    }


    public static void main(String[] args) throws Exception {

        byte[] plainText = "Hello AES-GCM  ..11111111111111111111".getBytes();

        System.out.println("GCM plaintText:");

        System.out.println(new String(plainText));


        byte[] aad = "My name is Bob".getBytes();

        GCMAes gcmAlice = new GCMAes(128, null);
        gcmAlice.initialize(Cipher.ENCRYPT_MODE, null);
        byte[] cipherText = gcmAlice.encrypt(plainText, aad);
        System.out.println("GCM cipher:");
        System.out.println(HexCustomUtil.bytes2hex(cipherText));
//
//        {
//            //for RPC ,serializing,persistence
//            new Object() {
//                byte[] aad;
//                byte[] iv;
//                byte[] cipherText;
//            };
//        }

        GCMAes gcmBob = new GCMAes(128, gcmAlice.exportKey());

        gcmBob.initialize(Cipher.DECRYPT_MODE, gcmAlice.iv.getIV());

        byte[] plainText2 = gcmBob.encrypt(cipherText, aad);

        System.out.println("GCM plaintText:");

        System.out.println(new String(plainText2));

    }


}

BaseCipher

package com.deepway.cryptology;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;

/**
 * @author huyanbing
 * @create 2022/12/17 6:44 下午
 */
public class BaseCipher {


    private final int keyLen;
    protected final SecretKey key;

    private String algorithm = "AES";

    /**
     * 构造函数
     *
     * @param keyLen
     * @param plainKey
     */
    public BaseCipher(int keyLen, byte[] plainKey) {
        //验证密钥长度是不是合规的
        assert keyLen == 128 || keyLen == 192 || keyLen == 256;
        this.keyLen = keyLen;
        if (plainKey == null) {
            key = generateKey();
        } else {
            key = importKey(plainKey);
        }
    }


    /**
     * 生成默认密钥
     *
     * @return
     */
    public SecretKey generateKey() {

        try {
            //静态工厂模式
            KeyGenerator keyGen = KeyGenerator.getInstance(algorithm);
            keyGen.init(keyLen);

            SecretKey key = keyGen.generateKey();
            return key;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 导出密钥
     *
     * @return
     */
    public byte[] exportKey() {
        return key.getEncoded();
    }


    /**
     * 导入密钥
     *
     * @param plainKey
     * @return
     */
    public SecretKey importKey(byte[] plainKey) {


        //=>  keyLen/8
        assert plainKey.length == keyLen >> 3;

        return new SecretKeySpec(plainKey, algorithm);
    }

}

package com.deepway.cryptology;

/**
 * @author huyanbing
 * @create 2022/12/16 3:29 下午
 */
public class HexCustomUtil {

    /*输入16进制byte[]输出16进制字符串*/
    public static String byteArrayToHexStr(byte[] byteArray) {
        if (byteArray == null) {
            return null;
        }
        char[] hexArray = "0123456789ABCDEF".toCharArray();
        char[] hexChars = new char[byteArray.length * 2];
        for (int j = 0; j < byteArray.length; j++) {
            int v = byteArray[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }


    public static String bytes2hex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        String tmp;
        sb.append("[");
        for (byte b : bytes) {
            // 将每个字节与0xFF进行与运算,然后转化为10进制,然后借助于Integer再转化为16进制
            tmp = Integer.toHexString(0xFF & b);
            if (tmp.length() == 1) {
                tmp = "0" + tmp;//只有一位的前面补个0
            }
            sb.append(tmp).append(" ");//每个字节用空格断开
        }
        sb.delete(sb.length() - 1, sb.length());//删除最后一个字节后面对于的空格
        sb.append("]");
        return sb.toString();
    }

}

上一篇下一篇

猜你喜欢

热点阅读