Skip to content

Commit

Permalink
Improve encryption support in FileInputStreamCache
Browse files Browse the repository at this point in the history
  • Loading branch information
gnodet committed May 2, 2024
1 parent 80ac25e commit 0d15332
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 42 deletions.
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

0 comments on commit 0d15332

Please sign in to comment.