创建加密 log4j appender 时使用什么算法
What algorithm to use when creating an encrypting log4j appender
我创建了一个 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(false)
创建适当的解密流。
问题是我们的安全团队正在为密钥管理争论不休。
他们不喜欢使用对称密钥加密,更希望我们使用密钥对而不是我们必须以某种方式管理的简单密码。
有谁知道使用密钥对的非填充 ECB 加密技术,适用于这种流加密和解密?
建议使用混合加密和 PGP 的评论是正确的。
PGP 是文件混合加密的事实标准,它是一种比 ECB 模式 AES 更强大的解决方案。
由于 PGP 的性质,它的工作方式将与您现有的解决方案略有不同。
PGP 消息有页眉和页脚,因此您希望每个文件都单独加密(您不能像使用普通 ECB 模式加密那样只解密单个块)。
看来您使用的是 log4j 1.2,I have created a working implementation of a PGP encrypting RollingFileAppender
.
要使用该示例,请生成装甲编码的 PGP public 密钥,运行 主要 class 然后使用任何 PGP 工具解密文件(我使用 GnuPG 创建密钥和解密)。
该示例是针对 log4j:log4j:1.2.17
和 org.bouncycastle:bcpg-jdk15on:1.56
构建的
您已经非常接近您想要实现的目标了。使用 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());
}
}
我创建了一个 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(false)
创建适当的解密流。
问题是我们的安全团队正在为密钥管理争论不休。 他们不喜欢使用对称密钥加密,更希望我们使用密钥对而不是我们必须以某种方式管理的简单密码。
有谁知道使用密钥对的非填充 ECB 加密技术,适用于这种流加密和解密?
建议使用混合加密和 PGP 的评论是正确的。
PGP 是文件混合加密的事实标准,它是一种比 ECB 模式 AES 更强大的解决方案。
由于 PGP 的性质,它的工作方式将与您现有的解决方案略有不同。
PGP 消息有页眉和页脚,因此您希望每个文件都单独加密(您不能像使用普通 ECB 模式加密那样只解密单个块)。
看来您使用的是 log4j 1.2,I have created a working implementation of a PGP encrypting RollingFileAppender
.
要使用该示例,请生成装甲编码的 PGP public 密钥,运行 主要 class 然后使用任何 PGP 工具解密文件(我使用 GnuPG 创建密钥和解密)。
该示例是针对 log4j:log4j:1.2.17
和 org.bouncycastle:bcpg-jdk15on:1.56
您已经非常接近您想要实现的目标了。使用 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());
}
}