Rumah >Java >javaTutorial >Cara melaksanakan operasi penyulitan dan penyahsulitan AES dalam Java

Cara melaksanakan operasi penyulitan dan penyahsulitan AES dalam Java

WBOY
WBOYke hadapan
2023-05-08 22:34:162755semak imbas

1. Pengetahuan latar belakang

Cara melaksanakan operasi penyulitan dan penyahsulitan AES dalam Java

  • Dalam kriptografi, algoritma penyulitan dibahagikan kepada penyulitan sehala dan penyulitan dua hala.

    • Penyulitan simetri bermakna penyulitan dan penyahsulitan menggunakan kunci yang sama, termasuk penyulitan AES, penyulitan DES, dsb.

    • Penyulitan asimetri bermaksud penyulitan dan penyahsulitan menggunakan kunci yang berbeza, termasuk penyulitan RSA, dsb.

    • Penyulitan sehala termasuk algoritma ringkasan seperti MD5 dan SHA, yang tidak boleh diterbalikkan.

    • Penyulitan dua hala termasuk penyulitan simetri dan penyulitan asimetri. Penyulitan dua hala boleh diterbalikkan, dan kunci kepada teks sifir wujud.

2 Pengenalan kepada AES

AES: Standard Penyulitan Lanjutan diterima pakai oleh kerajaan persekutuan A.S. penyulitan blok standard, salah satu yang paling popular pada masa ini对称加密算法.

  • ialah algoritma penyulitan blok generasi baharu yang digunakan untuk menggantikan DES.

  • AES menyokong tiga panjang kunci: 128 bit, 192 bit dan 256 bit.

3. Proses penyulitan AES (unit pemprosesan AES: bait)

Proses penyulitan dan penyahsulitan AES adalah sama seperti DES, kedua-duanya melalui 分组加密 dan kumpulan penyahsulitan . Penyulitan blok yang dipanggil adalah untuk mengumpulkan kandungan yang akan disulitkan dan dinyahsulitkan kepada kumpulan mengikut 128 bit, mengumpulkan kunci ke dalam kumpulan mengikut 128 bit, 192 bit, dan 256 bit, dan menyulitkan dan menyahsulit teks biasa yang dikumpulkan dan yang sepadan. kunci berkumpulan masing-masing.

Penyulitan: Selepas teks biasa dan kunci dikumpulkan, untuk setiap kumpulan: kumpulan teks biasa dan pemprosesan kumpulan kunci-> 10 pusingan penyulitan->

Penyahsulitan: Untuk setiap kumpulan: Kumpulan teks sifir -> Penambahan kunci bulat ->

Setiap kumpulan mempunyai panjang yang sama, 128 bit (16 bait);

Kumpulan utama:

Terdapat 128 bit, 192 bit dan 256 bit bilangan pusingan penyulitan 10, 12, 14 masing-masing

Pemprosesan kumpulan utama:

Mengambil kumpulan kunci 128 bit setiap satu sebagai contoh (bilangan pusingan penyulitan yang disyorkan ialah 10, dan operasi adalah sama untuk 9 kali pertama, Masa kesepuluh adalah berbeza) Begitu juga, kunci 128-bit juga diwakili oleh matriks dalam bait Melalui fungsi susunan kunci, urutan 44 elemen terbentuk W[0],W[1], … , W[43] (setiap elemen ialah 4 bait antaranya, W[0], W[1], W[2], W[3] ialah kunci asal, dan baki 40 elemen dibahagikan kepada 10 kumpulan, setiap elemen Kumpulan 4 (4*4=16 bait), yang digunakan untuk 10 pusingan penyulitan masing-masing.

Algoritma penyulitan AES melibatkan 4 operasi:

(SubBytes), (ShiftRows), (MixColumns) dan 字节替代 (AddRoundKey). 行移位列混淆Rajah berikut menunjukkan proses penyulitan dan penyahsulitan AES: 轮密钥加

    AddRoundKey (tambahan kunci bulat)
  • — Setiap perkataan dalam matriks Setiap subkunci adalah XORed dengan kunci bulat; setiap subkunci dijana oleh skema penjanaan kunci.

  • SubBytes (penggantian bait)
  • — Gantikan setiap bait dengan bait yang sepadan menggunakan jadual carian melalui fungsi penggantian bukan linear.

  • ShiftRows (anjakan baris)
  • — Alihkan pekeliling setiap lajur dalam matriks.

  • MixColumns (kecelaruan lajur)
  • — Untuk mencampurkan sepenuhnya operasi setiap baris dalam matriks. Langkah ini menggunakan transformasi linear untuk menggabungkan empat bait setiap lajur.

Andaikan panjang sekeping plaintext ialah 192 bit, dan jika ia dipecahkan kepada satu blok plaintext setiap 128 bit, blok plaintext kedua hanya akan mempunyai 64 bit, iaitu kurang daripada 128 bit. Apa yang perlu dilakukan pada masa ini? Ia adalah perlu untuk melapik blok plaintext (Padding). Cara melaksanakan operasi penyulitan dan penyahsulitan AES dalam Java

Padding melibatkan perkara berikut

:

三种填充模式

    : Tiada padding dilakukan, tetapi plaintext mestilah gandaan integer 16 bait.
  • NoPadding

  • (lalai): Jika blok plaintext kurang daripada 16 bait (128bit), bilangan aksara yang sepadan ditambah pada penghujung blok plaintext dan nilai bagi setiap bait adalah sama dengan Bilangan aksara yang hilang.
  • PKCS5Padding

    Contohnya, teks biasa: {1,2,3,4,5,a,b,c,d,e}, jika 6 bait tiada, penyiapannya ialah { 1, 2,3,4,5,a,b,c,d,e,6,6,6,6,6,6}

    : Jika ada adalah beberapa blok plaintext Berdasarkan 16 bait (128bit), bilangan bait yang sepadan ditambah pada penghujung blok plaintext Nilai aksara terakhir adalah sama dengan bilangan aksara yang hilang, dan aksara lain diisi dengan nombor rawak.
  • ISO10126Padding

    Contohnya, teks biasa: {1,2,3,4,5,a,b,c,d,e}, jika 6 bait tiada, ia mungkin dilengkapkan sebagai {1 ,2,3,4,5,a,b,c,d,e,5,c,3,G,$,6}
4.Pelaksanaan Java

Nota: Penyulitan AES mengandungi penyulitan Base64

Penyulitan:
penyulitan AES-> Teks Sifir

Penyahsulitan: Penyahsulitan Base64-> Penyahsulitan AES->

4.1 Jana kunci dan offset

Jana 16 nombor rawak huruf besar dan huruf kecil dan nombor:

private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 数字和26个字母组成
private static final Random RANDOM = new SecureRandom();
/**
 * 获取长度为 6 的随机字母+数字
 * @return 随机数字
 */
public static String getRandomNumber() {
    char[] nonceChars = new char[16];  //指定长度为6位/自己可以要求设置
    for (int index = 0; index < nonceChars.length; ++index) {
        nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
    }
    return new String(nonceChars);
}

4.2 AESUtil.java kod sumber

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Base64Utils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
 * AES加密工具类
 *
 * @author ACGkaka
 * @since 2021-06-18 19:11:03
 */
public class AESUtil {
    /**
     * 日志相关
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(AESUtil.class);
    /**
     * 编码
     */
    private static final String ENCODING = "UTF-8";
    /**
     * 算法定义
     */
    private static final String AES_ALGORITHM = "AES";
    /**
     * 指定填充方式
     */
    private static final String CIPHER_PADDING = "AES/ECB/PKCS5Padding";
    private static final String CIPHER_CBC_PADDING = "AES/CBC/PKCS5Padding";
    /**
     * 偏移量(CBC中使用,增强加密算法强度)
     */
    private static final String IV_SEED = "1234567812345678";
    /**
     * AES加密
     * @param content 待加密内容
     * @param aesKey  密码
     * @return
     */
    public static String encrypt(String content, String aesKey){
        if(StringUtils.isBlank(content)){
            LOGGER.info("AES encrypt: the content is null!");
            return null;
        }
        //判断秘钥是否为16位
        if(StringUtils.isNotBlank(aesKey) && aesKey.length() == 16){
            try {
                //对密码进行编码
                byte[] bytes = aesKey.getBytes(ENCODING);
                //设置加密算法,生成秘钥
                SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
                // "算法/模式/补码方式"
                Cipher cipher = Cipher.getInstance(CIPHER_PADDING);
                //选择加密
                cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
                //根据待加密内容生成字节数组
                byte[] encrypted = cipher.doFinal(content.getBytes(ENCODING));
                //返回base64字符串
                return Base64Utils.encodeToString(encrypted);
            } catch (Exception e) {
                LOGGER.info("AES encrypt exception:" + e.getMessage());
                throw new RuntimeException(e);
            }
        }else {
            LOGGER.info("AES encrypt: the aesKey is null or error!");
            return null;
        }
    }
    /**
     * 解密
     * 
     * @param content 待解密内容
     * @param aesKey  密码
     * @return
     */
    public static String decrypt(String content, String aesKey){
        if(StringUtils.isBlank(content)){
            LOGGER.info("AES decrypt: the content is null!");
            return null;
        }
        //判断秘钥是否为16位
        if(StringUtils.isNotBlank(aesKey) && aesKey.length() == 16){
            try {
                //对密码进行编码
                byte[] bytes = aesKey.getBytes(ENCODING);
                //设置解密算法,生成秘钥
                SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
                // "算法/模式/补码方式"
                Cipher cipher = Cipher.getInstance(CIPHER_PADDING);
                //选择解密
                cipher.init(Cipher.DECRYPT_MODE, skeySpec);
                //先进行Base64解码
                byte[] decodeBase64 = Base64Utils.decodeFromString(content);
                //根据待解密内容进行解密
                byte[] decrypted = cipher.doFinal(decodeBase64);
                //将字节数组转成字符串
                return new String(decrypted, ENCODING);
            } catch (Exception e) {
                LOGGER.info("AES decrypt exception:" + e.getMessage());
                throw new RuntimeException(e);
            }
        }else {
            LOGGER.info("AES decrypt: the aesKey is null or error!");
            return null;
        }
    }
    /**
     * AES_CBC加密
     * 
     * @param content 待加密内容
     * @param aesKey  密码
     * @return
     */
    public static String encryptCBC(String content, String aesKey){
        if(StringUtils.isBlank(content)){
            LOGGER.info("AES_CBC encrypt: the content is null!");
            return null;
        }
        //判断秘钥是否为16位
        if(StringUtils.isNotBlank(aesKey) && aesKey.length() == 16){
            try {
                //对密码进行编码
                byte[] bytes = aesKey.getBytes(ENCODING);
                //设置加密算法,生成秘钥
                SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
                // "算法/模式/补码方式"
                Cipher cipher = Cipher.getInstance(CIPHER_CBC_PADDING);
                //偏移
                IvParameterSpec iv = new IvParameterSpec(IV_SEED.getBytes(ENCODING));
                //选择加密
                cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
                //根据待加密内容生成字节数组
                byte[] encrypted = cipher.doFinal(content.getBytes(ENCODING));
                //返回base64字符串
                return Base64Utils.encodeToString(encrypted);
            } catch (Exception e) {
                LOGGER.info("AES_CBC encrypt exception:" + e.getMessage());
                throw new RuntimeException(e);
            }
        }else {
            LOGGER.info("AES_CBC encrypt: the aesKey is null or error!");
            return null;
        }
    }
    /**
     * AES_CBC解密
     * 
     * @param content 待解密内容
     * @param aesKey  密码
     * @return
     */
    public static String decryptCBC(String content, String aesKey){
        if(StringUtils.isBlank(content)){
            LOGGER.info("AES_CBC decrypt: the content is null!");
            return null;
        }
        //判断秘钥是否为16位
        if(StringUtils.isNotBlank(aesKey) && aesKey.length() == 16){
            try {
                //对密码进行编码
                byte[] bytes = aesKey.getBytes(ENCODING);
                //设置解密算法,生成秘钥
                SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
                //偏移
                IvParameterSpec iv = new IvParameterSpec(IV_SEED.getBytes(ENCODING));
                // "算法/模式/补码方式"
                Cipher cipher = Cipher.getInstance(CIPHER_CBC_PADDING);
                //选择解密
                cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
                //先进行Base64解码
                byte[] decodeBase64 = Base64Utils.decodeFromString(content);
                //根据待解密内容进行解密
                byte[] decrypted = cipher.doFinal(decodeBase64);
                //将字节数组转成字符串
                return new String(decrypted, ENCODING);
            } catch (Exception e) {
                LOGGER.info("AES_CBC decrypt exception:" + e.getMessage());
                throw new RuntimeException(e);
            }
        }else {
            LOGGER.info("AES_CBC decrypt: the aesKey is null or error!");
            return null;
        }
    }
    public static void main(String[] args) {
        // AES支持三种长度的密钥:128位、192位、256位。
        // 代码中这种就是128位的加密密钥,16字节 * 8位/字节 = 128位。
        String random = RandomStringUtils.random(16, "abcdefghijklmnopqrstuvwxyz1234567890");
        System.out.println("随机key:" + random);
        System.out.println();
        System.out.println("---------加密---------");
        String aesResult = encrypt("测试AES加密12", random);
        System.out.println("aes加密结果:" + aesResult);
        System.out.println();
        System.out.println("---------解密---------");
        String decrypt = decrypt(aesResult, random);
        System.out.println("aes解密结果:" + decrypt);
        System.out.println();
        System.out.println("--------AES_CBC加密解密---------");
        String cbcResult = encryptCBC("测试AES加密12456", random);
        System.out.println("aes_cbc加密结果:" + cbcResult);
        System.out.println();
        System.out.println("---------解密CBC---------");
        String cbcDecrypt = decryptCBC(cbcResult, random);
        System.out.println("aes解密结果:" + cbcDecrypt);
        System.out.println();
    }
}

4.3 Keputusan pelaksanaan

随机key:golrtt58318fx7ol
---------加密---------
aes加密结果:Xy8W9lCeVue9Ao36z+duM7D7WeS5tdBihIMb1q9KpNg=
---------解密---------
aes解密结果:测试AES加密12
--------AES_CBC加密解密---------
aes_cbc加密结果:xs3ypQXyd62P9jB0+RvOqxFnHIHBIlVdqoZLuqYNBLw=
---------解密CBC---------
aes解密结果:测试AES加密12456

4.4 Pengesahan dalam talian

Cara melaksanakan operasi penyulitan dan penyahsulitan AES dalam Java

Atas ialah kandungan terperinci Cara melaksanakan operasi penyulitan dan penyahsulitan AES dalam Java. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam