【问题标题】:What algorithm to use when creating an encrypting log4j appender创建加密 log4j appender 时使用什么算法
【发布时间】:2017-10-02 16:41:57
【问题描述】:

我创建了一个 RollingFileAppender,它将输出加密为 log4j 日志文件。 目前它使用 AES/ECB/NoPadding,效果很好。

这是我们创建密码的方式

public static Cipher getCipher(boolean encrypt) throws Exception {
    //https://en.wikipedia.org/wiki/Stream_cipher       
    byte[] key = ("sometestkey").getBytes("UTF-8");
    MessageDigest sha = MessageDigest.getInstance("SHA-1");
    key = sha.digest(key);
    key = Arrays.copyOf(key, 16); // use only first 128 bit

    Key k = new SecretKeySpec(key,"AES");
    Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
    if (encrypt) {
        cipher.init(Cipher.ENCRYPT_MODE, k);
    } else {
        cipher.init(Cipher.DECRYPT_MODE, k);
    }
    return cipher;
}

以下是我们创建附加程序的方法:

public class EncryptingRollingFileAppender extends RollingFileAppender {
    private CipherOutputStream s;
    private Cipher cipher; 
    public EncryptingRollingFileAppender() {super();}
    public EncryptingRollingFileAppender(Layout layout, String filename, boolean append) throws IOException {super(layout, filename, append);}
    public EncryptingRollingFileAppender(Layout layout, String filename) throws IOException {super(layout, filename);}

    @Override
    protected OutputStreamWriter createWriter(OutputStream outputStream) {
        if (cipher==null) {
            try {
                cipher = DecryptionTools.getCipher(true);
                s = new CipherOutputStream(outputStream, cipher);
            } catch (Throwable t) {
                throw new RuntimeException("failed to initialise encrypting file appender",t);
            }
        }
        OutputStreamWriter out = super.createWriter(s);
        return out;
    }   
}

我们可以使用解密文件

getCipher(假)

创建适当的解密流。

问题是我们的安全团队正在就密钥管理讨价还价。 他们不喜欢使用对称密钥加密,并且希望我们使用密钥对而不是我们必须以某种方式管理的简单密码。

有谁知道使用密钥对的非填充 ECB 加密技术,并且适用于这种流加密和解密?

【问题讨论】:

  • 您可以将hybrid encryption 与非对称和对称加密算法的任意组合一起使用,但您还有其他问题。加密的消息需要某种标头。如何区分同一个日志文件中的多条消息?
  • 您正在写入配置文件?真的吗?
  • @ArtjomB。你有任何Java混合加密的例子吗?更具体地说,我可以让 RC4 使用异步密钥对吗?
  • @Richard Here 是一个例子,但它没有被流化。我不明白你为什么要使用 RC4。
  • 我不太会遵循这个例子。我想使用 RC4,因为它创建的文件与未加密文件的大小相同。换句话说,加密没有额外的有效载荷。这样产生的日志文件将达到每天 60GB。

标签: java encryption cryptography log4j streaming


【解决方案1】:

建议使用混合加密和PGP的cmets是对的。
PGP 是文件混合加密的事实标准,它是一种比 ECB 模式 AES 更强大的解决方案。

由于 PGP 的性质,它的工作方式与您现有的解决方案略有不同。

PGP 消息具有页眉和页脚,因此您希望每个文件都单独加密(您不能像使用纯 ECB 模式加密那样解密单个块)。

您使用的好像是 log4j 1.2,I have created a working implementation of a PGP encrypting RollingFileAppender.

要使用该示例,生成一个装甲编码的 PGP 公钥,运行主类,然后使用任何 PGP 工具解密文件(我使用 GnuPG 创建密钥和解密)。

该示例是针对 log4j:log4j:1.2.17org.bouncycastle:bcpg-jdk15on:1.56 构建的

【讨论】:

  • 谢谢你,太好了。我认为我不能使用 PGP,因为它不允许我们解密仍在写入的文件。 PGP 后的文件大小也比未加密的文件大得多。我认为我需要的是一种将 RC4 与某种巧妙的密钥对系统一起使用的方法。经过大量研究,我认为这是不可能的。相反,我决定使用一个简单的密码将密钥转换为其他东西。然后使用该加密密钥对日志进行加密和解密。然后我只是混淆罐子以隐藏密码。它不安全,但它向前迈进了一步
  • 如果禁用 ascii "Armour" 编码,文件的大小应该或多或少相同(标题有一些固定大小的开销,随着文件大小的增加变得毫无意义,bouncycastle 也有一个压缩选项)。在技​​术上应该可以在编写文件时对其进行解密,但是标准工具可能无法正常工作,但也许可以编写一些代码来做到这一点。我会避免使用 RC4,现在真的没有理由将它用于任何事情,如果你真的想要一个流密码,你可以在 CFB 模式下使用 AES。
  • 但是aes是对称密钥,所以我还是有隐藏密钥的问题。
  • 是的,但是 RC4 也是如此,无论哪种方式,您都会遇到同样的问题。两者都不会解决非对称加密问题。 PGP 是一种使用非对称密码来加密对称密码密钥的方案。您可以制定自己的本土计划来做同样的事情,但我真的不明白这一点。
  • 我们的应用程序安全不希望代码或配置文件中有任何加密密钥。他们不希望我们的系统管理员能够在日志中看到敏感的客户数据。我们(开发者)可以混淆个别敏感领域,但这是一项无止境的任务。我们选择加密日志并为我们的支持团队提供搜索和解密工具。只有支持团队才能解密日志。如果我们使用对称密钥,则很难对 dev 和 ops 隐藏密钥。如果我们使用非对称密钥,似乎没有可用的流密码满足我们对流加密的要求
【解决方案2】:

您已经非常接近您想要实现的目标。使用 Log4j 1.2(因为您不能直接在 Log4j 2 中继承 RollingFileAppender),您可以为每个日志文件动态生成密码,使用 RSA 加密密码并存储在它旁边。然后使用密码为日志追加器生成一个 AES CipherOutputStream。

public class EncryptingRollingFileAppender extends RollingFileAppender {

    private CipherOutputStream s;

    private Cipher cipher;

    private byte[] secretKey;

    public EncryptingRollingFileAppender(Layout layout, String filename, boolean append) throws IOException {
        super(layout, filename, append);
        writeKeyFile(filename);
    }

    public EncryptingRollingFileAppender(Layout layout, String filename) throws IOException {
        super(layout, filename);
        writeKeyFile(filename);
    }

    private void writeKeyFile(final String logfilename) throws IOException {
        final int dot = logfilename.lastIndexOf('.');
        final String keyfilename = (dot == -1 ? logfilename : logfilename.substring(0, dot)) + ".key";
        try (FileOutputStream out = new FileOutputStream(keyfilename)) {
            out.write(DecryptionTools.encryptPasswordBase64(secretKey).getBytes(ISO_8859_1));
        } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | KeyStoreException
                | CertificateException e) {
        }
    }

    @Override
    protected OutputStreamWriter createWriter(OutputStream outputStream) {
        System.out.println("createWriter()");
        if (cipher == null) {
            secretKey = DecryptionTools.generateRandomKey(16).getBytes(ISO_8859_1);
            try {
                cipher = DecryptionTools.getCipher(true, secretKey);
            } catch (InvalidKeyException e) {
                System.out.println("InvalidKeyException");
            }
            s = new CipherOutputStream(outputStream, cipher);
        }

        OutputStreamWriter out = super.createWriter(s);
        return out;
    }
}

您需要几个帮助函数来从文件或 Java 密钥存储中读取私钥,可以在 here 找到。

测试文件TestEncryptingRollingFileAppender 展示了如何编写加密日志并将其读回。

import static com.acme.DecryptionTools.getCipher;
import static com.acme.DecryptionTools.decryptPasswordBase64;

public class TestEncryptingRollingFileAppender {

    @SuppressWarnings("deprecation")
    @Test
    public void testAppender() throws IOException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, KeyStoreException, CertificateException, InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException {

        final File logfile = File.createTempFile("testlog_", ".log");
        final String logfilename = logfile.getAbsolutePath();

        final Logger lggr = LogManager.getLogger(TestEncryptingRollingFileAppender.class);
        final EncryptingRollingFileAppender appender = new EncryptingRollingFileAppender(new SimpleLayout(), logfilename, true);

        appender.append(new LoggingEvent(lggr.getClass().getName(), lggr, Priority.INFO, "Test Log Line #1", null));
        appender.append(new LoggingEvent(lggr.getClass().getName(), lggr, Priority.INFO, "Test Log Line #1", null));

        final int dot = logfilename.lastIndexOf('.');
        byte[] key = decryptPasswordBase64(new String(Files.readAllBytes(Paths.get(logfilename.substring(0, dot)+".key"))));

        StringBuilder logContent = new StringBuilder();
        try (FileInputStream instrm = new FileInputStream(logfilename);
             CipherInputStream cistrm = new CipherInputStream(instrm, getCipher(false, key))) {
            int c;
            while ((c=cistrm.read())!=-1)
                logContent.append((char) c);
        }

        assertEquals("INFO - Test Log Line #1\r\nINFO - Test Log Line #1", logContent.toString());

    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-04
    • 1970-01-01
    • 2020-07-21
    • 2011-08-29
    • 2012-12-10
    相关资源
    最近更新 更多