利用 AES256 算法加解密文件(by Java)

本文要介紹的是基于 Java 語言實現(xiàn) AES256 加解密文件功能,主要流程包括

  • 讀取文件明文數(shù)據(jù),通過 AES256 加密算法進行加密,將加密后的數(shù)據(jù)寫回文件
  • 讀取文件密文數(shù)據(jù),通過 AES256 解密算法進行解密,將解密后的數(shù)據(jù)寫回文件


    AES 文件加解密流程.png

AES256 算法簡介

AES(高級加密標(biāo)準(zhǔn),Advanced Encryption Standard),對稱加密算法,不同于 RSA 等非對稱加密,其只使用一個密鑰參與加密和解密。

密鑰

AES256 中的 256 代表的是密鑰的長度為 256位,此外還存在 AES128、AES192,AES256 的安全性最高,AES128性能最高,本質(zhì)原因是它們的加密處理輪數(shù)不同。

填充

AES 算法在對明文加密的時候,并不是直接對明文數(shù)據(jù)進行加密,而是將明文拆分成一個一個獨立的明文段,每一段長度為 128bit。然后這些明文段經(jīng)過 AES 加密器處理,生成密文段,將密文段合并到一起,得到加密結(jié)果。
因為明文段是按照 128bit 長度進行拆分,就會存在長度不足 128bit 的情況,所以需要對不足的明文段進行填充。
Nopadding
不做任何填充,但是要求銘文必須是 16字節(jié) 的整數(shù)倍。
PKCS5Padding
不足的明文段,在末尾補足相應(yīng)數(shù)量的字符,且每個字節(jié)的值等于缺少的字符數(shù)。
ISO10126Padding
不足的明文段,在末尾補足相應(yīng)數(shù)量的字符,最后一個字符值等于缺少的字符數(shù),其他字符值填充隨機數(shù)。

模式

AES 的工作模式,分為 ECB、CBC、CTR、CFB、OFB。本文使用 CBC 模式,該模式會使用一個初始向量 IV,加密的時候,第一個明文段會首先和初始向量 IV 做異或操作,然后再經(jīng)過密鑰加密,然后第一個密文塊又會作為第二個明文段的加密向量來異或,依次類推下去,這樣相同的明文段加密出來的密文塊就是不同的,因此更加安全。

文件加密

本文主要介紹媒體文件的加密,所以不需要對文件的全部數(shù)據(jù)進行加密,僅僅加密頭部部分?jǐn)?shù)據(jù)即可,因為沒有頭部數(shù)據(jù),這些媒體文件也是無法成功進行解碼。使用部分?jǐn)?shù)據(jù)進行加密,從而提高加密性能。

public static int encryptFile(File file, SecretKey secretKey, int encryptLength) {
        try {
            // 以 byte 的形式讀取,不改變文件數(shù)據(jù)的編碼格式
            byte[] bytes = Files.readAllBytes(file.toPath());

            // 僅加密 encryptLength 長度的數(shù)據(jù)
            byte[] substring = new byte[encryptLength];
            System.arraycopy(bytes, 0, substring, 0, encryptLength);

            // 加密
            byte[] encrypt = encrypt(substring, secretKey);

            // 使用密文替換老數(shù)據(jù)
            byte[] newContent = new byte[encrypt.length + bytes.length - encryptLength];
            System.arraycopy(encrypt, 0, newContent, 0, encrypt.length);
            System.arraycopy(bytes, encryptLength, newContent, encrypt.length, bytes.length - encryptLength);

            // 覆蓋寫入文件
            Files.write(file.toPath(), newContent);

            return encrypt.length;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return 0;
    }

加密

private static byte[] encrypt(byte[] content, SecretKey secretKey) {
        byte[] str = null;
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
            str = cipher.doFinal(content);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return str;
    }

文件解密

因為數(shù)據(jù)加密后的長度與明文是不一致的,而文件加密只是部分加密,所以需要記錄下密文的長度,從而讀取密文,完成解密

public static void decryptFile(File file, SecretKey secretKey, int decryptLength) {
        try {
            // 以 byte 的形式讀取,不改變文件數(shù)據(jù)的編碼格式
            byte[] bytes = Files.readAllBytes(file.toPath());

            // 截取密文數(shù)據(jù)
            byte[] substring = new byte[decryptLength];
            System.arraycopy(bytes, 0, substring, 0, decryptLength);

            // 解密
            byte[] decrypt = decrypt(substring, secretKey);

            // 使用明文替換加密數(shù)據(jù)
            byte[] newContent = new byte[decrypt.length + bytes.length - decryptLength];
            System.arraycopy(decrypt, 0, newContent, 0, decrypt.length);
            System.arraycopy(bytes, decryptLength, newContent, decrypt.length, bytes.length - decryptLength);

            // 覆蓋寫入文件
            Files.write(file.toPath(), newContent);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

解密

private static byte[] decrypt(byte[] bytes, SecretKey secretKey) {
        byte[] decryptStr = null;
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
            decryptStr = cipher.doFinal(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return decryptStr;
    }

源碼

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.file.Files;

/**
 * AES 加解密
 */
public class AESEncrypt {
    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
    private static final byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

    /**
     * 生成 SecretKey
     * @param secret
     * @param salt
     * @return
     */
    public static SecretKey generateSecretKey(String secret, String salt) {
        SecretKey secretKey = null;
        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
            PBEKeySpec keySpec = new PBEKeySpec(secret.toCharArray(), salt.getBytes(), 65536, 256);
            secretKey = new SecretKeySpec(factory.generateSecret(keySpec).getEncoded(), "AES");
        } catch (Exception e) {
            e.printStackTrace();
        }

        return secretKey;
    }

    /**
     * 加密
     * @param content
     * @param secretKey
     * @return
     */
    private static byte[] encrypt(byte[] content, SecretKey secretKey) {
        byte[] str = null;
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
            str = cipher.doFinal(content);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return str;
    }

    /**
     * 解密
     * @param bytes
     * @param secretKey
     * @return
     */
    private static byte[] decrypt(byte[] bytes, SecretKey secretKey) {
        byte[] decryptStr = null;
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
            decryptStr = cipher.doFinal(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return decryptStr;
    }

    /**
     * 文件加密
     * @param file
     * @param secretKey
     */
    public static int encryptFile(File file, SecretKey secretKey, int encryptLength) {
        try {
            // 以 byte 的形式讀取,不改變文件數(shù)據(jù)的編碼格式
            byte[] bytes = Files.readAllBytes(file.toPath());

            // 僅加密 encryptLength 長度的數(shù)據(jù)
            byte[] substring = new byte[encryptLength];
            System.arraycopy(bytes, 0, substring, 0, encryptLength);

            // 加密
            byte[] encrypt = encrypt(substring, secretKey);

            // 使用密文替換老數(shù)據(jù)
            byte[] newContent = new byte[encrypt.length + bytes.length - encryptLength];
            System.arraycopy(encrypt, 0, newContent, 0, encrypt.length);
            System.arraycopy(bytes, encryptLength, newContent, encrypt.length, bytes.length - encryptLength);

            // 覆蓋寫入文件
            Files.write(file.toPath(), newContent);

            return encrypt.length;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return 0;
    }

    /**
     * 文件解密
     * @param file
     * @param secretKey
     * @param decryptLength
     */
    public static void decryptFile(File file, SecretKey secretKey, int decryptLength) {
        try {
            // 以 byte 的形式讀取,不改變文件數(shù)據(jù)的編碼格式
            byte[] bytes = Files.readAllBytes(file.toPath());

            // 截取密文數(shù)據(jù)
            byte[] substring = new byte[decryptLength];
            System.arraycopy(bytes, 0, substring, 0, decryptLength);

            // 解密
            byte[] decrypt = decrypt(substring, secretKey);

            // 使用明文替換加密數(shù)據(jù)
            byte[] newContent = new byte[decrypt.length + bytes.length - decryptLength];
            System.arraycopy(decrypt, 0, newContent, 0, decrypt.length);
            System.arraycopy(bytes, decryptLength, newContent, decrypt.length, bytes.length - decryptLength);

            // 覆蓋寫入文件
            Files.write(file.toPath(), newContent);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        try {
            // generate secret key
            SecretKey secretKey = generateSecretKey("password", "salt");

            File file = new File(args[0]);

            long encryptStart = System.currentTimeMillis();
            int encryptLength = encryptFile(file, secretKey, 128);
            long encryptEnd = System.currentTimeMillis();
            System.out.printf("Encrypt %s cost %d%n", args[0], (encryptEnd - encryptStart));

            decryptFile(file, secretKey, encryptLength);
            System.out.printf("Decrypt %s cost %d%n", args[0], (System.currentTimeMillis() - encryptEnd));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容