Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve encryption support in FileInputStreamCache #14042

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.apache.camel.Message;
import org.apache.camel.StaticService;
import org.apache.camel.StreamCache;
import org.apache.camel.support.jsse.SecureRandomParameters;

/**
* Strategy for using <a href="http://camel.apache.org/stream-caching.html">stream caching</a>.
Expand Down Expand Up @@ -224,6 +225,13 @@ interface SpoolRule {

String getSpoolCipher();

/**
* Sets the parameters used to create a secure random when using encryption.
*/
void setSecureRandomParameters(SecureRandomParameters secureRandomParameters);

SecureRandomParameters getSecureRandomParameters();

/**
* Whether to remove the temporary directory when stopping.
* <p/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.apache.camel.Message;
import org.apache.camel.StreamCache;
import org.apache.camel.spi.StreamCachingStrategy;
import org.apache.camel.support.jsse.SecureRandomParameters;
import org.apache.camel.support.service.ServiceSupport;
import org.apache.camel.util.FilePathResolver;
import org.apache.camel.util.FileUtil;
Expand Down Expand Up @@ -58,6 +59,7 @@ public class DefaultStreamCachingStrategy extends ServiceSupport implements Came
private int spoolUsedHeapMemoryThreshold;
private SpoolUsedHeapMemoryLimit spoolUsedHeapMemoryLimit;
private String spoolCipher;
private SecureRandomParameters secureRandomParameters;
private int bufferSize = IOHelper.DEFAULT_BUFFER_SIZE;
private boolean removeSpoolDirectoryWhenStopping = true;
private final UtilizationStatistics statistics = new UtilizationStatistics();
Expand Down Expand Up @@ -177,6 +179,16 @@ public void setSpoolCipher(String spoolCipher) {
this.spoolCipher = spoolCipher;
}

@Override
public SecureRandomParameters getSecureRandomParameters() {
return secureRandomParameters;
}

@Override
public void setSecureRandomParameters(SecureRandomParameters secureRandomParameters) {
this.secureRandomParameters = secureRandomParameters;
}

@Override
public int getBufferSize() {
return bufferSize;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
Expand Down Expand Up @@ -153,21 +154,21 @@ public void testCacheStreamToFileAndCloseStream() throws Exception {
@Test
public void testCacheStreamToFileAndCloseStreamEncrypted() throws Exception {
// set some stream or 8-bit block cipher transformation name
context.getStreamCachingStrategy().setSpoolCipher("RC4");
context.getStreamCachingStrategy().setSpoolCipher("AES/CBC/PKCS5Padding");

context.start();

CachedOutputStream cos = new CachedOutputStream(exchange);
cos.write(TEST_STRING.getBytes(StandardCharsets.UTF_8));
cos.flush();
cos.close();

File file = testDirectory().toFile();
String[] files = file.list();
assertNotNull(files, "There should be a list of files");
assertEquals(1, files.length, "we should have a temp file");
assertTrue(new File(file, files[0]).length() > 10, "The content is written");

java.io.FileInputStream tmpin = new java.io.FileInputStream(new File(file, files[0]));
FileInputStream tmpin = new FileInputStream(new File(file, files[0]));
String temp = toString(tmpin);
assertTrue(!temp.isEmpty() && !temp.contains("aaa"), "The content is not encrypted");
tmpin.close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ private void pageToFileStream() throws IOException {
// creates a tmp file and a file output stream
currentStream = tempFileManager.createOutputStream(strategy);
bout.writeTo(currentStream);
currentStream.flush();
} finally {
// ensure flag is flipped to file based
inMemory = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,44 +16,59 @@
*/
package org.apache.camel.converter.stream;

import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;

import org.apache.camel.support.jsse.SecureRandomParameters;
import org.apache.camel.util.StringHelper;

/**
* A class to hold a pair of encryption and decryption ciphers.
*/
public class CipherPair {

private final String transformation;
private final Cipher enccipher;
private final SecureRandom random;
private final Key key;
private final byte[] ivp;
private final AlgorithmParameterSpec params;

public CipherPair(String transformation) throws GeneralSecurityException {
public CipherPair(String transformation, SecureRandomParameters secureRandomParameters) throws GeneralSecurityException {
this.transformation = transformation;
this.random = secureRandomParameters != null
? secureRandomParameters.createSecureRandom()
: new SecureRandom();

String a = StringHelper.before(transformation, "/", transformation);

KeyGenerator keygen = KeyGenerator.getInstance(a);
keygen.init(new SecureRandom());
keygen.init(random);
key = keygen.generateKey();
enccipher = Cipher.getInstance(transformation);
enccipher.init(Cipher.ENCRYPT_MODE, key);
ivp = enccipher.getIV();

Cipher cipher = Cipher.getInstance(transformation);
cipher.init(Cipher.ENCRYPT_MODE, key, random);
AlgorithmParameters parameters = cipher.getParameters();
params = parameters != null ? parameters.getParameterSpec(AlgorithmParameterSpec.class) : null;
}

public String getTransformation() {
return transformation;
}

public Cipher getEncryptor() {
return enccipher;
public Cipher createEncryptor() {
try {
Cipher enccipher = Cipher.getInstance(transformation);
enccipher.init(Cipher.ENCRYPT_MODE, key, params, random);
return enccipher;
} catch (GeneralSecurityException e) {
// should not happen
throw new IllegalStateException("Could not instantiate encryptor", e);
}
}

/**
Expand All @@ -63,11 +78,11 @@ public Cipher getEncryptor() {
public Cipher createDecryptor() {
try {
Cipher deccipher = Cipher.getInstance(transformation);
deccipher.init(Cipher.DECRYPT_MODE, key, ivp == null ? null : new IvParameterSpec(ivp));
deccipher.init(Cipher.DECRYPT_MODE, key, params, random);
return deccipher;
} catch (GeneralSecurityException e) {
// should not happen
throw new IllegalStateException("Could not instanciate decryptor", e);
throw new IllegalStateException("Could not instantiate decryptor", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ public FileInputStreamCache(File file) {
this(new TempFileManager(file, true));
}

FileInputStreamCache(TempFileManager closer) {
this.file = closer.getTempFile();
FileInputStreamCache(TempFileManager tempFileManager) {
this.file = tempFileManager.getTempFile();
this.stream = null;
this.ciphers = closer.getCiphers();
this.ciphers = tempFileManager.getCiphers();
this.length = file.length();
this.tempFileManager = closer;
this.tempFileManager = tempFileManager;
this.tempFileManager.add(this);
}

Expand Down Expand Up @@ -178,17 +178,7 @@ private InputStream getInputStream() throws IOException {
private InputStream createInputStream(File file) throws IOException {
InputStream in = new BufferedInputStream(Files.newInputStream(file.toPath(), StandardOpenOption.READ));
if (ciphers != null) {
in = new CipherInputStream(in, ciphers.createDecryptor()) {
boolean closed;

@Override
public void close() throws IOException {
if (!closed) {
super.close();
closed = true;
}
}
};
in = new CipherInputStream(in, ciphers.createDecryptor());
}
return in;
}
Expand Down Expand Up @@ -314,22 +304,12 @@ OutputStream createOutputStream(StreamCachingStrategy strategy) throws IOExcepti
if (ObjectHelper.isNotEmpty(strategy.getSpoolCipher())) {
try {
if (ciphers == null) {
ciphers = new CipherPair(strategy.getSpoolCipher());
ciphers = new CipherPair(strategy.getSpoolCipher(), strategy.getSecureRandomParameters());
}
} catch (GeneralSecurityException e) {
throw new IOException(e.getMessage(), e);
}
out = new CipherOutputStream(out, ciphers.getEncryptor()) {
boolean closed;

@Override
public void close() throws IOException {
if (!closed) {
super.close();
closed = true;
}
}
};
out = new CipherOutputStream(out, ciphers.createEncryptor());
}
outputStream = out;
return out;
Expand Down