org.refcodes.security.PasswordTextEncrypter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of refcodes-security Show documentation
Show all versions of refcodes-security Show documentation
Artifact with commonly used security functionality such as message
digester and provider for the java.security framework. The
"refcodes-forwardsecrecy" framework settels on top of the
"refcodes-security" artifact.
The newest version!
// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// /////////////////////////////////////////////////////////////////////////////
// This code is copyright (c) by Siegfried Steiner, Munich, Germany and licensed
// under the following (see "http://en.wikipedia.org/wiki/Multi-licensing")
// licenses:
// -----------------------------------------------------------------------------
// GNU General Public License, v3.0 ("http://www.gnu.org/licenses/gpl-3.0.html")
// -----------------------------------------------------------------------------
// Apache License, v2.0 ("http://www.apache.org/licenses/LICENSE-2.0")
// -----------------------------------------------------------------------------
// Please contact the copyright holding author(s) of the software artifacts in
// question for licensing issues not being covered by the above listed licenses,
// also regarding commercial licensing models or regarding the compatibility
// with other open source licenses.
// /////////////////////////////////////////////////////////////////////////////
package org.refcodes.security;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
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.refcodes.runtime.SystemContext;
/**
* The {@link PasswordTextEncrypter} is a text {@link Encrypter} for encrypting
* {@link String} instances with a given password using a {@value #KEY_FACTORY}
* key factory and a {@value #CIPHER} cipher.
*
* ATTENTION: THIS IMPLEMENTATION STORES AN OBFUSCATED PASSWORD AT RUNTIME!
*/
public class PasswordTextEncrypter implements Encrypter {
// /////////////////////////////////////////////////////////////////////////
// CONSTANTS:
// /////////////////////////////////////////////////////////////////////////
private static final String CIPHER = "AES/CBC/PKCS5Padding";
private static final String KEY_FACTORY = "PBKDF2WithHmacSHA256";
private static final String ALGORITHM = "AES";
// /////////////////////////////////////////////////////////////////////////
// VARIABLES:
// /////////////////////////////////////////////////////////////////////////
private char[] _password;
private static final SecureRandom RND = new SecureRandom();
// /////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS:
// /////////////////////////////////////////////////////////////////////////
/**
* Constructs a {@link PasswordTextEncrypter} instance with the given
* password.
*
* ATTENTION: THE PROVIDED CHAR ARRAY IS CLEARED (FILLED WITH ZEROS) AFTER
* BEING PASSED TO THIS CONSTRUCTOR!
*
* @param aPassword The password to be used for encryption.
*/
public PasswordTextEncrypter( char[] aPassword ) {
_password = SystemContext.HOST_USER_APPLICATION_SESSION.toInvertible( aPassword );
Arrays.fill( aPassword, (char) 0 );
}
/**
* Constructs a {@link PasswordTextEncrypter} instance with the given
* password.
*
* ATTENTION: USE A CHAR ARRAY INSTEAD OF A STRING AS PASSWORD IF YOU DON'T
* WANT YOUR PASSWORD TO HANG AROUND IN THE JVM'S STRING CACHE!
*
* @param aPassword The password to be used for encryption.
*/
public PasswordTextEncrypter( String aPassword ) {
this( aPassword.toCharArray() );
}
// /////////////////////////////////////////////////////////////////////////
// METHODS:
// /////////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc}
*/
@Override
public String toEncrypted( String aInput ) throws EncryptionException {
final byte[] theSalt = new byte[8];
byte[] iv = null;
byte[] theEncrypted = null;
byte[] theCombined = null;
RND.nextBytes( theSalt );
final PBEKeySpec theSpec = new PBEKeySpec( SystemContext.HOST_USER_APPLICATION_SESSION.toInvertible( _password ), theSalt, 65536, 256 );
SecretKey theSecret = null;
try {
final SecretKeyFactory theFactory = SecretKeyFactory.getInstance( KEY_FACTORY );
theSecret = new SecretKeySpec( theFactory.generateSecret( theSpec ).getEncoded(), ALGORITHM );
final Cipher theCipher = Cipher.getInstance( CIPHER );
iv = new byte[theCipher.getBlockSize()];
RND.nextBytes( iv );
final IvParameterSpec ivSpec = new IvParameterSpec( iv );
theCipher.init( Cipher.ENCRYPT_MODE, theSecret, ivSpec );
theEncrypted = theCipher.doFinal( aInput.getBytes( StandardCharsets.UTF_8 ) );
theCombined = new byte[theSalt.length + iv.length + theEncrypted.length];
System.arraycopy( theSalt, 0, theCombined, 0, theSalt.length );
System.arraycopy( iv, 0, theCombined, theSalt.length, iv.length );
System.arraycopy( theEncrypted, 0, theCombined, theSalt.length + iv.length, theEncrypted.length );
return Base64.getEncoder().encodeToString( theCombined );
}
catch ( NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | InvalidKeySpecException | InvalidAlgorithmParameterException e ) {
throw new EncryptionException( "Unable to encrypt text as of <" + e.getClass().getName() + ">!", e );
}
finally {
Arrays.fill( theSalt, (byte) 0 );
if ( theCombined != null ) {
Arrays.fill( theCombined, (byte) 0 );
}
if ( iv != null ) {
Arrays.fill( iv, (byte) 0 );
}
if ( theEncrypted != null ) {
Arrays.fill( theEncrypted, (byte) 0 );
}
theSpec.clearPassword();
if ( theSecret != null ) {
try {
theSecret.destroy();
}
catch ( Exception ignore ) {}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public int toEncrypted( byte[] aBuffer, int aOffset, int aLength, byte[] aOutBuffer, int aOutOffset ) throws EncryptionException {
final String theInput = new String( aBuffer, aOffset, aLength, StandardCharsets.ISO_8859_1 ); // if you need 8 bit transparency, ISO-8859-1 is the best choice
final byte[] theEcrypted = toEncrypted( theInput ).getBytes( StandardCharsets.ISO_8859_1 ); // if you need 8 bit transparency, ISO-8859-1 is the best choice
for ( int i = 0; i < theEcrypted.length; i++ ) {
aOutBuffer[i + aOffset] = theEcrypted[i];
}
return aLength;
}
/**
* {@inheritDoc}
*/
@Override
public void dispose() {
Arrays.fill( _password, (char) 0 );
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy