Java 如何正确使用 CipherOutputStream 对使用 log4j (RollingFileAppender) 创建的日志进行加密和解密

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6608529/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 08:11:54  来源:igfitidea点击:

How to use CipherOutputStream correctly to encrypt and decrypt log created with log4j (RollingFileAppender)

javafilelog4jencryption

提问by ordinarydot

I have a problem in encrypting/decrypting log file generated by log4j's RollingFileAppender. For the encryption I have tried to extend the RollingFileAppender, just call it EncryptedRollingFileAppender. I override the method

我在加密/解密由 log4j 的 RollingFileAppender 生成的日志文件时遇到问题。对于加密,我尝试扩展 RollingFileAppender,只需将其称为 EncryptedRollingFileAppender。我覆盖了方法

setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)

and basically I use CipherOutputStream and Base64OutputStream to encrypt and encode everything written to the output stream. Here's part of the code:

基本上我使用 CipherOutputStream 和 Base64OutputStream 来加密和编码写入输出流的所有内容。这是代码的一部分:

...
setImmediateFlush(true);

FileOutputStream ostream = null;
CipherOutputStream cstream = null;
Base64OutputStream b64stream = null;
try {        
    byte[] keyBytes = "1234123412341234".getBytes();  //example
    final byte[] ivBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
         0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; //example

    final SecretKey key = new SecretKeySpec(keyBytes, "AES");
    final IvParameterSpec IV = new IvParameterSpec(ivBytes);
    final Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, key, IV);

    ostream = new FileOutputStream(fileName, true);
    b64stream = new Base64OutputStream(ostream);
    cstream = new CipherOutputStream(b64stream, cipher);

    } catch(Exception ex) {
        ex.printStackTrace();
    }

Writer cw = createWriter(cstream);
...

And then i decrypt the file with this code:

然后我用这个代码解密文件:

private static void decryptFile(String filename) throws Exception {
    FileInputStream fis = null;
    BufferedReader br = new BufferedReader(new FileReader(filename));

    File file = new File(filename + "-decrypted");
    file.createNewFile();
    Writer out = new OutputStreamWriter(new FileOutputStream(filename + "-decrypted"), "UTF-8");

    String line = null;
    try {
         while (( line = br.readLine()) != null){
             line = decrypt(Base64.decodeBase64(line));
             out.write(line);
         }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (br != null) {
            br.close();
        }
        if (fis != null) {
            fis.close();
        }
        if (out != null) {
            out.close();
        }
    }
}

public static String decrypt(byte[] line) throws Exception {
    byte[] keyBytes = "1234123412341234".getBytes();
    final byte[] ivBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
                0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };

    final SecretKey secretkey = new SecretKeySpec(keyBytes, "AES");
    final IvParameterSpec IV = new IvParameterSpec(ivBytes);
    final Cipher decipher = Cipher.getInstance("AES/CFB8/NoPadding");
    decipher.init(Cipher.DECRYPT_MODE, secretkey, IV);
    final byte[] plainText = decipher.doFinal(line);

    return new String(plainText, "UTF-8").trim();
}

It worked but only partially. Some texts in the result file were decrypted correctly but some others were not. If you're curious, this is what I mean by partially:

它起作用了,但只是部分起作用。结果文件中的一些文本被正确解密,而另一些则没有。如果你很好奇,这就是我所说的部分意思:

07 Jul 11 13:13:13, DEBUG  MrBean.java:checkUserVal????V;??????? - username: squall,password: 4GROmr95Qcf????v?M?7?y?5?@CGO09 ,active: true 

I also have tried changing the algorithm to "DESede" but it was still partially decrypted. Then I tried to use "CBC/PKCS5Padding" in both ends but I got an exception

我也尝试将算法更改为“DESede”,但它仍然被部分解密。然后我尝试在两端都使用“CBC/PKCS5Padding”但我得到了一个例外

javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher

I assume the encryption is not properly padding the input but I wonder why... because when I use the same encryption and decryption algorithms without the CipherOutputStream the padding worked just fine. Anyone can help me to make this works? Any help will be appreciated.

我认为加密没有正确填充输入,但我想知道为什么......因为当我使用相同的加密和解密算法而没有 CipherOutputStream 时,填充工作得很好。任何人都可以帮助我完成这项工作吗?任何帮助将不胜感激。

PS: Sorry for my English, it's not my native language.

PS:对不起,我的英语不是我的母语。

采纳答案by President James K. Polk

Looks pretty good. Are you resetting/initializing the cipher object for every message? You probably don't want to do that, but if you do then you need to think carefully about the structure of the cipher file, because the decryptor needs to know where the message boundaries are.

看起来不错。您是否为每条消息重置/初始化密码对象?您可能不想这样做,但是如果您这样做了,那么您需要仔细考虑密码文件的结构,因为解密器需要知道消息边界在哪里。