org.refcodes.security.PasswordTextDecrypter 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.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 PasswordTextDecrypter} is a text {@link Decrypter} for decrypting
* {@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 PasswordTextDecrypter implements Decrypter {
// /////////////////////////////////////////////////////////////////////////
// STATICS:
// /////////////////////////////////////////////////////////////////////////
private static final String CIPHER = "AES/CBC/PKCS5Padding";
private static final String KEY_FACTORY = "PBKDF2WithHmacSHA256";
// /////////////////////////////////////////////////////////////////////////
// CONSTANTS:
// /////////////////////////////////////////////////////////////////////////
private static final String ALGORITHM = "AES";
// /////////////////////////////////////////////////////////////////////////
// VARIABLES:
// /////////////////////////////////////////////////////////////////////////
private char[] _password;
// /////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS:
// /////////////////////////////////////////////////////////////////////////
/**
* Constructs a {@link PasswordTextDecrypter} 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 decryption.
*/
public PasswordTextDecrypter( char[] aPassword ) {
_password = SystemContext.HOST_USER_APPLICATION_SESSION.toInvertible( aPassword );
Arrays.fill( aPassword, (char) 0 );
}
/**
* Constructs a {@link PasswordTextDecrypter} 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 decryption.
*/
public PasswordTextDecrypter( String aPassword ) {
this( aPassword.toCharArray() );
}
// /////////////////////////////////////////////////////////////////////////
// METHODS:
// /////////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc}
*/
@Override
public String toDecrypted( String aInput ) throws DecryptionException {
final byte[] theCombined = Base64.getDecoder().decode( aInput );
final byte[] theSalt = new byte[8];
final byte[] iv = new byte[16];
final byte[] theEncrypted = new byte[theCombined.length - theSalt.length - iv.length];
System.arraycopy( theCombined, 0, theSalt, 0, theSalt.length );
System.arraycopy( theCombined, theSalt.length, iv, 0, iv.length );
System.arraycopy( theCombined, theSalt.length + iv.length, theEncrypted, 0, theEncrypted.length );
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 );
final IvParameterSpec ivSpec = new IvParameterSpec( iv );
theCipher.init( Cipher.DECRYPT_MODE, theSecret, ivSpec );
final byte[] theDecrypted = theCipher.doFinal( theEncrypted );
return new String( theDecrypted, StandardCharsets.UTF_8 );
}
catch ( NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | InvalidKeySpecException | InvalidAlgorithmParameterException e ) {
throw new DecryptionException( "Unable to decrypt text as of <" + e.getClass().getName() + ">!", e );
}
finally {
Arrays.fill( theSalt, (byte) 0 );
Arrays.fill( theCombined, (byte) 0 );
Arrays.fill( iv, (byte) 0 );
Arrays.fill( theEncrypted, (byte) 0 );
theSpec.clearPassword();
if ( theSecret != null ) {
try {
theSecret.destroy();
}
catch ( Exception ignore ) {}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public int toDecrypted( byte[] aBuffer, int aOffset, int aLength, byte[] aOutBuffer, int aOutOffset ) throws DecryptionException {
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 = toDecrypted( 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