diff --git a/src/main/java/org/apache/commons/fileupload/disk/DiskFileItem.java b/src/main/java/org/apache/commons/fileupload/disk/DiskFileItem.java index 02fb010fa4..f97398569c 100644 --- a/src/main/java/org/apache/commons/fileupload/disk/DiskFileItem.java +++ b/src/main/java/org/apache/commons/fileupload/disk/DiskFileItem.java @@ -18,7 +18,6 @@ import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -35,7 +34,8 @@ import org.apache.commons.fileupload.util.Streams; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; -import org.apache.commons.io.output.DeferredFileOutputStream; +import org.apache.commons.io.output.CipheredDeferredFileOutputStream; + /** *

The default implementation of the @@ -138,7 +138,7 @@ public class DiskFileItem /** * Output stream for this item. */ - private transient DeferredFileOutputStream dfos; + private transient CipheredDeferredFileOutputStream dfos; /** * The temporary file to use. @@ -201,7 +201,7 @@ public DiskFileItem(final String fieldName, public InputStream getInputStream() throws IOException { if (!isInMemory()) { - return new FileInputStream(dfos.getFile()); + return dfos.createCipheredInputStream(); } if (cachedContent == null) { @@ -284,7 +284,7 @@ public long getSize() { if (dfos.isInMemory()) { return dfos.getData().length; } - return dfos.getFile().length(); + return dfos.getByteCount(); } /** @@ -308,7 +308,7 @@ public byte[] get() { InputStream fis = null; try { - fis = new FileInputStream(dfos.getFile()); + fis = dfos.createCipheredInputStream(); IOUtils.readFully(fis, fileData); } catch (final IOException e) { fileData = null; @@ -500,7 +500,7 @@ public void setFormField(final boolean state) { public OutputStream getOutputStream() throws IOException { if (dfos == null) { final File outputFile = getTempFile(); - dfos = new DeferredFileOutputStream(sizeThreshold, outputFile); + dfos = new CipheredDeferredFileOutputStream(sizeThreshold, outputFile); } return dfos; } diff --git a/src/main/java/org/apache/commons/fileupload/util/crypto/CipherServiceProvider.java b/src/main/java/org/apache/commons/fileupload/util/crypto/CipherServiceProvider.java new file mode 100644 index 0000000000..7864e79cef --- /dev/null +++ b/src/main/java/org/apache/commons/fileupload/util/crypto/CipherServiceProvider.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.fileupload.util.crypto; + +import javax.crypto.Cipher; + +/** + * Ciphers for temporary file encryption. + */ +public interface CipherServiceProvider { + + CipherProvider getCipherProvider(); + + interface CipherProvider { + + /** + * Cipher for encryption of temporary file. + */ + Cipher getEncryptionCipher(); + + /** + * Cipher for decryption of temporary file. + */ + Cipher getDecryptionCipher(); + } +} diff --git a/src/main/java/org/apache/commons/fileupload/util/crypto/CipherSpiLoader.java b/src/main/java/org/apache/commons/fileupload/util/crypto/CipherSpiLoader.java new file mode 100644 index 0000000000..4d9ee67933 --- /dev/null +++ b/src/main/java/org/apache/commons/fileupload/util/crypto/CipherSpiLoader.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.fileupload.util.crypto; + +import java.util.Iterator; +import java.util.ServiceLoader; + +import org.apache.commons.fileupload.util.crypto.CipherServiceProvider.CipherProvider; + +public final class CipherSpiLoader { + + /** + * This provider provide consequent enrcyption or decyption cipher. There is no effect, if it is null. + */ + private static CipherServiceProvider cipherServiceProvider; + + static { + Iterator iterator = ServiceLoader.load(CipherServiceProvider.class) + .iterator(); + if (iterator.hasNext()) { + cipherServiceProvider = iterator.next(); + } + } + + private CipherSpiLoader() { + } + + public static CipherProvider getCipherProvider() { + return cipherServiceProvider == null ? null : cipherServiceProvider.getCipherProvider(); + } + + public static void changeCipherProvider(CipherServiceProvider myCipherServiceProvider) { + cipherServiceProvider = myCipherServiceProvider; + } +} diff --git a/src/main/java/org/apache/commons/fileupload/util/crypto/package-info.java b/src/main/java/org/apache/commons/fileupload/util/crypto/package-info.java new file mode 100644 index 0000000000..2910c91d2b --- /dev/null +++ b/src/main/java/org/apache/commons/fileupload/util/crypto/package-info.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This package contains an interface which provide ciphers for encription + * and decription. The Util class load one implementation and wrap input or + * outputStreams with cipher. + */ +package org.apache.commons.fileupload.util.crypto; diff --git a/src/main/java/org/apache/commons/io/output/CipheredDeferredFileOutputStream.java b/src/main/java/org/apache/commons/io/output/CipheredDeferredFileOutputStream.java new file mode 100644 index 0000000000..7b66c842df --- /dev/null +++ b/src/main/java/org/apache/commons/io/output/CipheredDeferredFileOutputStream.java @@ -0,0 +1,344 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.io.output; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; + +import org.apache.commons.fileupload.util.crypto.CipherServiceProvider.CipherProvider; +import org.apache.commons.fileupload.util.crypto.CipherSpiLoader; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +/** + * An output stream which will retain data in memory until a specified threshold is reached, and only then commit it to + * disk. If the stream is closed before the threshold is reached, the data will not be written to disk at all. + *

+ * This class originated in FileUpload processing. In this use case, you do not know in advance the size of the file + * being uploaded. If the file is small you want to store it in memory (for speed), but if the file is large you want to + * store it to file (to avoid memory issues). + *

+ */ +public class CipheredDeferredFileOutputStream extends ThresholdingOutputStream { + + /** + * The output stream to which data will be written prior to the threshold being reached. + */ + private ByteArrayOutputStream memoryOutputStream; + + /** + * The output stream to which data will be written at any given time. This will always be one of + * {@code memoryOutputStream} or {@code diskOutputStream}. + */ + private OutputStream currentOutputStream; + + /** + * The file to which output will be directed if the threshold is exceeded. + */ + private File outputFile; + + /** + * The temporary file prefix. + */ + private final String prefix; + + /** + * The temporary file suffix. + */ + private final String suffix; + + /** + * The directory to use for temporary files. + */ + private final File directory; + + /** + * True when close() has been called successfully. + */ + private boolean closed; + + /** + * CipherProvider for temporary file encryption. + */ + private CipherProvider cipherProvider; + + /** + * CipherProvider for temporary file encryption. + */ + private boolean cipherInitialized; + + /** + * Constructs an instance of this class which will trigger an event at the specified threshold, and save data to a + * file beyond that point. The initial buffer size will default to + * {@value AbstractByteArrayOutputStream#DEFAULT_SIZE} bytes which is ByteArrayOutputStream's default buffer size. + * + * @param threshold The number of bytes at which to trigger an event. + * @param outputFile The file to which data is saved beyond the threshold. + */ + public CipheredDeferredFileOutputStream(final int threshold, final File outputFile) { + this(threshold, outputFile, null, null, null, AbstractByteArrayOutputStream.DEFAULT_SIZE); + } + + /** + * Constructs an instance of this class which will trigger an event at the specified threshold, and save data either + * to a file beyond that point. + * + * @param threshold The number of bytes at which to trigger an event. + * @param outputFile The file to which data is saved beyond the threshold. + * @param prefix Prefix to use for the temporary file. + * @param suffix Suffix to use for the temporary file. + * @param directory Temporary file directory. + * @param initialBufferSize The initial size of the in memory buffer. + */ + private CipheredDeferredFileOutputStream(final int threshold, final File outputFile, final String prefix, + final String suffix, final File directory, final int initialBufferSize) { + super(threshold); + this.outputFile = outputFile; + this.prefix = prefix; + this.suffix = suffix; + this.directory = directory; + + memoryOutputStream = new ByteArrayOutputStream(initialBufferSize); + currentOutputStream = memoryOutputStream; + } + + /** + * Constructs an instance of this class which will trigger an event at the specified threshold, and save data to a + * file beyond that point. + * + * @param threshold The number of bytes at which to trigger an event. + * @param initialBufferSize The initial size of the in memory buffer. + * @param outputFile The file to which data is saved beyond the threshold. + * + * @since 2.5 + */ + public CipheredDeferredFileOutputStream(final int threshold, final int initialBufferSize, final File outputFile) { + this(threshold, outputFile, null, null, null, initialBufferSize); + if (initialBufferSize < 0) { + throw new IllegalArgumentException("Initial buffer size must be atleast 0."); + } + } + + /** + * Constructs an instance of this class which will trigger an event at the specified threshold, and save data to a + * temporary file beyond that point. + * + * @param threshold The number of bytes at which to trigger an event. + * @param initialBufferSize The initial size of the in memory buffer. + * @param prefix Prefix to use for the temporary file. + * @param suffix Suffix to use for the temporary file. + * @param directory Temporary file directory. + * + * @since 2.5 + */ + public CipheredDeferredFileOutputStream(final int threshold, final int initialBufferSize, final String prefix, + final String suffix, final File directory) { + this(threshold, null, prefix, suffix, directory, initialBufferSize); + if (prefix == null) { + throw new IllegalArgumentException("Temporary file prefix is missing"); + } + if (initialBufferSize < 0) { + throw new IllegalArgumentException("Initial buffer size must be atleast 0."); + } + } + + /** + * Constructs an instance of this class which will trigger an event at the specified threshold, and save data to a + * temporary file beyond that point. The initial buffer size will default to 32 bytes which is + * ByteArrayOutputStream's default buffer size. + * + * @param threshold The number of bytes at which to trigger an event. + * @param prefix Prefix to use for the temporary file. + * @param suffix Suffix to use for the temporary file. + * @param directory Temporary file directory. + * + * @since 1.4 + */ + public CipheredDeferredFileOutputStream(final int threshold, final String prefix, final String suffix, + final File directory) { + this(threshold, null, prefix, suffix, directory, AbstractByteArrayOutputStream.DEFAULT_SIZE); + if (prefix == null) { + throw new IllegalArgumentException("Temporary file prefix is missing"); + } + } + + /** + * Closes underlying output stream, and mark this as closed. + * + * @throws IOException if an error occurs. + */ + @Override + public void close() throws IOException { + super.close(); + closed = true; + } + + /** + * Returns the data for this output stream as an array of bytes, assuming that the data has been retained in memory. + * If the data was written to disk, this method returns {@code null}. + * + * @return The data for this output stream, or {@code null} if no such data is available. + */ + public byte[] getData() { + return memoryOutputStream != null ? memoryOutputStream.toByteArray() : null; + } + + /** + * Returns either the output file specified in the constructor or the temporary file created or null. + *

+ * If the constructor specifying the file is used then it returns that same output file, even when threshold has not + * been reached. + *

+ * If constructor specifying a temporary file prefix/suffix is used then the temporary file created once the + * threshold is reached is returned If the threshold was not reached then {@code null} is returned. + * + * @return The file for this output stream, or {@code null} if no such file exists. + */ + public File getFile() { + return outputFile; + } + + /** + * Returns the current output stream. This may be memory based or disk based, depending on the current state with + * respect to the threshold. + * + * @return The underlying output stream. + * + * @throws IOException if an error occurs. + */ + @Override + protected OutputStream getStream() throws IOException { + return currentOutputStream; + } + + /** + * Determines whether or not the data for this output stream has been retained in memory. + * + * @return {@code true} if the data is available in memory; {@code false} otherwise. + */ + public boolean isInMemory() { + return !isThresholdExceeded(); + } + + /** + * Switches the underlying output stream from a memory based stream to one that is backed by disk. This is the point + * at which we realize that too much data is being written to keep in memory, so we elect to switch to disk-based + * storage. + * + * @throws IOException if an error occurs. + */ + @Override + protected void thresholdReached() throws IOException { + if (prefix != null) { + outputFile = File.createTempFile(prefix, suffix, directory); + } + FileUtils.forceMkdirParent(outputFile); + OutputStream fos = new FileOutputStream(outputFile); + if (getCipherProvider() != null) { + fos = new CipherOutputStream(fos, getCipherProvider().getEncryptionCipher()); + } + try { + memoryOutputStream.writeTo(fos); + } catch (final IOException e) { + fos.close(); + throw e; + } + currentOutputStream = fos; + memoryOutputStream = null; + } + + /** + * Gets the current contents of this byte stream as an {@link InputStream}. + * If the data for this output stream has been retained in memory, the + * returned stream is backed by buffers of {@code this} stream, + * avoiding memory allocation and copy, thus saving space and time.
+ * Otherwise, the returned stream will be one that is created from the data + * that has been committed to disk. + * + * @return the current contents of this output stream. + * @throws IOException if this stream is not yet closed or an error occurs. + * @see org.apache.commons.io.output.ByteArrayOutputStream#toInputStream() + * + * @since 2.9.0 + */ + public InputStream toInputStream() throws IOException { + // we may only need to check if this is closed if we are working with a file + // but we should force the habit of closing whether we are working with + // a file or memory. + if (!closed) { + throw new IOException("Stream not closed"); + } + + if (isInMemory()) { + return memoryOutputStream.toInputStream(); + } + return createCipheredInputStream(); + } + + public InputStream createCipheredInputStream() throws IOException { + InputStream is = new FileInputStream(outputFile); + if (getCipherProvider() != null) { + is = new CipherInputStream(is, getCipherProvider().getDecryptionCipher()); + } + return is; + } + + /** + * Writes the data from this output stream to the specified output stream, after + * it has been closed. + * + * @param outputStream output stream to write to. + * @throws NullPointerException if the OutputStream is {@code null}. + * @throws IOException if this stream is not yet closed or an error + * occurs. + */ + public void writeTo(final OutputStream outputStream) throws IOException { + // we may only need to check if this is closed if we are working with a file + // but we should force the habit of closing whether we are working with + // a file or memory. + if (!closed) { + throw new IOException("Stream not closed"); + } + + if (isInMemory()) { + memoryOutputStream.writeTo(outputStream); + } else { + InputStream fis = createCipheredInputStream(); + try { + IOUtils.copy(fis, outputStream); + } finally { + IOUtils.closeQuietly(fis); + } + } + } + + private CipherProvider getCipherProvider() { + if (!cipherInitialized) { + cipherProvider = CipherSpiLoader.getCipherProvider(); + cipherInitialized = true; + } + return cipherProvider; + } +} + diff --git a/src/main/java/org/apache/commons/io/output/package-info.java b/src/main/java/org/apache/commons/io/output/package-info.java new file mode 100644 index 0000000000..1a95f5c79e --- /dev/null +++ b/src/main/java/org/apache/commons/io/output/package-info.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This package is for commons-io put-backed DefferedFileOutputStream. + */ +package org.apache.commons.io.output; diff --git a/src/test/java/org/apache/commons/fileupload/DefaultFileItemTest.java b/src/test/java/org/apache/commons/fileupload/DefaultFileItemTest.java index 890dbae9f1..aed9e11ac7 100644 --- a/src/test/java/org/apache/commons/fileupload/DefaultFileItemTest.java +++ b/src/test/java/org/apache/commons/fileupload/DefaultFileItemTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -27,7 +28,7 @@ import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; - +import org.apache.commons.fileupload.util.crypto.CipherSpiLoader; import org.apache.commons.io.FileUtils; import org.junit.Test; @@ -133,7 +134,8 @@ public void testBelowThreshold() { */ @Test public void testAboveThresholdDefaultRepository() { - doTestAboveThreshold(null); + doTestAboveThreshold(null, false); + doTestAboveThreshold(null, true); } /** @@ -146,7 +148,8 @@ public void testAboveThresholdSpecifiedRepository() throws IOException { final String tempDirName = "testAboveThresholdSpecifiedRepository"; final File tempDir = new File(tempPath, tempDirName); FileUtils.forceMkdir(tempDir); - doTestAboveThreshold(tempDir); + doTestAboveThreshold(tempDir, false); + doTestAboveThreshold(tempDir, true); assertTrue(tempDir.delete()); } @@ -158,7 +161,11 @@ public void testAboveThresholdSpecifiedRepository() throws IOException { * @param repository The directory within which temporary files will be * created. */ - public void doTestAboveThreshold(final File repository) { + public void doTestAboveThreshold(final File repository, boolean withCipher) { + + if (withCipher) { + CipherSpiLoader.changeCipherProvider(new TestCipherServiceProvider()); + } final FileItemFactory factory = createFactory(repository); final String textFieldName = "textField"; final String textFieldValue = "01234567890123456789"; @@ -189,13 +196,23 @@ public void doTestAboveThreshold(final File repository) { final File storeLocation = dfi.getStoreLocation(); assertNotNull(storeLocation); assertTrue(storeLocation.exists()); - assertEquals(storeLocation.length(), testFieldValueBytes.length); + if (withCipher) { + assertNotEquals(storeLocation.length(), testFieldValueBytes.length); + assertEquals(item.getSize(), testFieldValueBytes.length); + } + else { + assertEquals(storeLocation.length(), testFieldValueBytes.length); + } if (repository != null) { assertEquals(storeLocation.getParentFile(), repository); } item.delete(); + + if (withCipher) { + CipherSpiLoader.changeCipherProvider(null); + } } diff --git a/src/test/java/org/apache/commons/fileupload/TestCipherProvider.java b/src/test/java/org/apache/commons/fileupload/TestCipherProvider.java new file mode 100644 index 0000000000..0a5480e6e9 --- /dev/null +++ b/src/test/java/org/apache/commons/fileupload/TestCipherProvider.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.fileupload; + +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.spec.KeySpec; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.fileupload.util.crypto.CipherServiceProvider; + +public class TestCipherProvider implements CipherServiceProvider.CipherProvider { + private static final String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding"; + + private KeySpec keySpec; + private SecretKey tmp; + private SecretKey secret; + private byte[] ivBytes; + + public TestCipherProvider() { + super(); + try { + keySpec = new PBEKeySpec("secret".toCharArray(), "salt".getBytes(), 1, 256); + tmp = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256").generateSecret(keySpec); + secret = new SecretKeySpec(tmp.getEncoded(), "AES"); + } catch (GeneralSecurityException e) { + throw new RuntimeException("TestCipherProvider doesn't initilized!", e); + } + } + + @Override + public Cipher getEncryptionCipher() { + + try { + Cipher ecipher = Cipher.getInstance(CIPHER_TRANSFORMATION); + ecipher.init(Cipher.ENCRYPT_MODE, secret); + AlgorithmParameters params = ecipher.getParameters(); + this.ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV(); + + return ecipher; + } catch (GeneralSecurityException e) { + throw new RuntimeException("Encryption cipher doesn't initilized!", e); + } + } + + @Override + public Cipher getDecryptionCipher() { + + try { + Cipher dcipher = Cipher.getInstance(CIPHER_TRANSFORMATION); + dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes)); + + return dcipher; + + } catch (GeneralSecurityException e) { + throw new RuntimeException("Decryption Cipher doesn't initilized!", e); + } + } +} diff --git a/src/test/java/org/apache/commons/fileupload/TestCipherServiceProvider.java b/src/test/java/org/apache/commons/fileupload/TestCipherServiceProvider.java new file mode 100644 index 0000000000..c975ed2f9e --- /dev/null +++ b/src/test/java/org/apache/commons/fileupload/TestCipherServiceProvider.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.fileupload; + +import org.apache.commons.fileupload.util.crypto.CipherServiceProvider; + +public class TestCipherServiceProvider implements CipherServiceProvider { + + private TestCipherProvider provider = new TestCipherProvider(); + + @Override + public CipherProvider getCipherProvider() { + return provider; + } + +}