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

org.refcodes.forwardsecrecy.DecryptionProviderImpl Maven / Gradle / Ivy

The newest version!
// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// =============================================================================
// This code is copyright (c) by Siegfried Steiner, Munich, Germany, distributed
// on an "AS IS" BASIS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, and licen-
// sed 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")
// together with the GPL linking exception applied; as being applied by the GNU
// Classpath ("http://www.gnu.org/software/classpath/license.html")
// =============================================================================
// Apache License, v2.0 ("http://www.apache.org/licenses/TEXT-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.forwardsecrecy;

import java.io.UnsupportedEncodingException;
import java.security.Provider;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.refcodes.controlflow.RetryCounter;
import org.refcodes.data.Encoding;
import org.refcodes.data.IoRetryCount;
import org.refcodes.data.LatencySleepTime;
import org.refcodes.data.LoopExtensionTime;
import org.refcodes.security.Algorithm;
import org.refcodes.security.DecryptionException;

/**
 * This class is a basic implementation of the {@link DecryptionProvider}
 * interface.
 * 

* ATTENTION: This implementation does not take care of housekeeping. In case * this class is to be run as a service, it must provide housekeeping facilities * in terms of cleaning up instances contained in internal data structures (such * as the hash maps) after a defined period of time those instance were not * accessed. */ public class DecryptionProviderImpl implements DecryptionProvider { // ///////////////////////////////////////////////////////////////////////// // STATICS: // ///////////////////////////////////////////////////////////////////////// private static final Logger LOGGER = Logger.getLogger( DecryptionProviderImpl.class.getName() ); // ///////////////////////////////////////////////////////////////////////// // VARIABLES: // ///////////////////////////////////////////////////////////////////////// private DecryptionService _decryptionService; private Provider _jceProvider; private String _jceAlgorithm; private Map _cipherUidToStringEncryptor = new WeakHashMap<>(); // ///////////////////////////////////////////////////////////////////////// // CONSTRUCTORS: // ///////////////////////////////////////////////////////////////////////// /** * Constructs the {@link DecryptionProvider} with the given * {@link DecryptionService} and with the specified JCE {@link Provider} as * well as the according JCE algorithm. * * @param aDecryptionService The {@link DecryptionService} to use for * getting the known ciphers for decryption * @param aJceProvider The JCE {@link Provider} to be used. * @param aJceAlgorithm the JCE algorithm to be used by the JCE * {@link Provider}. */ public DecryptionProviderImpl( DecryptionService aDecryptionService, Provider aJceProvider, String aJceAlgorithm ) { _decryptionService = aDecryptionService; _jceProvider = aJceProvider; _jceAlgorithm = aJceAlgorithm; } /** * Constructs the {@link DecryptionProvider} using AES as implemented by the * {@link BouncyCastleProvider}. * * @param aDecryptionService The service to use for getting the known * ciphers for decryption */ public DecryptionProviderImpl( DecryptionService aDecryptionService ) { this( aDecryptionService, new BouncyCastleProvider(), Algorithm.AES.getName() ); } // ///////////////////////////////////////////////////////////////////////// // METHODS: // ///////////////////////////////////////////////////////////////////////// /** * {@inheritDoc} */ @Override public String toDecrypted( String aInput ) throws UnknownCipherUidException, NoCipherUidException { final String theCipherUid = ForwardSecrecyUtility.toCipherUidPrefix( aInput ); if ( theCipherUid == null || theCipherUid.isEmpty() ) { throw new NoCipherUidException( "No cipher UID has been provided by the encrypted text to be decrypted!" ); } final CipherVersion theCipherVersion = getCipherVersion( theCipherUid ); final String theEncyrptedText = ForwardSecrecyUtility.toEncryptedTextBody( aInput ); final StringEncryptor theTextEncryptor = getStringEncryptor( theCipherVersion ); final String theDecryptedText = theTextEncryptor.decrypt( theEncyrptedText ); return theDecryptedText; } /** * {@inheritDoc} */ @Override public int toDecrypted( byte[] aInput, int aInputOffset, int aInputLength, byte[] aOutput, int aOutputOffset ) throws DecryptionException { final byte[] theInputHex = Arrays.copyOfRange( aInput, aInputOffset, aInputOffset + aInputLength ); String theInputText; try { theInputText = new String( theInputHex, Encoding.UTF_8.getCode() ); } catch ( UnsupportedEncodingException e ) { theInputText = new String( theInputHex ); } final String theOutputText; theOutputText = toDecrypted( theInputText ); byte[] theOutputHex; try { theOutputHex = theOutputText.getBytes( Encoding.UTF_8.getCode() ); } catch ( UnsupportedEncodingException e ) { theOutputHex = theOutputText.getBytes(); } if ( aOutput.length < aOutputOffset + theOutputHex.length ) { throw new ArrayIndexOutOfBoundsException( "The decrypted data is of length <" + theOutputHex.length + "> though your buffer with length <" + aOutput.length + "> does not provide enugh elements after offset <" + aOutputOffset + ">." ); } for ( int i = 0; i < theOutputHex.length; i++ ) { aOutput[aOutputOffset + i] = theOutputHex[i]; } return theOutputHex.length; } /** * {@inheritDoc} */ @Override public void dispose() { final Map theMap = _cipherUidToStringEncryptor; if ( theMap != null ) { theMap.clear(); _cipherUidToStringEncryptor = null; } final Provider theProvider = _jceProvider; if ( theProvider != null ) { theProvider.clear(); _jceProvider = null; } _decryptionService = null; _jceAlgorithm = null; } // ///////////////////////////////////////////////////////////////////////// // HELPER: // ///////////////////////////////////////////////////////////////////////// /** * Client side retrieval of the {@link CipherVersion} for the given ciper's * UID. We do this client side as we would get loads of client/server * request in case we decide to separate encryption and decryption as well * as service and server. * * @param aCipherUid The cipher UID for which to get the * {@link CipherVersion} * * @return the cipher version * * @throws UnknownCipherUidException in case the given cipher's UID is * unknown */ private CipherVersion getCipherVersion( String aCipherUid ) throws UnknownCipherUidException { final RetryCounter theRetryCounter = new RetryCounter( IoRetryCount.NORM.getValue(), LatencySleepTime.MIN.getTimeMillis(), LoopExtensionTime.NORM.getTimeMillis() ); while ( theRetryCounter.nextRetry() ) { final List theCipherVersions = _decryptionService.getCipherVersions(); if ( theCipherVersions != null ) { for ( CipherVersion eCipherVersion : theCipherVersions ) { if ( eCipherVersion.getUniversalId().equals( aCipherUid ) ) { return eCipherVersion; } } } if ( theRetryCounter.hasNextRetry() ) { LOGGER.log( Level.WARNING, "No cipher found for cipher UID \"" + aCipherUid + "\", retry count is <" + theRetryCounter.getRetryCount() + "> of <" + theRetryCounter.getRetryNumber() + "> (waiting for <" + theRetryCounter.getNextRetryDelayMillis() / 1000 + "> seconds before next retry) ..." ); } } throw new UnknownCipherUidException( "No cipher version found for cipher UID \"" + aCipherUid + "\" after <" + theRetryCounter.getRetryNumber() + "> retries!", aCipherUid ); } /** * Lazy retrieval of the according {@link StringEncryptor} by the given * cipher UID. * * @param aCipherVersion The cipher UID for which to get the * {@link StringEncryptor}. * * @return The according {@link StringEncryptor} */ private StringEncryptor getStringEncryptor( CipherVersion aCipherVersion ) { StandardPBEStringEncryptor theStringEncryptor = _cipherUidToStringEncryptor.get( aCipherVersion.getCipher() ); if ( theStringEncryptor == null ) { synchronized ( this ) { theStringEncryptor = _cipherUidToStringEncryptor.get( aCipherVersion.getCipher() ); if ( theStringEncryptor == null ) { theStringEncryptor = new StandardPBEStringEncryptor(); theStringEncryptor.setProvider( _jceProvider ); theStringEncryptor.setAlgorithm( _jceAlgorithm ); theStringEncryptor.setPassword( aCipherVersion.getCipher() ); _cipherUidToStringEncryptor.put( aCipherVersion.getUniversalId(), theStringEncryptor ); } } } return theStringEncryptor; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy