加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – 如何附加到AES加密文件

发布时间:2020-12-15 00:20:47 所属栏目:Java 来源:网络整理
导读:我正在写一些生成加密日志文件的记录器.不幸的是,密码术不是我的强项.现在我可以写入文件,然后关闭文件.然后我可以打开它,附加一些消息,再次关闭,解密后,我看到文件中间的填充字节.有没有办法使用加密文件,而不必每次我要附加一些消息解密? 编辑:更多的细
我正在写一些生成加密日志文件的记录器.不幸的是,密码术不是我的强项.现在我可以写入文件,然后关闭文件.然后我可以打开它,附加一些消息,再次关闭,解密后,我看到文件中间的填充字节.有没有办法使用加密文件,而不必每次我要附加一些消息解密?

编辑:更多的细节.当前实现使用CipherOutputStream.据我所知,没有办法寻求使用它.
如果我将控制输出数据大小可以被块大小整除,我可以使用’NoPadding’选项吗?

解决方法

如果您在 CBC mode中使用AES,则可以使用第二个最后一个块作为IV解密最后一个块,这可能只是部分已满,然后再加密最后一个块的明文,然后是新的明文.

这是一个概念证明:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


public class AppendAES {

    public static void appendAES(File file,byte[] data,byte[] key) throws IOException,NoSuchAlgorithmException,NoSuchPaddingException,InvalidKeyException,InvalidAlgorithmParameterException,IllegalBlockSizeException,BadPaddingException {
        RandomAccessFile rfile = new RandomAccessFile(file,"rw");
        byte[] iv = new byte[16];
        byte[] lastBlock = null;
        if (rfile.length() % 16L != 0L) {
            throw new IllegalArgumentException("Invalid file length (not a multiple of block size)");
        } else if (rfile.length() == 16) {
            throw new IllegalArgumentException("Invalid file length (need 2 blocks for iv and data)");
        } else if (rfile.length() == 0L) { 
            // new file: start by appending an IV
            new SecureRandom().nextBytes(iv);
            rfile.write(iv);
            // we have our iv,and there's no prior data to reencrypt
        } else { 
            // file length is at least 2 blocks
            rfile.seek(rfile.length()-32); // second to last block
            rfile.read(iv); // get iv
            byte[] lastBlockEnc = new byte[16]; 
                // last block
                // it's padded,so we'll decrypt it and 
                // save it for the beginning of our data
            rfile.read(lastBlockEnc);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE,new SecretKeySpec(key,"AES"),new IvParameterSpec(iv));
            lastBlock = cipher.doFinal(lastBlockEnc);
            rfile.seek(rfile.length()-16); 
                // position ourselves to overwrite the last block
        } 
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE,new IvParameterSpec(iv));
        byte[] out;
        if (lastBlock != null) { // lastBlock is null if we're starting a new file
            out = cipher.update(lastBlock);
            if (out != null) rfile.write(out);
        }
        out = cipher.doFinal(data);
        rfile.write(out);
        rfile.close();
    }

    public static void decryptAES(File file,OutputStream out,BadPaddingException {
        // nothing special here,decrypt as usual
        FileInputStream fin = new FileInputStream(file);
        byte[] iv = new byte[16];
        if (fin.read(iv) < 16) {
            throw new IllegalArgumentException("Invalid file length (needs a full block for iv)");
        };
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE,new IvParameterSpec(iv));
        byte[] buff = new byte[1<<13]; //8kiB
        while (true) {
            int count = fin.read(buff);
            if (count == buff.length) {
                out.write(cipher.update(buff));
            } else {
                out.write(cipher.doFinal(buff,count));
                break;
            }
        }
        fin.close();
    }

    public static void main(String[] args) throws Exception {
        byte[] key = new byte[]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
        for (int i = 0; i<1000; i++) {
            appendAES(new File("log.aes"),"All work and no play makes Jack a dull boy. ".getBytes("UTF-8"),key);
        }
        decryptAES(new File("log.aes"),new FileOutputStream("plain.txt"),key);
    }

}

我想指出,输出与通过一次运行中加密所产生的输出没有什么不同.这不是加密的自定义形式 – 它是AES / CBC / PKCS5Padding的标准.唯一的实现具体细节是,在空白文件的情况下,我在开始数据之前写了iv.

编辑:使用CipherOutputStream改进(对我的口味)解决方案:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


public class AppendAES {
    public static CipherOutputStream appendAES(File file,SecretKeySpec key) throws IOException,BadPaddingException {
        return appendAES(file,key,null);
    }

    public static CipherOutputStream appendAES(File file,SecretKeySpec key,SecureRandom sr) throws IOException,"rw");
        byte[] iv = new byte[16];
        byte[] lastBlock = null;
        if (rfile.length() % 16L != 0L) {
            throw new IllegalArgumentException("Invalid file length (not a multiple of block size)");
        } else if (rfile.length() == 16) {
            throw new IllegalArgumentException("Invalid file length (need 2 blocks for iv and data)");
        } else if (rfile.length() == 0L) { 
            // new file: start by appending an IV
            if (sr == null) sr = new SecureRandom();
            sr.nextBytes(iv);
            rfile.write(iv);
        } else { 
            // file length is at least 2 blocks
            rfile.seek(rfile.length()-32);
            rfile.read(iv);
            byte[] lastBlockEnc = new byte[16];
            rfile.read(lastBlockEnc);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE,new IvParameterSpec(iv));
            lastBlock = cipher.doFinal(lastBlockEnc);
            rfile.seek(rfile.length()-16);
        } 
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE,new IvParameterSpec(iv));
        byte[] out;
        if (lastBlock != null) {
            out = cipher.update(lastBlock);
            if (out != null) rfile.write(out);
        }
        CipherOutputStream cos = new CipherOutputStream(new FileOutputStream(rfile.getFD()),cipher);
        return cos;
    }

    public static CipherInputStream decryptAES(File file,BadPaddingException {
        FileInputStream fin = new FileInputStream(file);
        byte[] iv = new byte[16];
        if (fin.read(iv) < 16) {
            throw new IllegalArgumentException("Invalid file length (needs a full block for iv)");
        };
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE,new IvParameterSpec(iv));
        CipherInputStream cis = new CipherInputStream(fin,cipher);
        return cis;
    }

    public static void main(String[] args) throws Exception {
        byte[] keyBytes = new byte[]{
            0,15
        };
        SecretKeySpec key = new SecretKeySpec(keyBytes,"AES");

        for (int i = 0; i<100; i++) {
            CipherOutputStream cos = appendAES(new File("log.aes"),key);
            cos.write("All work and no play ".getBytes("UTF-8"));
            cos.write("makes Jack a dull boy.  n".getBytes("UTF-8"));
            cos.close();
        }

        CipherInputStream cis = decryptAES(new File("log.aes"),key);
        BufferedReader bread = new BufferedReader(new InputStreamReader(cis,"UTF-8"));
        System.out.println(bread.readLine());
        cis.close();
    }

}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读