All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.refcodes.security.PasswordTextDecrypter Maven / Gradle / Ivy

Go to download

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