- extended J8 mode supplying PBE and GCM based en-/decryption.
- extracted abstract classes (templates)
- extended tests
- use JUnit 5
- typo fixes
git-svn-id: https://svn.apache.org/repos/asf/turbine/fulcrum/trunk/yaafi-crypto@1853652 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index b9e4699..05cb3a9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,9 +54,10 @@
<dependencies>
<!-- testing dependencies -->
<dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.12</version>
+ <!-- to gt junit 5 -->
+ <groupId>org.apache.fulcrum</groupId>
+ <artifactId>fulcrum-testcontainer</artifactId>
+ <version>1.0.8</version>
<scope>test</scope>
</dependency>
</dependencies>
diff --git a/src/java/org/apache/fulcrum/jce/crypto/CryptoParameters.java b/src/java/org/apache/fulcrum/jce/crypto/CryptoParameters.java
index 5c72346..efdac7a 100644
--- a/src/java/org/apache/fulcrum/jce/crypto/CryptoParameters.java
+++ b/src/java/org/apache/fulcrum/jce/crypto/CryptoParameters.java
@@ -49,8 +49,15 @@
* @see https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
*
* PBEWithHmacSHA256AndAES_256/CBC/PKCS5Padding, PBEWithHmacSHA256AndAES_128/CBC/PKCS5Padding
+ * or
+ * GCM
*/
- String ALGORITHM_J8 = "PBEWithHmacSHA256AndAES_256"; //"PBEWithHmacSHA256AndAES_128 ";
+ String ALGORITHM_J8_PBE = "PBEWithHmacSHA256AndAES_256"; //"PBEWithHmacSHA256AndAES_128 ";
+ String ALGORITHM_J8_GCM = "AES/GCM/NoPadding";
+
+ public enum TYPES {
+ PBE, GCM
+ }
/**
* Prefix to decrypted hex hash to get a clue, what to use and what it is.
diff --git a/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryImpl.java b/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryImpl.java
index 30c7ee1..3c92617 100644
--- a/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryImpl.java
+++ b/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryImpl.java
@@ -21,13 +21,10 @@
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.Key;
import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
-import javax.crypto.CipherOutputStream;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
@@ -52,7 +49,7 @@
* @author <a href="mailto:maakus@earthlink.net">Markus Hahn</a>
*/
-public class CryptoStreamFactoryImpl implements CryptoStreamFactory
+public class CryptoStreamFactoryImpl extends CryptoStreamFactoryTemplate implements CryptoStreamFactory
{
/** the salt for the PBE algorithm */
protected byte[] salt;
@@ -80,14 +77,16 @@
* Factory method to get a default instance
* @return an instance of the CryptoStreamFactory
*/
- public synchronized static CryptoStreamFactory getInstance()
+ public static CryptoStreamFactory getInstance()
{
- if( CryptoStreamFactoryImpl.instance == null )
- {
- CryptoStreamFactoryImpl.instance = new CryptoStreamFactoryImpl();
+ synchronized(CryptoStreamFactoryImpl.class ) {
+ if( CryptoStreamFactoryImpl.instance == null )
+ {
+ CryptoStreamFactoryImpl.instance = new CryptoStreamFactoryImpl();
+ }
+
+ return CryptoStreamFactoryImpl.instance;
}
-
- return CryptoStreamFactoryImpl.instance;
}
/**
@@ -125,82 +124,6 @@
}
/**
- * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getInputStream(java.io.InputStream, String)
- */
- public InputStream getInputStream(InputStream is, String decryptionMode) throws GeneralSecurityException, IOException {
-
- InputStream result = null;
-
- if( "auto".equalsIgnoreCase(decryptionMode) )
- {
- result = getSmartInputStream(is);
- }
- else if( "true".equalsIgnoreCase(decryptionMode) )
- {
- result = getInputStream(is);
- }
- else
- {
- result = is;
- }
- return result;
- }
-
- /**
- * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getInputStream(java.io.InputStream, String, char[])
- */
- public InputStream getInputStream(InputStream is, String decryptionMode, char[] password) throws GeneralSecurityException, IOException {
-
- InputStream result = null;
-
- if( "auto".equalsIgnoreCase(decryptionMode) )
- {
- result = getSmartInputStream(is, password);
- }
- else if( "true".equalsIgnoreCase(decryptionMode) )
- {
- result = getInputStream(is, password);
- }
- else
- {
- result = is;
- }
- return result;
- }
-
- /**
- * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getInputStream(java.io.InputStream)
- */
- public InputStream getInputStream( InputStream is )
- throws GeneralSecurityException, IOException
- {
- Cipher cipher = this.createCipher( Cipher.DECRYPT_MODE, PasswordFactory.getInstance().create() );
- return new CipherInputStream( is, cipher );
- }
-
- /**
- * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getInputStream(java.io.InputStream,char[])
- */
- public InputStream getInputStream( InputStream is, char[] password )
- throws GeneralSecurityException, IOException
- {
- Cipher cipher = this.createCipher( Cipher.DECRYPT_MODE, password );
- return new CipherInputStream( is, cipher );
- }
-
- /**
- * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getSmartInputStream(java.io.InputStream)
- */
- public InputStream getSmartInputStream(InputStream is)
- throws GeneralSecurityException, IOException
- {
- return this.getSmartInputStream(
- is,
- PasswordFactory.getInstance().create()
- );
- }
-
- /**
* @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getSmartInputStream(java.io.InputStream,char[])
*/
public InputStream getSmartInputStream(InputStream is, char[] password )
@@ -218,25 +141,6 @@
}
/**
- * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getOutputStream(java.io.OutputStream)
- */
- public OutputStream getOutputStream( OutputStream os )
- throws GeneralSecurityException, IOException
- {
- Cipher cipher = this.createCipher( Cipher.ENCRYPT_MODE, PasswordFactory.getInstance().create() );
- return new CipherOutputStream( os, cipher ); }
-
- /**
- * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getOutputStream(java.io.OutputStream, char[])
- */
- public OutputStream getOutputStream( OutputStream os, char[] password )
- throws GeneralSecurityException, IOException
- {
- Cipher cipher = this.createCipher( Cipher.ENCRYPT_MODE, password );
- return new CipherOutputStream( os, cipher );
- }
-
- /**
* @return Returns the algorithm.
*/
protected String getAlgorithm()
@@ -275,7 +179,7 @@
* @return the key
* @throws GeneralSecurityException creating the key failed
*/
- private Key createKey( char[] password )
+ protected Key createKey( char[] password )
throws GeneralSecurityException
{
SecretKeyFactory keyFactory;
@@ -303,7 +207,7 @@
* @throws GeneralSecurityException creating a cipher failed
* @throws IOException creating a cipher failed
*/
- private Cipher createCipher( int mode, char[] password )
+ protected Cipher createCipher( int mode, char[] password )
throws GeneralSecurityException, IOException
{
Cipher cipher;
diff --git a/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryJ8.java b/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryJ8.java
index 5c73b71..8dc7be6 100644
--- a/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryJ8.java
+++ b/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryJ8.java
@@ -27,7 +27,7 @@
/**
* Interface for creating encrypting/decrypting streams.
*
- * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl </a>
+ * @author <a href="mailto:gk@apache.org">Georg Kallidis </a>
*/
public interface CryptoStreamFactoryJ8 extends CryptoStreamFactory
@@ -44,4 +44,17 @@
*/
OutputStream getOutputStream(InputStream is, OutputStream os, char[] password)
throws GeneralSecurityException, IOException;
+
+ /**
+ * Creates input stream based on the decryption mode
+ * using the default password.
+ *
+ * @param is the input stream to be wrapped
+ * @param decryptionMode the decryption mode (true|false|auto)
+ * @return an decrypting input stream
+ * @throws GeneralSecurityException creating the input stream failed
+ * @throws IOException creating the input stream failed
+ */
+ InputStream getInputStream(InputStream is, char[] password)
+ throws GeneralSecurityException, IOException;
}
diff --git a/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryJ8Impl.java b/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryJ8Impl.java
index 5d465e8..93462eb 100644
--- a/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryJ8Impl.java
+++ b/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryJ8Impl.java
@@ -58,7 +58,7 @@
* @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl </a>
* @author <a href="mailto:maakus@earthlink.net">Markus Hahn</a>
*/
-
+@Deprecated
public final class CryptoStreamFactoryJ8Impl extends CryptoStreamFactoryImpl implements CryptoStreamFactoryJ8
{
@@ -106,7 +106,7 @@
this.salt = generateSalt();
this.count = CryptoParameters.COUNT_J8;
this.providerName = PROVIDERNAME;
- this.algorithm = CryptoParameters.ALGORITHM_J8;
+ this.algorithm = CryptoParameters.ALGORITHM_J8_PBE;
}
/**
@@ -131,7 +131,7 @@
this.salt = salt;
this.count = count;
this.providerName = PROVIDERNAME;
- this.algorithm = CryptoParameters.ALGORITHM_J8;
+ this.algorithm = CryptoParameters.ALGORITHM_J8_PBE;
}
diff --git a/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryJ8Template.java b/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryJ8Template.java
new file mode 100644
index 0000000..31485b4
--- /dev/null
+++ b/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryJ8Template.java
@@ -0,0 +1,172 @@
+package org.apache.fulcrum.jce.crypto;
+
+/*
+ * 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.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.crypto.Cipher;
+
+import org.apache.fulcrum.jce.crypto.CryptoParameters.TYPES;
+import org.apache.fulcrum.jce.crypto.algo.CryptoStreamGCMImpl;
+import org.apache.fulcrum.jce.crypto.algo.CryptoStreamPBEImpl;
+
+/**
+ * Concrete factory for creating encrypting/decrypting streams.
+ *
+ *
+ **/
+public abstract class CryptoStreamFactoryJ8Template extends CryptoStreamFactoryImpl implements CryptoStreamFactoryJ8
+{
+
+ protected static final int SALT_SIZE = 16; //might increase cipher length
+ protected static final int KEY_SIZE = 256;
+
+ /** the default instances */
+ protected static Map<TYPES,CryptoStreamFactoryJ8Template> instances = new ConcurrentHashMap();
+
+ protected AlgorithmParameters algorithmParameters;// used only for debugging
+
+ public CryptoStreamFactoryJ8Template() {
+
+ }
+
+ /**
+ * Factory method to get a default instance
+ * @return an instance of the CryptoStreamFactory
+ */
+ public static CryptoStreamFactoryJ8 getInstance(TYPES type)
+ {
+ synchronized (CryptoStreamFactoryJ8Template.class) {
+ if( !instances.containsKey(type) )
+ {
+ try {
+ instances.put(type,
+ (type.equals(TYPES.PBE))? new CryptoStreamPBEImpl():
+ new CryptoStreamGCMImpl()
+ );
+ } catch (GeneralSecurityException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ return instances.get(type);
+ }
+ }
+
+
+ /**
+ * Constructor
+ *
+ * @param salt the salt for the PBE algorithm
+ * @param count the iteration for PBEParameterSpec
+ * @paramn type {@link TYPES}
+ */
+ public CryptoStreamFactoryJ8Template( byte[] salt, int count, TYPES type)
+ {
+ this.salt = salt;
+ this.count = count;
+ this.providerName = PROVIDERNAME;
+ this.algorithm = type.equals(TYPES.PBE)? CryptoParameters.ALGORITHM_J8_PBE:
+ CryptoParameters.ALGORITHM_J8_GCM;
+ }
+
+
+ /**
+ * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getSmartInputStream(java.io.InputStream)
+ */
+ @Override
+ public InputStream getSmartInputStream(InputStream is)
+ throws GeneralSecurityException, IOException
+ {
+ return this.getSmartInputStream(
+ is,
+ PasswordFactory.getInstance("SHA-256").create()
+ );
+ }
+
+ /**
+ * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getInputStream(java.io.InputStream,char[])
+ */
+ @Override
+ public InputStream getInputStream( InputStream is, char[] password )
+ throws GeneralSecurityException, IOException
+ {
+ byte[] encrypted = this.createCipher( is, Cipher.DECRYPT_MODE, password );
+ InputStream eis = new ByteArrayInputStream(encrypted);
+ return eis;
+ }
+
+
+ @Override
+ public OutputStream getOutputStream(InputStream is, OutputStream os, char[] password)
+ throws GeneralSecurityException, IOException {
+ byte[] encrypted = this.createCipher( is, Cipher.ENCRYPT_MODE, password );
+ InputStream eis = new ByteArrayInputStream(encrypted);
+ StreamUtil.copy(eis, os);
+ return os;
+ }
+
+ /**
+ * Create a PBE key.
+ *
+ * @param password the password to use.
+ * @param salt if provided this is used, otherweise {@link #getSalt()}.
+ * @return the key
+ * @throws GeneralSecurityException creating the key failed
+ */
+ protected abstract Key createKey( char[] password, byte[] salt )
+ throws GeneralSecurityException;
+
+ /**
+ * Create a Cipher.
+ *
+ * @param mode the cipher mode
+ * @param password the password
+ * @return an instance of a cipher
+ * @throws GeneralSecurityException creating a cipher failed
+ * @throws IOException creating a cipher failed
+ */
+ protected abstract byte[] createCipher(InputStream is, int mode, char[] password )
+ throws GeneralSecurityException, IOException;
+
+ protected byte[] generateSalt() throws GeneralSecurityException {
+ SecureRandom random;
+ try {
+ random = SecureRandom.getInstance("SHA1PRNG");
+ byte[] salt = new byte[SALT_SIZE ];
+ random.nextBytes(salt);
+ return salt;
+ } catch (NoSuchAlgorithmException e) {
+ throw new GeneralSecurityException(e);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryTemplate.java b/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryTemplate.java
new file mode 100644
index 0000000..347e218
--- /dev/null
+++ b/src/java/org/apache/fulcrum/jce/crypto/CryptoStreamFactoryTemplate.java
@@ -0,0 +1,167 @@
+package org.apache.fulcrum.jce.crypto;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+
+/**
+ * Concrete factory for creating encrypting/decrypting streams. The
+ * implementation uses the JCA (Java Crypto Extension) supplied
+ * by SUN (using SunJCE 1.42).
+ *
+ * The implementation uses as PBEWithMD5AndDES for encryption which
+ * should be sufficent for most applications.
+ *
+ * The implementation also supplies a default password in the case that
+ * the programmer don't want to have additional hassles. It is easy to
+ * reengineer the password being used but much better than a hard-coded
+ * password in the application.
+ *
+ * The code uses parts from Markus Hahn's Blowfish library found at
+ * http://blowfishj.sourceforge.net/
+ *
+ * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl </a>
+ * @author <a href="mailto:maakus@earthlink.net">Markus Hahn</a>
+ */
+
+public abstract class CryptoStreamFactoryTemplate implements CryptoStreamFactory
+{
+
+ /** the default instance */
+ protected static CryptoStreamFactory instance;
+
+ public static CryptoStreamFactory getInstance() {
+ return instance;
+ }
+
+ public static void setInstance(CryptoStreamFactory instance) {
+ CryptoStreamFactoryTemplate.instance = instance;
+ }
+
+ /**
+ * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getInputStream(java.io.InputStream, String)
+ */
+ public InputStream getInputStream(InputStream is, String decryptionMode) throws GeneralSecurityException, IOException {
+
+ InputStream result = null;
+
+ if( "auto".equalsIgnoreCase(decryptionMode) )
+ {
+ result = getSmartInputStream(is);
+ }
+ else if( "true".equalsIgnoreCase(decryptionMode) )
+ {
+ result = getInputStream(is);
+ }
+ else
+ {
+ result = is;
+ }
+ return result;
+ }
+
+ /**
+ * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getInputStream(java.io.InputStream, String, char[])
+ */
+ public InputStream getInputStream(InputStream is, String decryptionMode, char[] password) throws GeneralSecurityException, IOException {
+
+ InputStream result = null;
+
+ if( "auto".equalsIgnoreCase(decryptionMode) )
+ {
+ result = getSmartInputStream(is, password);
+ }
+ else if( "true".equalsIgnoreCase(decryptionMode) )
+ {
+ result = getInputStream(is, password);
+ }
+ else
+ {
+ result = is;
+ }
+ return result;
+ }
+
+ /**
+ * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getInputStream(java.io.InputStream)
+ */
+ public InputStream getInputStream( InputStream is )
+ throws GeneralSecurityException, IOException
+ {
+ Cipher cipher = this.createCipher( Cipher.DECRYPT_MODE, PasswordFactory.getInstance().create() );
+ return new CipherInputStream( is, cipher );
+ }
+
+ /**
+ * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getInputStream(java.io.InputStream,char[])
+ */
+ public InputStream getInputStream( InputStream is, char[] password )
+ throws GeneralSecurityException, IOException
+ {
+ Cipher cipher = this.createCipher( Cipher.DECRYPT_MODE, password );
+ return new CipherInputStream( is, cipher );
+ }
+
+ /**
+ * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getSmartInputStream(java.io.InputStream)
+ */
+ public InputStream getSmartInputStream(InputStream is)
+ throws GeneralSecurityException, IOException
+ {
+ return this.getSmartInputStream(
+ is,
+ PasswordFactory.getInstance().create()
+ );
+ }
+
+ /**
+ * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getSmartInputStream(java.io.InputStream,char[])
+ */
+ public abstract InputStream getSmartInputStream(InputStream is, char[] password )
+ throws GeneralSecurityException, IOException;
+
+ /**
+ * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getOutputStream(java.io.OutputStream)
+ */
+ public OutputStream getOutputStream( OutputStream os )
+ throws GeneralSecurityException, IOException
+ {
+ Cipher cipher = this.createCipher( Cipher.ENCRYPT_MODE, PasswordFactory.getInstance().create() );
+ return new CipherOutputStream( os, cipher ); }
+
+ /**
+ * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getOutputStream(java.io.OutputStream, char[])
+ */
+ public OutputStream getOutputStream( OutputStream os, char[] password )
+ throws GeneralSecurityException, IOException
+ {
+ Cipher cipher = this.createCipher( Cipher.ENCRYPT_MODE, password );
+ return new CipherOutputStream( os, cipher );
+ }
+ protected abstract Cipher createCipher(int encryptMode, char[] password) throws GeneralSecurityException, IOException;
+
+}
diff --git a/src/java/org/apache/fulcrum/jce/crypto/CryptoUtilJ8.java b/src/java/org/apache/fulcrum/jce/crypto/CryptoUtilJ8.java
index e0d1fe2..3287aa5 100644
--- a/src/java/org/apache/fulcrum/jce/crypto/CryptoUtilJ8.java
+++ b/src/java/org/apache/fulcrum/jce/crypto/CryptoUtilJ8.java
@@ -19,11 +19,14 @@
* under the License.
*/
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.fulcrum.jce.crypto.CryptoParameters.TYPES;
/**
* Helper class to provde generic functions to work with CryptoStreams.
@@ -38,28 +41,59 @@
public final class CryptoUtilJ8 extends CryptoUtil {
- /** the default instance */
- private static CryptoUtilJ8 instance;
+ protected TYPES type;// default see instance
+
+
+ public TYPES getType() {
+ return type;
+ }
+
+ /** the typed default instances */
+ private static Map<TYPES,CryptoUtilJ8> cryptoUtilJ8s = new ConcurrentHashMap();
/**
* Factory method to get a default instance
+ * @param type
* @return an instance of the CryptoStreamFactory
*/
- public synchronized static CryptoUtilJ8 getInstance()
+ public static CryptoUtilJ8 getInstance(TYPES type)
{
- if( CryptoUtilJ8.instance == null )
- {
- CryptoUtilJ8.instance = new CryptoUtilJ8();
+ synchronized (CryptoUtilJ8.class) {
+ if( !cryptoUtilJ8s.containsKey(type) )
+ {
+ cryptoUtilJ8s.put(type, new CryptoUtilJ8(type) );
+ }
+
+ return cryptoUtilJ8s.get(type);
}
-
- return CryptoUtilJ8.instance;
+ }
+
+ /**
+ * Factory method to get a default instance
+ *
+ * default type PDC
+ * @return an instance of the CryptoStreamFactory
+ */
+ public static CryptoUtilJ8 getInstance()
+ {
+ synchronized (CryptoUtilJ8.class) {
+ if( cryptoUtilJ8s.isEmpty() && !cryptoUtilJ8s.containsKey(TYPES.PBE) )
+ {
+ cryptoUtilJ8s.put(TYPES.PBE, new CryptoUtilJ8(TYPES.PBE) );
+ }
+
+ return cryptoUtilJ8s.get(TYPES.PBE);
+ }
+ }
+
+ public CryptoUtilJ8(TYPES type) {
+ this.type = type;
}
public CryptoUtilJ8() {
- useClearTextHeader = true;
}
-
+
/**
* Copies from a source to a target object using encryption and a caller
* supplied CryptoStreamFactory.
@@ -105,6 +139,6 @@
* @return the CryptoStreamFactory to be used
*/
public CryptoStreamFactory getCryptoStreamFactory() {
- return CryptoStreamFactoryJ8Impl.getInstance();
+ return CryptoStreamFactoryJ8Template.getInstance(type);
}
}
diff --git a/src/java/org/apache/fulcrum/jce/crypto/algo/CryptoStreamGCMImpl.java b/src/java/org/apache/fulcrum/jce/crypto/algo/CryptoStreamGCMImpl.java
new file mode 100644
index 0000000..ed0a005
--- /dev/null
+++ b/src/java/org/apache/fulcrum/jce/crypto/algo/CryptoStreamGCMImpl.java
@@ -0,0 +1,211 @@
+package org.apache.fulcrum.jce.crypto.algo;
+
+import java.io.ByteArrayOutputStream;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.fulcrum.jce.crypto.CryptoParameters;
+import org.apache.fulcrum.jce.crypto.CryptoStreamFactoryJ8Template;
+import org.apache.fulcrum.jce.crypto.StreamUtil;
+
+/**
+ * Concrete implementation for creating encrypting/decrypting streams. The
+ * implementation uses the JCA (Java Crypto Extension) supplied
+ * by SUN (using SunJCE 1.42).
+ *
+ * The implementation uses @see {@link CryptoParameters#ALGORITHM_J8_GCM} for encryption which
+ * should be sufficent for most applications.
+ *
+ * The implementation also supplies a default password in the case that
+ * the programmer don't want to have additional hassles. It is easy to
+ * reengineer the password being used but much better than a hard-coded
+ * password in the application.
+ *
+ *
+ * @author <a href="mailto:gk@apache.org">Georg Kallidis</a>
+ */
+
+public final class CryptoStreamGCMImpl extends CryptoStreamFactoryJ8Template
+{
+
+ protected static final int IV_SIZE = 12;
+ /**
+ * Constructor
+ */
+ public CryptoStreamGCMImpl() throws GeneralSecurityException
+ {
+ this.salt = generateSalt();
+ this.count = CryptoParameters.COUNT_J8;// not used
+ this.providerName = PROVIDERNAME;
+ this.algorithm = CryptoParameters.ALGORITHM_J8_GCM;
+ }
+
+
+ /**
+ * Constructor
+ *
+ * @param salt the salt for the PBE algorithm
+ * @param count the iteration for PBEParameterSpec
+ */
+ public CryptoStreamGCMImpl( byte[] salt, int count) throws GeneralSecurityException
+ {
+ this.salt = salt;
+ this.count = count;
+ this.providerName = PROVIDERNAME;
+ this.algorithm = CryptoParameters.ALGORITHM_J8_GCM;
+ }
+
+ /**
+ * Create a AES/GCM key.
+ *
+ * @param password the password to use.
+ * @param salt if provided this is used, otherweise {@link #getSalt()}.
+ * @return the key
+ * @throws GeneralSecurityException creating the key failed
+ */
+ @Override
+ protected Key createKey( char[] password, byte[] salt )
+ throws GeneralSecurityException
+ {
+
+ SecretKey key = new SecretKeySpec(((salt == null)? this.getSalt(): salt), "AES");
+ return key;
+ }
+
+ /**
+ * Create a Cipher.
+ *
+ * @param mode the cipher mode
+ * @param password the password
+ * @return an instance of a cipher
+ * @throws GeneralSecurityException creating a cipher failed
+ * @throws IOException creating a cipher failed
+ */
+ @Override
+ protected byte[] createCipher(InputStream is, int mode, char[] password )
+ throws GeneralSecurityException, IOException
+ {
+ Cipher cipher;
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
+ long total = StreamUtil.copy(is, bos);
+ byte[] input = bos.toByteArray();
+
+ byte[] ciphertext = null;
+ byte[] salt = null;
+ byte[] iv = null;
+
+ if (mode == Cipher.DECRYPT_MODE) {
+
+ ByteBuffer byteBuffer = ByteBuffer.wrap(input);
+ salt = new byte[ SALT_SIZE ];
+ byteBuffer.get(salt);
+ iv = new byte[ IV_SIZE ];
+ byteBuffer.get(iv);
+ ciphertext = new byte[byteBuffer.remaining()];
+ byteBuffer.get(ciphertext);
+
+// salt = Arrays.copyOfRange(input, 0, SALT_SIZE );
+// iv = Arrays.copyOfRange(input, salt.length, salt.length + 16 );
+// ciphertext = Arrays.copyOfRange(input, salt.length + iv.length, input.length);// cut out salt and iv
+ }
+
+ Key key = this.createKey( password, salt );
+
+ if( this.getProviderName() == null )
+ {
+ cipher = Cipher.getInstance( this.getAlgorithm() );
+ }
+ else
+ {
+ cipher = Cipher.getInstance( this.getAlgorithm(), this.getProviderName() );
+ }
+
+ // save
+ if (mode == Cipher.DECRYPT_MODE) {
+
+ GCMParameterSpec gcmParamSpec = new GCMParameterSpec(128, iv);
+ cipher.init( mode, key, gcmParamSpec );
+
+ //cipher.init( mode, key, algorithmParameters );
+ ciphertext = cipher.doFinal(ciphertext); // actually the unencrypted bytes
+ }
+
+ // save
+ if (mode == Cipher.ENCRYPT_MODE) {
+ iv = generateIV();
+ GCMParameterSpec gcmParamSpec = new GCMParameterSpec(128, iv);
+
+ salt = this.getSalt();
+ cipher.init( mode, key, gcmParamSpec );
+
+ //algorithmParameters = cipher.getParameters();
+
+ // might update with associated Data
+ // cipher.updateAAD(associatedData );// not supported PBEWithHmacSHA256AndAES_256
+
+ byte[] result = cipher.doFinal(input);
+ //iv = cipher.getIV(); // AES has 128bit block size, but iv is 16bit
+
+ // Salt and IV need to be stored with the result, otherwise we can't decrypt the message later.
+ ByteBuffer byteBuffer = ByteBuffer.allocate(salt.length + iv.length + result.length);
+ ciphertext = byteBuffer.put(salt).put(iv).put(result).array();
+
+// ciphertext = new byte[salt.length + iv.length + result.length];
+//
+// System.arraycopy(salt, 0, ciphertext, 0, salt.length);
+// System.arraycopy(iv, 0, ciphertext, salt.length, iv.length);
+// System.arraycopy(result, 0, ciphertext, salt.length + iv.length, result.length);// push after salt and iv
+ }
+ return ciphertext;
+ }
+
+ private byte[] generateIV( ) throws GeneralSecurityException {
+ SecureRandom random;
+ try {
+ random = SecureRandom.getInstance("SHA1PRNG");
+ byte[] iv = new byte[ IV_SIZE ];
+ random.nextBytes(iv);
+ return iv;
+ } catch (NoSuchAlgorithmException e) {
+ throw new GeneralSecurityException(e);
+ }
+ }
+
+ @Override
+ protected Cipher createCipher(int encryptMode, char[] password) throws GeneralSecurityException, IOException {
+ throw new RuntimeException("not provided for this implementation");
+ }
+
+
+}
diff --git a/src/java/org/apache/fulcrum/jce/crypto/algo/CryptoStreamPBEImpl.java b/src/java/org/apache/fulcrum/jce/crypto/algo/CryptoStreamPBEImpl.java
new file mode 100644
index 0000000..de1cb4c
--- /dev/null
+++ b/src/java/org/apache/fulcrum/jce/crypto/algo/CryptoStreamPBEImpl.java
@@ -0,0 +1,221 @@
+package org.apache.fulcrum.jce.crypto.algo;
+
+import java.io.ByteArrayOutputStream;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+
+import org.apache.fulcrum.jce.crypto.CryptoParameters;
+import org.apache.fulcrum.jce.crypto.CryptoStreamFactoryJ8Template;
+import org.apache.fulcrum.jce.crypto.StreamUtil;
+
+/**
+ * Concrete implementation for creating encrypting/decrypting streams. The
+ * implementation uses the JCA (Java Crypto Extension) supplied
+ * by SUN (using SunJCE 1.42).
+ *
+ * The implementation uses as @see {@link CryptoParameters#ALGORITHM_J8_PBE} for encryption which
+ * should be sufficent for most applications.
+ *
+ * The implementation also supplies a default password in the case that
+ * the programmer don't want to have additional hassles. It is easy to
+ * reengineer the password being used but much better than a hard-coded
+ * password in the application.
+ *
+ * The code uses parts from Markus Hahn's Blowfish library found at
+ * http://blowfishj.sourceforge.net/
+ *
+ * @author <a href="mailto:gk@apache.org">Georg Kallidis</a>
+ * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl </a>
+ * @author <a href="mailto:maakus@earthlink.net">Markus Hahn</a>
+ */
+
+public final class CryptoStreamPBEImpl extends CryptoStreamFactoryJ8Template
+{
+
+ protected static final int IV_SIZE = 16;
+ /**
+ * Constructor
+ */
+ public CryptoStreamPBEImpl() throws GeneralSecurityException
+ {
+ this.salt = generateSalt();
+ this.count = CryptoParameters.COUNT_J8;
+ this.providerName = PROVIDERNAME;
+ this.algorithm = CryptoParameters.ALGORITHM_J8_PBE;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param salt the salt for the PBE algorithm
+ * @param count the iteration for PBEParameterSpec
+ */
+ public CryptoStreamPBEImpl( byte[] salt, int count) throws GeneralSecurityException
+ {
+ this.salt = salt;
+ this.count = count;
+ this.providerName = PROVIDERNAME;
+ this.algorithm = CryptoParameters.ALGORITHM_J8_PBE;
+ }
+
+ /**
+ * Create a PBE key.
+ *
+ * @param password the password to use.
+ * @param salt if provided this is used, otherweise {@link #getSalt()}.
+ * @return the key
+ * @throws GeneralSecurityException creating the key failed
+ */
+ @Override
+ protected Key createKey( char[] password, byte[] salt )
+ throws GeneralSecurityException
+ {
+ SecretKeyFactory keyFactory;
+ String algorithm = this.getAlgorithm();
+
+ PBEKeySpec keySpec = new PBEKeySpec(password, (salt == null)? this.getSalt(): salt, this.getCount(), KEY_SIZE );
+
+ byte[] encodedTmp = null;
+ try {
+ if( getProviderName() == null )
+ {
+ keyFactory = SecretKeyFactory.getInstance( algorithm );
+ }
+ else
+ {
+ keyFactory = SecretKeyFactory.getInstance( algorithm, this.getProviderName() );
+ }
+ return keyFactory.generateSecret(keySpec);
+
+ } catch (NoSuchAlgorithmException e) {
+ throw new GeneralSecurityException(e);
+ } finally {
+ if (encodedTmp != null) {
+ Arrays.fill(encodedTmp, (byte)0);
+ }
+ if (keySpec != null) {
+ keySpec.clearPassword();
+ }
+ }
+ }
+
+ /**
+ * Create a Cipher.
+ *
+ * @param mode the cipher mode
+ * @param password the password
+ * @return an instance of a cipher
+ * @throws GeneralSecurityException creating a cipher failed
+ * @throws IOException creating a cipher failed
+ */
+ @Override
+ protected byte[] createCipher(InputStream is, int mode, char[] password )
+ throws GeneralSecurityException, IOException
+ {
+ Cipher cipher;
+ PBEParameterSpec paramSpec = null;
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
+ long total = StreamUtil.copy(is, bos);
+ byte[] input = bos.toByteArray();
+
+ byte[] ciphertext = null;
+
+ byte[] salt = null;
+ byte[] iv = null;
+ if (mode == Cipher.DECRYPT_MODE) {
+
+ ByteBuffer byteBuffer = ByteBuffer.wrap(input);
+ salt = new byte[SALT_SIZE ];
+ byteBuffer.get(salt);
+ iv = new byte[ IV_SIZE ];
+ byteBuffer.get(iv);
+ ciphertext = new byte[byteBuffer.remaining()];
+ byteBuffer.get(ciphertext);
+
+// salt = Arrays.copyOfRange(input, 0, SALT_SIZE );
+// iv = Arrays.copyOfRange(input, salt.length, salt.length + 16 );
+// ciphertext = Arrays.copyOfRange(input, salt.length + iv.length, input.length);// cut out salt and iv
+ }
+
+ Key key = this.createKey( password, salt );
+
+ if( this.getProviderName() == null )
+ {
+ cipher = Cipher.getInstance( this.getAlgorithm() );
+ }
+ else
+ {
+ cipher = Cipher.getInstance( this.getAlgorithm(), this.getProviderName() );
+ }
+
+ // save
+ if (mode == Cipher.DECRYPT_MODE) {
+
+ paramSpec = new PBEParameterSpec( salt, this.getCount(), new IvParameterSpec(iv) );
+
+ cipher.init( mode, key, paramSpec );
+ //cipher.init( mode, key, algorithmParameters );
+ ciphertext = cipher.doFinal(ciphertext); // actually the unencrypted bytes
+ }
+
+ // save
+ if (mode == Cipher.ENCRYPT_MODE) {
+ paramSpec = new PBEParameterSpec( this.getSalt(), this.getCount() );
+ salt = paramSpec.getSalt();
+ cipher.init( mode, key, paramSpec );
+ //algorithmParameters = cipher.getParameters();
+
+ byte[] result = cipher.doFinal(input);
+ iv = cipher.getIV(); // AES has 128bit block size, but iv is 16bit
+
+ // Salt and IV need to be stored with the result, otherwise we can't decrypt the message later.
+ ByteBuffer byteBuffer = ByteBuffer.allocate(salt.length + iv.length + result.length);
+ ciphertext = byteBuffer.put(salt).put(iv).put(result).array();
+
+// ciphertext = new byte[salt.length + iv.length + result.length];
+// System.arraycopy(salt, 0, ciphertext, 0, salt.length);
+// System.arraycopy(iv, 0, ciphertext, salt.length, iv.length);
+// System.arraycopy(result, 0, ciphertext, salt.length + iv.length, result.length);// push after salt and iv
+ }
+ return ciphertext;
+ }
+
+
+ @Override
+ protected Cipher createCipher(int encryptMode, char[] password) throws GeneralSecurityException, IOException {
+ throw new RuntimeException("not provided for this implementation");
+ }
+
+}
diff --git a/src/java/org/apache/fulcrum/jce/crypto/cli/MainJ8.java b/src/java/org/apache/fulcrum/jce/crypto/cli/MainJ8.java
index a0ad7f6..5f7bcab 100644
--- a/src/java/org/apache/fulcrum/jce/crypto/cli/MainJ8.java
+++ b/src/java/org/apache/fulcrum/jce/crypto/cli/MainJ8.java
@@ -84,7 +84,7 @@
*/
public static void printHelp()
{
- System.out.println("\r\n*** Command line tool for encrypting/decrypting strings/files ***\r\n*** algorithm based on "+ CryptoParameters.ALGORITHM_J8+ "***\r\n");
+ System.out.println("\r\n*** Command line tool for encrypting/decrypting strings/files ***\r\n*** algorithm based on "+ CryptoParameters.ALGORITHM_J8_PBE+ "***\r\n");
System.out.println( "*** Usage: ***\r\n");
System.out.println("java -cp target\\classes; "+ MainJ8.class.getName()+ " <operation mode:file:string> <coding mode:enc|dec> <password> <path|string> [target]\r\ne.g.\r\n");
System.out.println( MainJ8.class.getSimpleName()+ " file [enc|dec] passwd source [target]");
@@ -152,7 +152,7 @@
}
else if( cipherMode.equals("enc") )
{
- System.out.println("Enrypting " + sourceFile.getAbsolutePath() );
+ System.out.println("Encrypting " + sourceFile.getAbsolutePath() );
cryptoUtilJ8.encrypt( fis, baos, password );
fis.close();
diff --git a/src/test/org/apache/fulcrum/jce/crypto/CryptoUtilJ8ParameterizedTest.java b/src/test/org/apache/fulcrum/jce/crypto/CryptoUtilJ8ParameterizedTest.java
new file mode 100644
index 0000000..2ecbd60
--- /dev/null
+++ b/src/test/org/apache/fulcrum/jce/crypto/CryptoUtilJ8ParameterizedTest.java
@@ -0,0 +1,328 @@
+package org.apache.fulcrum.jce.crypto;
+
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.fulcrum.jce.crypto.CryptoParameters.TYPES;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+
+/**
+ * Test suite for crypto functionality
+ *
+ * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
+ */
+public class CryptoUtilJ8ParameterizedTest {
+ /** the password to be used */
+ private String password;
+
+ /** the test data directory */
+ private File testDataDirectory;
+
+ /** the temp data director */
+ private File tempDataDirectory;
+
+ private List<CryptoUtilJ8> cryptoUtilJ8s = new ArrayList<>();
+
+
+ /**
+ * Constructor
+ */
+ public CryptoUtilJ8ParameterizedTest() {
+
+ this.password = "mysecret";
+ this.testDataDirectory = new File("./src/test/data");
+ this.tempDataDirectory = new File("./target/temp");
+ this.tempDataDirectory.mkdirs();
+ }
+
+
+// @ParameterizedTest
+// @EnumSource( TYPES.class )
+// public void setUp(TYPES type) throws Exception {
+// cryptoUtilJ8 = CryptoUtilJ8.getInstance(type); // (TYPES.PBE);
+// }
+
+ /**
+ * @return Returns the password.
+ */
+ protected char[] getPassword() {
+ return password.toCharArray();
+ }
+
+ /**
+ * @return Returns the tempDataDirectory.
+ */
+ protected File getTempDataDirectory() {
+ return tempDataDirectory;
+ }
+
+ /**
+ * @return Returns the testDataDirectory.
+ */
+ protected File getTestDataDirectory() {
+ return testDataDirectory;
+ }
+
+ @AfterEach
+ public void setup() {
+ cryptoUtilJ8s.clear();
+ }
+
+ /** Encrypt a text file
+ * @throws Exception Generic exception
+ */
+ @ParameterizedTest
+ @EnumSource( TYPES.class )
+ public void testTextEncryption(TYPES type) {
+
+ cryptoUtilJ8s.add(CryptoUtilJ8.getInstance(type));
+ File sourceFile = new File(this.getTestDataDirectory(), "plain.txt");
+ File targetFile = new File(this.getTempDataDirectory(), "plain.j8.enc.txt");
+
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ try {
+ System.out.println("checking "+ cuj8.getType());
+ cuj8.encrypt(sourceFile, targetFile, this.getPassword());
+ } catch (GeneralSecurityException e) {
+ e.printStackTrace();
+ fail();
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+ } );
+ }
+
+ /** Decrypt a text file
+ * @throws Exception Generic exception
+ */
+ @ParameterizedTest
+ @EnumSource( TYPES.class )
+ public void testTextDecryption(TYPES type) {
+ cryptoUtilJ8s.add(CryptoUtilJ8.getInstance(type));
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ System.out.println("checking "+ cuj8.getType());
+ try {
+ File sourceFile = new File(this.getTestDataDirectory(), "plain.txt");
+ File targetFile = new File(this.getTempDataDirectory(), "plain.j8.enc.txt");
+ cuj8.encrypt(sourceFile, targetFile, this.getPassword());
+
+ File sourceFile2 = targetFile;
+ File targetFile2 = new File(this.getTempDataDirectory(), "plain.j8.dec.txt");
+ cuj8.decrypt(sourceFile2, targetFile2.getAbsolutePath(), this.getPassword());
+ assertEquals(
+ new String(Files.readAllBytes( Paths.get(sourceFile.toURI())) ),
+ new String(Files.readAllBytes( Paths.get(targetFile2.toURI())) )
+ );
+ } catch (GeneralSecurityException | IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+ });
+ }
+
+ /** Encrypt a PDF file
+ *
+ * @throws Exception Generic exception
+ */
+ @ParameterizedTest
+ @EnumSource( TYPES.class )
+ public void testPdfEncryption(TYPES type) {
+ cryptoUtilJ8s.add(CryptoUtilJ8.getInstance(type));
+ File sourceFile = new File(this.getTestDataDirectory(), "plain.pdf");
+ File targetFile = new File(this.getTempDataDirectory(), "plain.j8.enc.pdf");
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ try {
+ cuj8.encrypt(sourceFile, targetFile, this.getPassword());
+ } catch (GeneralSecurityException | IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+ });
+ }
+
+ /** Decrypt a PDF file
+ *
+ * @throws Exception Generic exception
+ */
+ @ParameterizedTest
+ @EnumSource( TYPES.class )
+ public void testPdfDecryption(TYPES type) {
+ cryptoUtilJ8s.add(CryptoUtilJ8.getInstance(type));
+ //testPdfEncryption();
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ try {
+ File sourceFile = new File(this.getTestDataDirectory(), "plain.pdf");
+ File targetFile = new File(this.getTempDataDirectory(), "plain.j8.enc.pdf");
+ cuj8.encrypt(sourceFile, targetFile, this.getPassword());
+
+ File sourceFile2 = targetFile;
+ File targetFile2 = new File(this.getTempDataDirectory(), "plain.j8.dec.pdf");
+ cuj8.decrypt(sourceFile2, targetFile2, this.getPassword());
+
+ assertEquals(
+ new String(Files.readAllBytes( Paths.get(sourceFile.toURI())) ),
+ new String(Files.readAllBytes( Paths.get(targetFile2.toURI())) )
+ );
+ } catch (GeneralSecurityException | IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+ });
+
+ }
+
+ /** Test encryption and decryption of Strings
+ *
+ * @throws Exception Generic exception
+ */
+ @ParameterizedTest
+ @EnumSource( TYPES.class )
+ public void testStringEncryption(TYPES type) {
+ cryptoUtilJ8s.add(CryptoUtilJ8.getInstance(type));
+ char[] testVector = new char[513];
+
+ for (int i = 0; i < testVector.length; i++) {
+ testVector[i] = (char) i;
+ }
+
+ String source = new String(testVector);
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ String cipherText;
+ String plainText;
+ try {
+ cipherText = cuj8.encryptString(source, this.getPassword());
+ plainText = cuj8.decryptString(cipherText, this.getPassword());
+ assertEquals(source, plainText, source +" is not equal with " + plainText);
+ } catch (GeneralSecurityException | IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+
+ });
+
+
+ }
+
+ /** Test encryption and decryption of Strings
+ * @throws Exception Generic exception
+ */
+ @ParameterizedTest
+ @EnumSource( TYPES.class )
+ public void testStringHandling(TYPES type) {
+ cryptoUtilJ8s.add(CryptoUtilJ8.getInstance(type));
+ String source = "Nobody knows the toubles I have seen ...";
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ String cipherText;
+ try {
+ cipherText = cuj8.encryptString(source, this.getPassword());
+ String plainText = cuj8.decryptString(cipherText, this.getPassword());
+ assertEquals(source, plainText);
+ } catch (GeneralSecurityException | IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+
+ });
+
+ }
+
+ /** Test creating a password
+ * @throws Exception Generic exception
+ */
+ @Test
+ public void testPasswordFactory() throws Exception {
+ char[] result = null;
+ result = PasswordFactory.getInstance("SHA-256").create();
+ System.out.println("random pw:" + new String(result));
+ result = PasswordFactory.getInstance("SHA-256",10_000).create(this.getPassword());
+ System.out.println("password pw with seed:" + new String(result));
+ assertNotNull(result);
+ return;
+ }
+
+ /** Test encryption and decryption of binary data
+ * @throws Exception Generic exception
+ */
+ @ParameterizedTest
+ @EnumSource( TYPES.class )
+ public void testBinaryHandling(TYPES type) throws Exception {
+ cryptoUtilJ8s.add(CryptoUtilJ8.getInstance(type));
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ byte[] source = new byte[256];
+ byte[] result = null;
+
+ for (int i = 0; i < source.length; i++) {
+ source[i] = (byte) i;
+ }
+
+ ByteArrayOutputStream cipherText = new ByteArrayOutputStream();
+ ByteArrayOutputStream plainText = new ByteArrayOutputStream();
+ try {
+ cuj8.encrypt(source, cipherText, this.getPassword());
+ cuj8.decrypt(cipherText, plainText, this.getPassword());
+ } catch (GeneralSecurityException | IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+ result = plainText.toByteArray();
+
+ for (int i = 0; i < source.length; i++) {
+ if (source[i] != result[i]) {
+ fail("Binary data are different at position " + i);
+ }
+ }
+ });
+
+
+
+ }
+
+ /** Test encryption and decryption of Strings
+ * @throws Exception Generic exception
+ */
+ @ParameterizedTest
+ @EnumSource( TYPES.class )
+ public void testStringWithPasswordEncryption(TYPES type) {
+ char[] password = "57cb-4a23-d838-45222".toCharArray();
+ String source = "e02c-3b76-ff1e-5d9a1";
+ cryptoUtilJ8s.add(CryptoUtilJ8.getInstance(type));
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ System.out.println("checking "+ cuj8.getType());
+ String cipherText = null;
+ try {
+ cipherText = cuj8.encryptString(source, password);
+ System.out.println(cipherText);// about 128
+
+ System.out.println("length for " + cuj8.getType() + " is:" +cipherText.length());// about 128
+ if (cuj8.type == TYPES.PBE) {
+ assertEquals(128, cipherText.length()); // 128bytes + 10 bytes for cleartext
+ }
+ CryptoStreamFactoryJ8Template.setInstance(null);
+ String plainText = cuj8.decryptString(cipherText, password);
+ assertEquals(source, plainText);
+ } catch (GeneralSecurityException | IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+
+ });
+
+ }
+
+}
diff --git a/src/test/org/apache/fulcrum/jce/crypto/CryptoUtilJ8Test.java b/src/test/org/apache/fulcrum/jce/crypto/CryptoUtilJ8Test.java
index 8855d69..455b165 100644
--- a/src/test/org/apache/fulcrum/jce/crypto/CryptoUtilJ8Test.java
+++ b/src/test/org/apache/fulcrum/jce/crypto/CryptoUtilJ8Test.java
@@ -1,14 +1,23 @@
package org.apache.fulcrum.jce.crypto;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.List;
-import org.junit.Before;
-import org.junit.Test;
+import org.apache.fulcrum.jce.crypto.CryptoParameters.TYPES;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
/**
@@ -16,7 +25,6 @@
*
* @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
*/
-
public class CryptoUtilJ8Test {
/** the password to be used */
private String password;
@@ -27,7 +35,8 @@
/** the temp data director */
private File tempDataDirectory;
- private CryptoUtilJ8 cryptoUtilJ8;
+ private static List<CryptoUtilJ8> cryptoUtilJ8s = new ArrayList<>();
+
/**
* Constructor
@@ -46,10 +55,28 @@
*
* @throws Exception Generic exception
*/
- @Before
- public void setUp() throws Exception {
- cryptoUtilJ8 = CryptoUtilJ8.getInstance();
+ @BeforeAll
+ public static void setUp() throws Exception {
+ cryptoUtilJ8s.clear();
+ for (TYPES type : CryptoParameters.TYPES.values()) {
+ cryptoUtilJ8s.add(CryptoUtilJ8.getInstance(type));
+ }
+ for (CryptoUtilJ8 cryptoUtilJ8 : cryptoUtilJ8s) {
+ System.out.println("registered cryptoUtilsJ8: "+ cryptoUtilJ8.getType() );
+ System.out.println( ((CryptoStreamFactoryJ8Template)cryptoUtilJ8.getCryptoStreamFactory()).getAlgorithm());
+ }
+
}
+ @AfterAll
+ public static void destroy() {
+ cryptoUtilJ8s.clear();
+ }
+
+// @ParameterizedTest
+// @EnumSource( TYPES.class )
+// public void setUp(TYPES type) throws Exception {
+// cryptoUtilJ8 = CryptoUtilJ8.getInstance(type); // (TYPES.PBE);
+// }
/**
* @return Returns the password.
@@ -76,21 +103,47 @@
* @throws Exception Generic exception
*/
@Test
- public void testTextEncryption() throws Exception {
+ public void testTextEncryption() {
+
File sourceFile = new File(this.getTestDataDirectory(), "plain.txt");
File targetFile = new File(this.getTempDataDirectory(), "plain.j8.enc.txt");
- cryptoUtilJ8.encrypt(sourceFile, targetFile, this.getPassword());
+
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ try {
+ cuj8.encrypt(sourceFile, targetFile, this.getPassword());
+ } catch (GeneralSecurityException e) {
+ e.printStackTrace();
+ fail();
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+ } );
}
/** Decrypt a text file
* @throws Exception Generic exception
*/
@Test
- public void testTextDecryption() throws Exception {
- testTextEncryption();
- File sourceFile = new File(this.getTempDataDirectory(), "plain.j8.enc.txt");
- File targetFile = new File(this.getTempDataDirectory(), "plain.j8.dec.txt");
- cryptoUtilJ8.decrypt(sourceFile, targetFile.getAbsolutePath(), this.getPassword());
+ public void testTextDecryption() {
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ try {
+ File sourceFile = new File(this.getTestDataDirectory(), "plain.txt");
+ File targetFile = new File(this.getTempDataDirectory(), "plain.j8.enc.txt");
+ cuj8.encrypt(sourceFile, targetFile, this.getPassword());
+
+ File sourceFile2 = new File(this.getTempDataDirectory(), "plain.j8.enc.txt");;
+ File targetFile2 = new File(this.getTempDataDirectory(), "plain.j8.dec.txt");
+ cuj8.decrypt(sourceFile2, targetFile2.getAbsolutePath(), this.getPassword());
+ assertEquals(
+ new String(Files.readAllBytes( Paths.get(sourceFile.toURI())) ),
+ new String(Files.readAllBytes( Paths.get(targetFile2.toURI())) )
+ );
+ } catch (GeneralSecurityException | IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+ });
}
/** Encrypt a PDF file
@@ -98,10 +151,17 @@
* @throws Exception Generic exception
*/
@Test
- public void testPdfEncryption() throws Exception {
+ public void testPdfEncryption() {
File sourceFile = new File(this.getTestDataDirectory(), "plain.pdf");
File targetFile = new File(this.getTempDataDirectory(), "plain.j8.enc.pdf");
- cryptoUtilJ8.encrypt(sourceFile, targetFile, this.getPassword());
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ try {
+ cuj8.encrypt(sourceFile, targetFile, this.getPassword());
+ } catch (GeneralSecurityException | IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+ });
}
/** Decrypt a PDF file
@@ -109,11 +169,28 @@
* @throws Exception Generic exception
*/
@Test
- public void testPdfDecryption() throws Exception {
- testPdfEncryption();
- File sourceFile = new File(this.getTempDataDirectory(), "plain.j8.enc.pdf");
- File targetFile = new File(this.getTempDataDirectory(), "plain.j8.dec.pdf");
- cryptoUtilJ8.decrypt(sourceFile, targetFile, this.getPassword());
+ public void testPdfDecryption() {
+ //testPdfEncryption();
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ try {
+ File sourceFile = new File(this.getTestDataDirectory(), "plain.pdf");
+ File targetFile = new File(this.getTempDataDirectory(), "plain.j8.enc.pdf");
+ cuj8.encrypt(sourceFile, targetFile, this.getPassword());
+
+ File sourceFile2 = new File(this.getTempDataDirectory(), "plain.j8.enc.pdf");
+ File targetFile2 = new File(this.getTempDataDirectory(), "plain.j8.dec.pdf");
+ cuj8.decrypt(sourceFile2, targetFile2, this.getPassword());
+
+ assertEquals(
+ new String(Files.readAllBytes( Paths.get(sourceFile.toURI())) ),
+ new String(Files.readAllBytes( Paths.get(targetFile2.toURI())) )
+ );
+ } catch (GeneralSecurityException | IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+ });
+
}
/** Test encryption and decryption of Strings
@@ -121,7 +198,7 @@
* @throws Exception Generic exception
*/
@Test
- public void testStringEncryption() throws Exception {
+ public void testStringEncryption() {
char[] testVector = new char[513];
for (int i = 0; i < testVector.length; i++) {
@@ -129,20 +206,42 @@
}
String source = new String(testVector);
- String cipherText = cryptoUtilJ8.encryptString(source, this.getPassword());
- String plainText = cryptoUtilJ8.decryptString(cipherText, this.getPassword());
- assertEquals(source +" is not equal with " + plainText, source, plainText);
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ String cipherText;
+ String plainText;
+ try {
+ cipherText = cuj8.encryptString(source, this.getPassword());
+ plainText = cuj8.decryptString(cipherText, this.getPassword());
+ assertEquals(source, plainText, source +" is not equal with " + plainText);
+ } catch (GeneralSecurityException | IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+
+ });
+
+
}
/** Test encryption and decryption of Strings
* @throws Exception Generic exception
*/
@Test
- public void testStringHandling() throws Exception {
+ public void testStringHandling() {
String source = "Nobody knows the toubles I have seen ...";
- String cipherText = cryptoUtilJ8.encryptString(source, this.getPassword());
- String plainText = cryptoUtilJ8.decryptString(cipherText, this.getPassword());
- assertEquals(source, plainText);
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ String cipherText;
+ try {
+ cipherText = cuj8.encryptString(source, this.getPassword());
+ String plainText = cuj8.decryptString(cipherText, this.getPassword());
+ assertEquals(source, plainText);
+ } catch (GeneralSecurityException | IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+
+ });
+
}
/** Test creating a password
@@ -152,9 +251,9 @@
public void testPasswordFactory() throws Exception {
char[] result = null;
result = PasswordFactory.getInstance("SHA-256").create();
- System.out.println(new String(result));
+ System.out.println("random pw:" + new String(result));
result = PasswordFactory.getInstance("SHA-256",10_000).create(this.getPassword());
- System.out.println(new String(result));
+ System.out.println("password pw with seed:" + new String(result));
assertNotNull(result);
return;
}
@@ -164,41 +263,65 @@
*/
@Test
public void testBinaryHandling() throws Exception {
- byte[] source = new byte[256];
- byte[] result = null;
+
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ byte[] source = new byte[256];
+ byte[] result = null;
- for (int i = 0; i < source.length; i++) {
- source[i] = (byte) i;
- }
-
- ByteArrayOutputStream cipherText = new ByteArrayOutputStream();
- ByteArrayOutputStream plainText = new ByteArrayOutputStream();
-
- cryptoUtilJ8.encrypt(source, cipherText, this.getPassword());
- cryptoUtilJ8.decrypt(cipherText, plainText, this.getPassword());
-
- result = plainText.toByteArray();
-
- for (int i = 0; i < source.length; i++) {
- if (source[i] != result[i]) {
- fail("Binary data are different at position " + i);
+ for (int i = 0; i < source.length; i++) {
+ source[i] = (byte) i;
}
- }
+
+ ByteArrayOutputStream cipherText = new ByteArrayOutputStream();
+ ByteArrayOutputStream plainText = new ByteArrayOutputStream();
+ try {
+ cuj8.encrypt(source, cipherText, this.getPassword());
+ cuj8.decrypt(cipherText, plainText, this.getPassword());
+ } catch (GeneralSecurityException | IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+ result = plainText.toByteArray();
+
+ for (int i = 0; i < source.length; i++) {
+ if (source[i] != result[i]) {
+ fail("Binary data are different at position " + i);
+ }
+ }
+ });
+
+
+
}
/** Test encryption and decryption of Strings
* @throws Exception Generic exception
*/
@Test
- public void testStringWithPasswordEncryption() throws Exception {
+ public void testStringWithPasswordEncryption() {
char[] password = "57cb-4a23-d838-45222".toCharArray();
String source = "e02c-3b76-ff1e-5d9a1";
- String cipherText = cryptoUtilJ8.encryptString(source, password);
- System.out.println(cipherText);// about 128
- assertEquals(138, cipherText.length()); // 128bytes + 10 bytes for cleartext
- CryptoStreamFactoryJ8Impl.setInstance(null);
- String plainText = cryptoUtilJ8.decryptString(cipherText, password);
- assertEquals(source, plainText);
+
+ cryptoUtilJ8s.forEach(cuj8 -> {
+ String cipherText = null;
+ try {
+ cipherText = cuj8.encryptString(source, password);
+ System.out.println(cipherText);// about 128
+
+ System.out.println("length for " + cuj8.getType() + " is:" +cipherText.length());// about 128
+ if (cuj8.type == TYPES.PBE) {
+ assertEquals(128, cipherText.length()); // 128bytes + 10 bytes for cleartext
+ }
+ CryptoStreamFactoryJ8Template.setInstance(null);
+ String plainText = cuj8.decryptString(cipherText, password);
+ assertEquals(source, plainText);
+ } catch (GeneralSecurityException | IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+
+ });
+
}
}
diff --git a/src/test/org/apache/fulcrum/jce/crypto/CryptoUtilTest.java b/src/test/org/apache/fulcrum/jce/crypto/CryptoUtilTest.java
index 9657577..d3d68ce 100644
--- a/src/test/org/apache/fulcrum/jce/crypto/CryptoUtilTest.java
+++ b/src/test/org/apache/fulcrum/jce/crypto/CryptoUtilTest.java
@@ -63,9 +63,9 @@
* @throws Exception Generic exception
*/
protected void setUp() throws Exception {
- CryptoStreamFactoryImpl factory = new CryptoStreamFactoryImpl(CryptoParameters.SALT, CryptoParameters.COUNT);
+ CryptoStreamFactoryImpl factory = new CryptoStreamFactoryImpl(CryptoParameters.SALT, CryptoParameters.COUNT);
- CryptoStreamFactoryImpl.setInstance(factory);
+ CryptoStreamFactoryImpl.setInstance(factory);
}
/**
diff --git a/src/test/org/apache/fulcrum/jce/crypto/Main.java b/src/test/org/apache/fulcrum/jce/crypto/Main.java
index 8560fa6..95b79df 100644
--- a/src/test/org/apache/fulcrum/jce/crypto/Main.java
+++ b/src/test/org/apache/fulcrum/jce/crypto/Main.java
@@ -136,7 +136,7 @@
}
else if( cipherMode.equals("enc") )
{
- System.out.println("Enrypting " + sourceFile.getAbsolutePath() );
+ System.out.println("Encrypting " + sourceFile.getAbsolutePath() );
CryptoUtil.getInstance().encrypt( fis, baos, password );
fis.close();
diff --git a/src/test/org/apache/fulcrum/jce/crypto/SmartDecryptingInputStreamTest.java b/src/test/org/apache/fulcrum/jce/crypto/SmartDecryptingInputStreamTest.java
index ed8278a..ca4ebb4 100644
--- a/src/test/org/apache/fulcrum/jce/crypto/SmartDecryptingInputStreamTest.java
+++ b/src/test/org/apache/fulcrum/jce/crypto/SmartDecryptingInputStreamTest.java
@@ -174,7 +174,7 @@
FileInputStream fis = new FileInputStream( file );
CryptoUtil.getInstance().encrypt(
- CryptoStreamFactoryImpl.getInstance(),
+ CryptoStreamFactoryImpl.getInstance(),
fis,
baos,
this.getPassword()
@@ -197,7 +197,7 @@
ByteArrayOutputStream baos = new ByteArrayOutputStream();
SmartDecryptingInputStream sdis = new SmartDecryptingInputStream(
- CryptoStreamFactoryImpl.getInstance(),
+ CryptoStreamFactoryImpl.getInstance(),
bais,
this.getPassword()
);
@@ -219,7 +219,7 @@
FileInputStream fis = new FileInputStream( file );
SmartDecryptingInputStream sdis = new SmartDecryptingInputStream(
- CryptoStreamFactoryImpl.getInstance(),
+ CryptoStreamFactoryImpl.getInstance(),
fis,
this.getPassword()
);