diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java b/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java index 0677a577..1d2b2b24 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java @@ -8,14 +8,8 @@ package ch.ethz.seb.sebserver.gbl.async; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; -import java.util.function.Consumer; import java.util.function.Supplier; -import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; @@ -72,41 +66,4 @@ public class AsyncService { momoized); } - public void pipeToOutputStream( - final OutputStream output, - final Consumer consumer) { - - this.asyncRunner.runAsync(() -> { - - PipedOutputStream pout = null; - PipedInputStream pin = null; - try { - pout = new PipedOutputStream(); - pin = new PipedInputStream(pout); - - consumer.accept(pout); - - IOUtils.copyLarge(pin, output); - - pin.close(); - pout.flush(); - pout.close(); - - } catch (final IOException e) { - log.error("Error while pipe stream data: ", e); - } finally { - try { - pin.close(); - } catch (final IOException e1) { - log.error("Failed to close PipedInputStream: ", e1); - } - try { - pout.close(); - } catch (final IOException e1) { - log.error("Failed to close PipedOutputStream: ", e1); - } - } - }); - } - } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Result.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Result.java index 65a1586f..e992ab36 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Result.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Result.java @@ -181,6 +181,12 @@ public final class Result { } } + public void ifPresent(final Consumer consumer) { + if (this.value != null) { + consumer.accept(this.value); + } + } + /** Use this to map a given Result of type T to another Result of type U * within a given mapping function. * diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebConfigCryptor.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebConfigCryptor.java index 6d7591c3..2f564ff8 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebConfigCryptor.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebConfigCryptor.java @@ -10,10 +10,8 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig; import java.io.InputStream; import java.io.OutputStream; -import java.nio.ByteBuffer; import java.util.Set; -import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService.Strategy; /** Interface for a SEB Configuration encryption and decryption strategy. @@ -27,32 +25,14 @@ public interface SebConfigCryptor { * @return Set of strategies a concrete implementation is supporting */ Set strategies(); - /** Encrypt a given SEB configuration plain text representation within the given SebConfigEncryptionContext - * - * @param plainTextConfig SEB configuration plain text representation - * @param context SebConfigEncryptionContext containing additional data if needed - * @return Result of encrypted data within a ByteBuffer or reference to an Exception on error case */ - Result encrypt( - final CharSequence plainTextConfig, - final SebConfigEncryptionContext context); - - /** Decrypt a given encrypted SEB configuration that has been encrypted by one of the supported strategies. - * - * @param cipher the encrypted SEB configuration cipher(text) within a ByteBuffer - * @param context SebConfigEncryptionContext containing additional data if needed - * @return Result of decrypted SEB configuration within a ByteBuffer or reference to an Exception on error case. */ - Result decrypt( - final ByteBuffer cipher, - final SebConfigEncryptionContext context); - void encrypt( - final OutputStream encryptedOutput, - final InputStream plainTextInputStream, + final OutputStream output, + final InputStream input, final SebConfigEncryptionContext context); void decrypt( - final OutputStream plainTextOutput, - final InputStream cipherInputStream, + final OutputStream output, + final InputStream input, final SebConfigEncryptionContext context); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebConfigEncryptionContext.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebConfigEncryptionContext.java index 2ceaaf1d..59d4d8d7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebConfigEncryptionContext.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebConfigEncryptionContext.java @@ -29,8 +29,9 @@ public interface SebConfigEncryptionContext { /** Get a defined Certificate if supported. * + * @param key The key of the Certificate to get * @return a defined Certificate * @throws UnsupportedOperationException if not supported */ - Certificate getCertificate(); + Certificate getCertificate(CharSequence key); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebConfigEncryptionService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebConfigEncryptionService.java index 5b1b5b5f..bfcc759f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebConfigEncryptionService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebConfigEncryptionService.java @@ -10,12 +10,10 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig; import java.io.InputStream; import java.io.OutputStream; -import java.nio.ByteBuffer; import java.security.cert.Certificate; import java.util.function.Function; import java.util.function.Supplier; -import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Utils; public interface SebConfigEncryptionService { @@ -60,21 +58,18 @@ public interface SebConfigEncryptionService { * @param plainTextConfig plainTextConfig plain text SEB Configuration as CharSequence * @return Result of plain text SEB Configuration within a ByteBuffer or a reference to an Exception on error * case */ - Result plainText(CharSequence plainTextConfig); +// void streamPlainData( +// final OutputStream output, +// final InputStream input); - void streamEncryption( + void streamEncrypted( final OutputStream output, final InputStream input, - final Strategy strategy, - final CharSequence password); + SebConfigEncryptionContext context); - Result encryptWithCertificate( - CharSequence plainTextConfig, - Strategy strategy, - Certificate certificate); - - Result decrypt( - ByteBuffer cipher, + void streamDecrypted( + final OutputStream output, + final InputStream input, Supplier passwordSupplier, Function certificateStore); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ZipService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ZipService.java index 42612bcd..1a02ee73 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ZipService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ZipService.java @@ -16,4 +16,5 @@ public interface ZipService { void write(OutputStream out, InputStream in); void read(OutputStream out, InputStream in); + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/NoneEncryptor.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/NoneEncryptor.java new file mode 100644 index 00000000..0c0b4b6d --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/NoneEncryptor.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Set; + +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig; +import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigCryptor; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionContext; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService.Strategy; + +@Lazy +@Component +@WebServiceProfile +public class NoneEncryptor implements SebConfigCryptor { + + private static final Logger log = LoggerFactory.getLogger(NoneEncryptor.class); + + private static final Set STRATEGIES = Utils.immutableSetOf( + Strategy.PLAIN_TEXT); + + @Override + public Set strategies() { + return STRATEGIES; + } + + @Override + @Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME) + public void encrypt( + final OutputStream output, + final InputStream input, + final SebConfigEncryptionContext context) { + + if (log.isDebugEnabled()) { + log.debug("No encryption, write plain input data"); + } + + try { + + IOUtils.copyLarge(input, output); + + input.close(); + output.flush(); + output.close(); + + } catch (final IOException e) { + log.error("Error while streaming plain data to output: ", e); + } finally { + try { + input.close(); + } catch (final IOException e) { + log.error("Failed to close InputStream"); + } + + if (log.isDebugEnabled()) { + log.debug("Finished with no encryption. Close input stream"); + } + } + } + + @Override + @Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME) + public void decrypt( + final OutputStream output, + final InputStream input, + final SebConfigEncryptionContext context) { + + if (log.isDebugEnabled()) { + log.debug("No decryption, read plain input data"); + } + + try { + + IOUtils.copyLarge(input, output); + + input.close(); + output.flush(); + output.close(); + + } catch (final IOException e) { + log.error("Error while streaming plain data to output: ", e); + } finally { + try { + input.close(); + } catch (final IOException e) { + log.error("Failed to close InputStream"); + } + + if (log.isDebugEnabled()) { + log.debug("Finished with no encryption. Close input stream"); + } + } + + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/PasswordEncryptor.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/PasswordEncryptor.java index 48b2e2be..e1b68aaa 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/PasswordEncryptor.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/PasswordEncryptor.java @@ -11,13 +11,12 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.ByteBuffer; import java.util.Set; import org.apache.tomcat.util.http.fileupload.IOUtils; +import org.cryptonode.jncryptor.AES256JNCryptorInputStream; import org.cryptonode.jncryptor.AES256JNCryptorOutputStream; import org.cryptonode.jncryptor.CryptorException; -import org.cryptonode.jncryptor.JNCryptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; @@ -26,7 +25,6 @@ import org.springframework.stereotype.Component; import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; -import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigCryptor; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionContext; @@ -43,35 +41,11 @@ public class PasswordEncryptor implements SebConfigCryptor { Strategy.PASSWORD_PSWD, Strategy.PASSWORD_PWCC); - private final JNCryptor jnCryptor; - - protected PasswordEncryptor(final JNCryptor jnCryptor) { - this.jnCryptor = jnCryptor; - } - @Override public Set strategies() { return STRATEGIES; } - @Override - public Result encrypt(final CharSequence plainTextConfig, final SebConfigEncryptionContext context) { - return Result.tryCatch(() -> { - return ByteBuffer.wrap(this.jnCryptor.encryptData( - Utils.toByteArray(plainTextConfig), - Utils.toCharArray(context.getPassword()))); - }); - } - - @Override - public Result decrypt(final ByteBuffer cipher, final SebConfigEncryptionContext context) { - return Result.tryCatch(() -> { - return ByteBuffer.wrap(this.jnCryptor.decryptData( - Utils.toByteArray(cipher), - Utils.toCharArray(context.getPassword()))); - }); - } - @Override @Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME) public void encrypt( @@ -80,7 +54,7 @@ public class PasswordEncryptor implements SebConfigCryptor { final SebConfigEncryptionContext context) { if (log.isDebugEnabled()) { - log.debug("*** Start streaming asynchronous encryption of SEB exam configuration data"); + log.debug("*** Start streaming asynchronous encryption"); } AES256JNCryptorOutputStream encryptOutput = null; @@ -88,17 +62,17 @@ public class PasswordEncryptor implements SebConfigCryptor { encryptOutput = new AES256JNCryptorOutputStream( output, - Utils.toCharArray(context.getPassword())); + Utils.toCharArray(context.getPassword()), + 10000); IOUtils.copyLarge(input, encryptOutput); - encryptOutput.close(); + input.close(); encryptOutput.flush(); encryptOutput.close(); - output.flush(); } catch (final CryptorException e) { - log.error("Error while trying to stream and encrypt seb exam configuration data: ", e); + log.error("Error while trying to stream and encrypt data: ", e); } catch (final IOException e) { log.error("Error while trying to read/write form/to streams: ", e); } finally { @@ -110,7 +84,7 @@ public class PasswordEncryptor implements SebConfigCryptor { } if (log.isDebugEnabled()) { - log.debug("*** Finish streaming asynchronous encryption of SEB exam configuration data"); + log.debug("*** Finish streaming asynchronous encryption"); } } } @@ -118,11 +92,42 @@ public class PasswordEncryptor implements SebConfigCryptor { @Override @Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME) public void decrypt( - final OutputStream plainTextOutput, - final InputStream cipherInputStream, + final OutputStream output, + final InputStream input, final SebConfigEncryptionContext context) { - // TODO Auto-generated method stub + if (log.isDebugEnabled()) { + log.debug("*** Start streaming asynchronous decryption"); + } + + AES256JNCryptorInputStream encryptInput = null; + try { + + encryptInput = new AES256JNCryptorInputStream( + input, + Utils.toCharArray(context.getPassword())); + + IOUtils.copyLarge(encryptInput, output); + + input.close(); + encryptInput.close(); + output.flush(); + output.close(); + + } catch (final IOException e) { + log.error("Error while trying to read/write form/to streams: ", e); + } finally { + try { + if (encryptInput != null) + encryptInput.close(); + } catch (final IOException e) { + log.error("Failed to close AES256JNCryptorOutputStream: ", e); + } + + if (log.isDebugEnabled()) { + log.debug("*** Finish streaming asynchronous decryption"); + } + } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebClientConfigServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebClientConfigServiceImpl.java index 1c40bd73..f4e8888e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebClientConfigServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebClientConfigServiceImpl.java @@ -8,13 +8,11 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; -import java.nio.ByteBuffer; import java.util.Collection; import java.util.UUID; @@ -31,7 +29,6 @@ import ch.ethz.seb.sebserver.gbl.model.institution.Institution; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; -import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentialService; import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentials; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO; @@ -40,6 +37,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebClientConfigSe import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService.Strategy; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ZipService; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.SebConfigEncryptionServiceImpl.EncryptionContext; @Lazy @Service @@ -147,6 +145,7 @@ public class SebClientConfigServiceImpl implements SebClientConfigService { PipedOutputStream pOut = null; PipedInputStream pIn = null; try { + // zip the plain text final InputStream plainIn = IOUtils.toInputStream(plainTextConfig, "UTF-8"); pOut = new PipedOutputStream(); @@ -157,7 +156,10 @@ public class SebClientConfigServiceImpl implements SebClientConfigService { if (encryptionPassword != null) { passwordEncryption(output, encryptionPassword, pIn); } else { - noEncryption(output, plainTextConfig); + this.sebConfigEncryptionService.streamEncrypted( + output, + pIn, + EncryptionContext.contextOfPlainText()); } } catch (final Exception e) { @@ -177,21 +179,6 @@ public class SebClientConfigServiceImpl implements SebClientConfigService { } } - private void noEncryption( - final OutputStream output, - final String plainTextConfig) throws IOException { - - log.debug("Serve plain text seb configuration with specified header"); - - final ByteBuffer encryptedConfig = this.sebConfigEncryptionService.plainText(plainTextConfig) - .getOrThrow(); - - IOUtils.copyLarge( - new ByteArrayInputStream(Utils.toByteArray(encryptedConfig)), - output); - - } - private void passwordEncryption( final OutputStream output, final CharSequence encryptionPassword, @@ -204,11 +191,12 @@ public class SebClientConfigServiceImpl implements SebClientConfigService { final CharSequence encryptionPasswordPlaintext = this.clientCredentialService .decrypt(encryptionPassword); - this.sebConfigEncryptionService.streamEncryption( + this.sebConfigEncryptionService.streamEncrypted( output, input, - Strategy.PASSWORD_PSWD, - encryptionPasswordPlaintext); + EncryptionContext.contextOf( + Strategy.PASSWORD_PSWD, + encryptionPasswordPlaintext)); if (log.isDebugEnabled()) { log.debug("*** Finished Seb client configuration with password based encryption"); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebConfigEncryptionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebConfigEncryptionServiceImpl.java index 2ba1b4e5..26953b28 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebConfigEncryptionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebConfigEncryptionServiceImpl.java @@ -13,7 +13,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; -import java.nio.ByteBuffer; import java.security.cert.Certificate; import java.util.Arrays; import java.util.Collection; @@ -31,7 +30,6 @@ import org.springframework.stereotype.Service; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; -import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigCryptor; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionContext; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService; @@ -57,27 +55,24 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption } - @Override - public Result plainText(final CharSequence plainTextConfig) { - - if (log.isDebugEnabled()) { - log.debug("No encryption, use plain text with header"); - } - - return Result.tryCatch(() -> { - return addHeader( - Utils.toByteBuffer(plainTextConfig), - Strategy.PLAIN_TEXT); - }); - } +// @Override +// public void streamPlainData( +// final OutputStream output, +// final InputStream input) { +// +// +// getEncryptor(strategy) +// .getOrThrow() +// .encrypt(pout, input, context); +// } @Override - public void streamEncryption( + public void streamEncrypted( final OutputStream output, final InputStream input, - final Strategy strategy, - final CharSequence password) { + final SebConfigEncryptionContext context) { + final Strategy strategy = context.getStrategy(); PipedOutputStream pout = null; PipedInputStream pin = null; try { @@ -91,26 +86,27 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption pout.write(strategy.header); getEncryptor(strategy) .getOrThrow() - .encrypt(pout, - input, - EncryptionContext.contextOf(strategy, password)); + .encrypt(pout, input, context); IOUtils.copyLarge(pin, output); pin.close(); pout.flush(); pout.close(); + output.flush(); } catch (final IOException e) { log.error("Error while stream encrypted data: ", e); } finally { try { - pin.close(); + if (pin != null) + pin.close(); } catch (final IOException e1) { log.error("Failed to close PipedInputStream: ", e1); } try { - pout.close(); + if (pout != null) + pout.close(); } catch (final IOException e1) { log.error("Failed to close PipedOutputStream: ", e1); } @@ -118,95 +114,72 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption } @Override - public Result encryptWithCertificate( - final CharSequence plainTextConfig, - final Strategy strategy, - final Certificate certificate) { - - if (log.isDebugEnabled()) { - log.debug("Certificate encryption with strategy: {}", strategy); - } - - return getEncryptor(strategy) - .flatMap(encryptor -> encryptor.encrypt( - plainTextConfig, - EncryptionContext.contextOf(strategy, certificate))) - .map(bb -> addHeader(bb, strategy)); - } - - @Override - public Result decrypt( - final ByteBuffer cipher, + public void streamDecrypted( + final OutputStream output, + final InputStream input, final Supplier passwordSupplier, final Function certificateStore) { - return verifyStrategy(cipher) - .flatMap(strategy -> decrypt(strategy, cipher, passwordSupplier, certificateStore)); - } + PipedOutputStream pout = null; + PipedInputStream pin = null; + try { + pout = new PipedOutputStream(); + pin = new PipedInputStream(pout); - private Result decrypt( - final Strategy strategy, - final ByteBuffer cipher, - final Supplier passwordSupplier, - final Function certificateStore) { + final Strategy strategy = verifyStrategy(input); - if (log.isDebugEnabled()) { - log.debug("Decryption with strategy: {}", strategy); - } + if (log.isDebugEnabled()) { + log.debug("Password decryption with strategy: {}", strategy); + } - if (strategy == Strategy.PLAIN_TEXT) { - return Result.of(removeHeader(cipher, strategy)); - } + final EncryptionContext context = new EncryptionContext( + strategy, + (passwordSupplier != null) ? passwordSupplier.get() : null, + certificateStore); - return getEncryptor(strategy) - .flatMap(encryptor -> encryptor.decrypt( - removeHeader(cipher, strategy), - (strategy.type == Type.PASSWORD) - ? EncryptionContext.contextOf(strategy, passwordSupplier.get()) - : EncryptionContext.contextOf(strategy, certificateStore))); - } + getEncryptor(strategy) + .getOrThrow() + .decrypt(pout, input, context); - private ByteBuffer addHeader(final ByteBuffer input, final Strategy strategy) { - final ByteBuffer _input = (input == null) ? ByteBuffer.allocate(0) : input; + IOUtils.copyLarge(pin, output); - _input.rewind(); - final ByteBuffer buffer = ByteBuffer.allocate( - SebConfigEncryptionServiceImpl.HEADER_SIZE + - _input.limit()); + pin.close(); + pout.flush(); + pout.close(); + output.flush(); - buffer.put(strategy.header); - buffer.put(_input); - return buffer.asReadOnlyBuffer(); - } - - private ByteBuffer removeHeader(final ByteBuffer input, final Strategy strategy) { - input.rewind(); - final byte[] header = new byte[SebConfigEncryptionServiceImpl.HEADER_SIZE]; - input.get(header); - - if (Arrays.equals(strategy.header, header)) { - final byte[] b = new byte[input.remaining()]; - input.get(b); - return ByteBuffer.wrap(b).asReadOnlyBuffer(); - } else { - input.clear(); - return input.asReadOnlyBuffer(); - } - } - - private Result verifyStrategy(final ByteBuffer cipher) { - cipher.rewind(); - final byte[] header = new byte[HEADER_SIZE]; - cipher.get(header); - //final String headerString = Utils.toString(header); - for (final Strategy s : Strategy.values()) { - if (Arrays.equals(s.header, header)) { - return Result.of(s); + } catch (final IOException e) { + log.error("Error while stream decrypted data: ", e); + } finally { + try { + if (pin != null) + pin.close(); + } catch (final IOException e1) { + log.error("Failed to close PipedInputStream: ", e1); + } + try { + if (pout != null) + pout.close(); + } catch (final IOException e1) { + log.error("Failed to close PipedOutputStream: ", e1); } } + } - log.error("Failed to verify encryption strategy. Fallback to plain text strategy"); - return Result.of(Strategy.PLAIN_TEXT); + private Strategy verifyStrategy(final InputStream input) { + try { + final byte[] header = new byte[HEADER_SIZE]; + input.read(header); + for (final Strategy s : Strategy.values()) { + if (Arrays.equals(s.header, header)) { + return s; + } + } + throw new IllegalStateException("Failed to verify decryption strategy from input stream"); + } catch (final IOException e) { + log.error("Failed to read decryption strategy from input stream"); + throw new IllegalStateException("Failed to verify decryption strategy from input stream"); + } } private Result getEncryptor(final Strategy strategy) { @@ -218,21 +191,20 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption return Result.of(encryptor); } - protected static class EncryptionContext implements SebConfigEncryptionContext { + static class EncryptionContext implements SebConfigEncryptionContext { public final Strategy strategy; public final CharSequence password; - public final Certificate certificate; + public final Function certificateStore; private EncryptionContext( final Strategy strategy, final CharSequence password, - final Certificate certificate, final Function certificateStore) { this.strategy = strategy; this.password = password; - this.certificate = certificate; + this.certificateStore = certificateStore; } @Override @@ -246,18 +218,16 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption } @Override - public Certificate getCertificate() { - return this.certificate; + public Certificate getCertificate(final CharSequence key) { + if (this.certificateStore == null) { + throw new UnsupportedOperationException(); + } + return this.certificateStore.apply(key); } static SebConfigEncryptionContext contextOf(final Strategy strategy, final CharSequence password) { checkPasswordbased(strategy); - return new EncryptionContext(strategy, password, null, null); - } - - static SebConfigEncryptionContext contextOf(final Strategy strategy, final Certificate certificate) { - checkCertificateBased(strategy); - return new EncryptionContext(strategy, null, certificate, null); + return new EncryptionContext(strategy, password, null); } static SebConfigEncryptionContext contextOf( @@ -265,7 +235,7 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption final Function certificateStore) { checkCertificateBased(strategy); - return new EncryptionContext(strategy, null, null, certificateStore); + return new EncryptionContext(strategy, null, certificateStore); } static void checkPasswordbased(final Strategy strategy) { @@ -280,6 +250,10 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption } } + public static SebConfigEncryptionContext contextOfPlainText() { + return new EncryptionContext(Strategy.PLAIN_TEXT, null, null); + } + } } diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/PasswordEncryptorTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/PasswordEncryptorTest.java index 34152854..5858bf11 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/PasswordEncryptorTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/PasswordEncryptorTest.java @@ -10,10 +10,10 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; import static org.junit.Assert.*; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.ByteBuffer; import java.util.Arrays; import org.apache.commons.io.IOUtils; @@ -22,7 +22,6 @@ import org.cryptonode.jncryptor.AES256JNCryptorOutputStream; import org.cryptonode.jncryptor.JNCryptor; import org.junit.Test; -import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionContext; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService.Strategy; @@ -45,7 +44,7 @@ public class PasswordEncryptorTest { public void testUsingPassword() throws Exception { final String config = ""; - final byte[] plaintext = Utils.toByteArray(config);//getRandomBytes(127); + final byte[] plaintext = Utils.toByteArray(config); final String password = "Testing1234"; @@ -63,36 +62,9 @@ public class PasswordEncryptorTest { assertArrayEquals(plaintext, result); } - @Test - public void test1() { - final JNCryptor jnCryptor = new AES256JNCryptor(); - jnCryptor.setPBKDFIterations(10000); - final PasswordEncryptor encryptor = new PasswordEncryptor(jnCryptor); - - final String config = ""; - final String pwd = "password"; - - final SebConfigEncryptionContext context = EncryptionContext.contextOf( - Strategy.PASSWORD_PWCC, - pwd); - - final Result encrypt = encryptor.encrypt(config, context); - assertFalse(encrypt.hasError()); - final ByteBuffer cipher = encrypt.getOrThrow(); - final byte[] byteArray = Utils.toByteArray(cipher); - - final Result decrypt = encryptor.decrypt(cipher, context); - assertFalse(decrypt.hasError()); - - final String decryptedConfig = Utils.toString(decrypt.getOrThrow()); - assertEquals(config, decryptedConfig); - } - @Test public void test2() throws IOException { - final JNCryptor jnCryptor = new AES256JNCryptor(); - jnCryptor.setPBKDFIterations(10000); - final PasswordEncryptor encryptor = new PasswordEncryptor(jnCryptor); + final PasswordEncryptor encryptor = new PasswordEncryptor(); final String config = ""; final String pwd = "password"; @@ -109,14 +81,16 @@ public class PasswordEncryptorTest { final byte[] byteArray = out.toByteArray(); - final Result decrypt = encryptor.decrypt( - ByteBuffer.wrap(byteArray), + final ByteArrayOutputStream out2 = new ByteArrayOutputStream(512); + encryptor.decrypt( + out2, + new ByteArrayInputStream(byteArray), context); - assertFalse(decrypt.hasError()); - final ByteBuffer buffer = decrypt.getOrThrow(); - buffer.rewind(); - final String decryptedConfig = Utils.toString(buffer); + final byte[] byteArray2 = out2.toByteArray(); + assertNotNull(byteArray2); + + final String decryptedConfig = new String(byteArray2, "UTF-8"); assertEquals(config, decryptedConfig); } diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebConfigEncryptionServiceImplTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebConfigEncryptionServiceImplTest.java index 0b983d1b..5e0a4dcd 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebConfigEncryptionServiceImplTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebConfigEncryptionServiceImplTest.java @@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; import static org.junit.Assert.*; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; @@ -17,31 +18,44 @@ import java.util.Arrays; import java.util.List; import org.apache.commons.io.IOUtils; -import org.cryptonode.jncryptor.AES256JNCryptor; -import org.cryptonode.jncryptor.JNCryptor; import org.junit.Test; -import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigCryptor; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService.Strategy; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.SebConfigEncryptionServiceImpl.EncryptionContext; public class SebConfigEncryptionServiceImplTest { @Test - public void testPlainText() { + public void testPlainText() throws IOException { final SebConfigEncryptionServiceImpl sebConfigEncryptionServiceImpl = sebConfigEncryptionServiceImpl(); final String config = ""; - final Result plainText = sebConfigEncryptionServiceImpl.plainText(config); - assertFalse(plainText.hasError()); - final ByteBuffer cipher = plainText.get(); - assertEquals("plnd", Utils.toString(cipher)); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + sebConfigEncryptionServiceImpl + .streamEncrypted( + out, + IOUtils.toInputStream(config, "UTF-8"), + EncryptionContext.contextOfPlainText()); - final Result decrypt = sebConfigEncryptionServiceImpl.decrypt(cipher, null, null); - assertFalse(decrypt.hasError()); - assertEquals("", Utils.toString(decrypt.get())); + final byte[] plainWithHeader = out.toByteArray(); + assertNotNull(plainWithHeader); + assertEquals("plnd", Utils.toString(plainWithHeader)); + + final ByteArrayOutputStream out2 = new ByteArrayOutputStream(512); + sebConfigEncryptionServiceImpl.streamDecrypted( + out2, + new ByteArrayInputStream(plainWithHeader), + null, + null); + + final byte[] byteArray2 = out2.toByteArray(); + assertNotNull(byteArray2); + + final String decryptedConfig = new String(byteArray2, "UTF-8"); + assertEquals(config, decryptedConfig); } @Test @@ -53,11 +67,12 @@ public class SebConfigEncryptionServiceImplTest { final ByteArrayOutputStream out = new ByteArrayOutputStream(1024); - sebConfigEncryptionServiceImpl.streamEncryption( + sebConfigEncryptionServiceImpl.streamEncrypted( out, IOUtils.toInputStream(config, "UTF-8"), - Strategy.PASSWORD_PWCC, - pwd); + EncryptionContext.contextOf( + Strategy.PASSWORD_PWCC, + pwd)); final byte[] byteArray = out.toByteArray(); @@ -65,17 +80,24 @@ public class SebConfigEncryptionServiceImplTest { final ByteBuffer cipher = ByteBuffer.wrap(byteArray); assertTrue(Utils.toString(cipher).startsWith(Utils.toString(Strategy.PASSWORD_PWCC.header))); - final Result decrypt = sebConfigEncryptionServiceImpl.decrypt(cipher, () -> pwd, null); - assertFalse(decrypt.hasError()); - assertEquals("", Utils.toString(decrypt.get())); + final ByteArrayOutputStream out2 = new ByteArrayOutputStream(512); + sebConfigEncryptionServiceImpl.streamDecrypted( + out2, + new ByteArrayInputStream(byteArray), + () -> pwd, + null); + + final byte[] byteArray2 = out2.toByteArray(); + assertNotNull(byteArray2); + + final String decryptedConfig = new String(byteArray2, "UTF-8"); + assertEquals(config, decryptedConfig); } private SebConfigEncryptionServiceImpl sebConfigEncryptionServiceImpl() { - final JNCryptor jnCryptor = new AES256JNCryptor(); - jnCryptor.setPBKDFIterations(10000); - final List encryptors = Arrays.asList( - new PasswordEncryptor(jnCryptor)); + new PasswordEncryptor(), + new NoneEncryptor()); return new SebConfigEncryptionServiceImpl(encryptors); }