美文网首页
利用 AES256 算法加解密文件(by Java)

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

作者: GeorgeMR | 来源:发表于2021-10-22 14:42 被阅读0次

    本文要介绍的是基于 Java 语言实现 AES256 加解密文件功能,主要流程包括

    • 读取文件明文数据,通过 AES256 加密算法进行加密,将加密后的数据写回文件
    • 读取文件密文数据,通过 AES256 解密算法进行解密,将解密后的数据写回文件


      AES 文件加解密流程.png

    AES256 算法简介

    AES(高级加密标准,Advanced Encryption Standard),对称加密算法,不同于 RSA 等非对称加密,其只使用一个密钥参与加密和解密。

    密钥

    AES256 中的 256 代表的是密钥的长度为 256位,此外还存在 AES128、AES192,AES256 的安全性最高,AES128性能最高,本质原因是它们的加密处理轮数不同。

    填充

    AES 算法在对明文加密的时候,并不是直接对明文数据进行加密,而是将明文拆分成一个一个独立的明文段,每一段长度为 128bit。然后这些明文段经过 AES 加密器处理,生成密文段,将密文段合并到一起,得到加密结果。
    因为明文段是按照 128bit 长度进行拆分,就会存在长度不足 128bit 的情况,所以需要对不足的明文段进行填充
    Nopadding
    不做任何填充,但是要求铭文必须是 16字节 的整数倍。
    PKCS5Padding
    不足的明文段,在末尾补足相应数量的字符,且每个字节的值等于缺少的字符数。
    ISO10126Padding
    不足的明文段,在末尾补足相应数量的字符,最后一个字符值等于缺少的字符数,其他字符值填充随机数。

    模式

    AES 的工作模式,分为 ECB、CBC、CTR、CFB、OFB。本文使用 CBC 模式,该模式会使用一个初始向量 IV,加密的时候,第一个明文段会首先和初始向量 IV 做异或操作,然后再经过密钥加密,然后第一个密文块又会作为第二个明文段的加密向量来异或,依次类推下去,这样相同的明文段加密出来的密文块就是不同的,因此更加安全。

    文件加密

    本文主要介绍媒体文件的加密,所以不需要对文件的全部数据进行加密,仅仅加密头部部分数据即可,因为没有头部数据,这些媒体文件也是无法成功进行解码。使用部分数据进行加密,从而提高加密性能。

    public static int encryptFile(File file, SecretKey secretKey, int encryptLength) {
            try {
                // 以 byte 的形式读取,不改变文件数据的编码格式
                byte[] bytes = Files.readAllBytes(file.toPath());
    
                // 仅加密 encryptLength 长度的数据
                byte[] substring = new byte[encryptLength];
                System.arraycopy(bytes, 0, substring, 0, encryptLength);
    
                // 加密
                byte[] encrypt = encrypt(substring, secretKey);
    
                // 使用密文替换老数据
                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;
        }
    

    文件解密

    因为数据加密后的长度与明文是不一致的,而文件加密只是部分加密,所以需要记录下密文的长度,从而读取密文,完成解密

    public static void decryptFile(File file, SecretKey secretKey, int decryptLength) {
            try {
                // 以 byte 的形式读取,不改变文件数据的编码格式
                byte[] bytes = Files.readAllBytes(file.toPath());
    
                // 截取密文数据
                byte[] substring = new byte[decryptLength];
                System.arraycopy(bytes, 0, substring, 0, decryptLength);
    
                // 解密
                byte[] decrypt = decrypt(substring, secretKey);
    
                // 使用明文替换加密数据
                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 的形式读取,不改变文件数据的编码格式
                byte[] bytes = Files.readAllBytes(file.toPath());
    
                // 仅加密 encryptLength 长度的数据
                byte[] substring = new byte[encryptLength];
                System.arraycopy(bytes, 0, substring, 0, encryptLength);
    
                // 加密
                byte[] encrypt = encrypt(substring, secretKey);
    
                // 使用密文替换老数据
                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 的形式读取,不改变文件数据的编码格式
                byte[] bytes = Files.readAllBytes(file.toPath());
    
                // 截取密文数据
                byte[] substring = new byte[decryptLength];
                System.arraycopy(bytes, 0, substring, 0, decryptLength);
    
                // 解密
                byte[] decrypt = decrypt(substring, secretKey);
    
                // 使用明文替换加密数据
                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();
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:利用 AES256 算法加解密文件(by Java)

          本文链接:https://www.haomeiwen.com/subject/caocaltx.html