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

org.refcodes.forwardsecrecy.PublicKeyDecryptionServerWrapper 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.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.refcodes.exception.Trap;

import edu.vt.middleware.crypt.CryptException;
import edu.vt.middleware.crypt.asymmetric.AsymmetricAlgorithm;
import edu.vt.middleware.crypt.asymmetric.RSA;
import edu.vt.middleware.crypt.digest.SHA512;
import edu.vt.middleware.crypt.signature.RSASignature;
import edu.vt.middleware.crypt.signature.SignatureAlgorithm;
import edu.vt.middleware.crypt.util.Base64Converter;

/**
 * Wraps a decryption server and provides asymmetric encryption support.
 * Implementation of the {@link DecryptionServer}, provides support for an
 * asymmetric encryption approach. The retrieved cipher versions are decrypted
 * with the provided private key and encrypted with a public key matching one of
 * the public keys. This wrapper assumes that the wrapped decryption server
 * passes encrypted ciphers in its cipher versions.
 */
public class PublicKeyDecryptionServerWrapper implements DecryptionServer {

	// /////////////////////////////////////////////////////////////////////////
	// STATICS:
	// /////////////////////////////////////////////////////////////////////////

	private static final Logger LOGGER = Logger.getLogger( PublicKeyDecryptionServerWrapper.class.getName() );

	// /////////////////////////////////////////////////////////////////////////
	// CONSTANTS:
	// /////////////////////////////////////////////////////////////////////////

	private static final String KEY_FILE_NAME_CONTAINS = "pub";
	private static final String KEY_FILE_NAME_SUFFIX = ".pem";

	// /////////////////////////////////////////////////////////////////////////
	// VARIABLES:
	// /////////////////////////////////////////////////////////////////////////

	private CipherVersionFactory _cipherVersionFactory;
	private final Map _signatureAlgorithmToEncryptAlgorithms = new HashMap<>();
	private DecryptionServer _decryptionServer;
	private AsymmetricAlgorithm _decryptAlgorithm;
	private Base64Converter _base64Converter;
	private String _privateKeyPath;

	// For logging (and debugging) only |-->
	private final Map _signatureAlgorithmToPublicKeyPath = new HashMap<>();
	// For logging (and debugging) only <--|

	// /////////////////////////////////////////////////////////////////////////
	// CONSTRUCTORS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Constructs the service with the required configuration. CAUTION:
	 * Regarding the public key files, only files having the suffix "*.pem" and
	 * which contain "pub" in the file name are considered for loading.
	 *
	 * @param aPrivateKeyPath The path on the file system to the private key to
	 *        be used for decrypting any ciphers passed by the decryption server
	 *        in CipherVersions retrieved from a storage.
	 * @param aPublicKeysPath The folder in which the public keys reside. As
	 *        there may be multiple files of which some are not public keys,
	 *        files which do not have the suffix "*.pem" and which have not the
	 *        term "pub" inside, are ignored.
	 * @param aDecryptionServer The decryption server from which to retrieve the
	 *        cipher versions
	 * @param aCipherVersionFactory The factory to be used for creating
	 *        {@link CipherVersion} instances.
	 * 
	 * @throws CryptException in case the cryptography algorithm had problems.
	 * @throws IOException in case of I/O problems
	 * @throws NoSuchAlgorithmException thrown in case the cryptographic
	 *         algorithm was not found.
	 * @throws InvalidKeySpecException thrown in case an invalid key spec has
	 *         been encountered.
	 */
	public PublicKeyDecryptionServerWrapper( String aPrivateKeyPath, String aPublicKeysPath, DecryptionServer aDecryptionServer, CipherVersionFactory aCipherVersionFactory ) throws CryptException, IOException, InvalidKeySpecException, NoSuchAlgorithmException {
		this( aPrivateKeyPath, null, aPublicKeysPath, aDecryptionServer, aCipherVersionFactory );
	}

	/**
	 * Constructs the service with the required configuration. CAUTION:
	 * Regarding the public key files, only files having the suffix "*.pem" and
	 * which contain "pub" in the file name are considered for loading.
	 *
	 * @param aPrivateKeyPath The path on the file system to the private key to
	 *        be used for decrypting any ciphers passed by the decryption server
	 *        in CipherVersions retrieved from a storage.
	 * @param aPublicKeysPath The folder in which the public keys reside. As
	 *        there may be multiple files of which some are not public keys,
	 *        files which do not have the suffix "*.pem" and which have not the
	 *        term "pub" inside, are ignored.
	 * @param aDecryptionServer The decryption server from which to retrieve the
	 *        cipher versions
	 * 
	 * @throws CryptException in case the cryptography algorithm had problems.
	 * @throws IOException in case of I/O problems
	 * @throws NoSuchAlgorithmException thrown in case the cryptographic
	 *         algorithm was not found.
	 * @throws InvalidKeySpecException thrown in case an invalid key spec has
	 *         been encountered.
	 */
	public PublicKeyDecryptionServerWrapper( String aPrivateKeyPath, String aPublicKeysPath, DecryptionServer aDecryptionServer ) throws CryptException, IOException, InvalidKeySpecException, NoSuchAlgorithmException {
		this( aPrivateKeyPath, null, aPublicKeysPath, aDecryptionServer, new CipherVersionFactoryImpl() );
	}

	/**
	 * Constructs the service with the required configuration. CAUTION:
	 * Regarding the public key files, only files having the suffix "*.pem" and
	 * which contain "pub" in the file name are considered for loading.
	 *
	 * @param aPrivateKeyPath The path on the file system to the private key to
	 *        be used for decrypting any ciphers passed by the decryption server
	 *        in CipherVersions retrieved from a storage.
	 * @param aPrivateKeyPassPhrase The pass phrase for decrypting the private
	 *        key.
	 * @param aPublicKeysPath The folder in which the public keys reside. As
	 *        there may be multiple files of which some are not public keys,
	 *        files which do not have the suffix "*.pem" and which have not the
	 *        term "pub" inside, are ignored.
	 * @param aDecryptionServer The decryption server from which to retrieve the
	 *        cipher versions
	 * 
	 * @throws CryptException in case the cryptography algorithm had problems.
	 * @throws IOException in case of I/O problems
	 * @throws NoSuchAlgorithmException thrown in case the cryptographic
	 *         algorithm was not found.
	 * @throws InvalidKeySpecException thrown in case an invalid key spec has
	 *         been encountered.
	 */
	public PublicKeyDecryptionServerWrapper( String aPrivateKeyPath, String aPrivateKeyPassPhrase, String aPublicKeysPath, DecryptionServer aDecryptionServer ) throws CryptException, IOException, InvalidKeySpecException, NoSuchAlgorithmException {
		this( aPrivateKeyPath, aPrivateKeyPassPhrase, aPublicKeysPath, aDecryptionServer, new CipherVersionFactoryImpl() );
	}

	/**
	 * Constructs the service with the required configuration. CAUTION:
	 * Regarding the public key files, only files having the suffix "*.pem" and
	 * which contain "pub" in the file name are considered for loading.
	 *
	 * @param aPrivateKeyPath The path on the file system to the private key to
	 *        be used for decrypting any ciphers passed by the decryption server
	 *        in CipherVersions retrieved from a storage.
	 * @param aPrivateKeyPassPhrase The pass phrase for decrypting the private
	 *        key.
	 * @param aPublicKeysPath The folder in which the public keys reside. As
	 *        there may be multiple files of which some are not public keys,
	 *        files which do not have the suffix "*.pem" and which have not the
	 *        term "pub" inside, are ignored.
	 * @param aDecryptionServer The decryption server from which to retrieve the
	 *        cipher versions
	 * @param aCipherVersionFactory The factory to be used for creating
	 *        {@link CipherVersion} instances.
	 * 
	 * @throws CryptException in case the cryptography algorithm had problems.
	 * @throws IOException in case of I/O problems
	 * @throws NoSuchAlgorithmException thrown in case the cryptographic
	 *         algorithm was not found.
	 * @throws InvalidKeySpecException thrown in case an invalid key spec has
	 *         been encountered.
	 */
	public PublicKeyDecryptionServerWrapper( String aPrivateKeyPath, String aPrivateKeyPassPhrase, String aPublicKeysPath, DecryptionServer aDecryptionServer, CipherVersionFactory aCipherVersionFactory ) throws CryptException, IOException, InvalidKeySpecException, NoSuchAlgorithmException {
		_cipherVersionFactory = aCipherVersionFactory;
		_privateKeyPath = aPrivateKeyPath;
		_decryptionServer = aDecryptionServer;

		// ---------------------
		// Load the private key:
		// ---------------------
		final File thePrivateKeyFile = new File( aPrivateKeyPath );
		LOGGER.log( Level.FINE, "Loading private key from file \"" + thePrivateKeyFile.getAbsolutePath() + "\"..." );
		try {
			final PrivateKey thePrivateKey;
			thePrivateKey = ForwardSecrecyUtility.readPrivateKey( thePrivateKeyFile, aPrivateKeyPassPhrase );
			_decryptAlgorithm = new RSA();
			_base64Converter = new Base64Converter();
			_decryptAlgorithm.setKey( thePrivateKey );
			_decryptAlgorithm.initDecrypt();
		}
		catch ( CryptException | FileNotFoundException e ) {
			LOGGER.log( Level.WARNING, "Unable to load private key from file \"" + thePrivateKeyFile.getAbsolutePath() + "\" as of: " + Trap.asMessage( e ), e );
			throw e;
		}
		// ---------------------
		// Load the public keys:
		// ---------------------
		final File thePublicKeysFile = new File( aPublicKeysPath );
		LOGGER.log( Level.FINE, "Loading public keys from directory \"" + thePublicKeysFile.getAbsolutePath() + "\"..." );
		try {
			PublicKey ePublicKey;
			RSA eEncryptAlgorithm;
			SignatureAlgorithm eSignatureAlgorithm;
			String eFileName;
			if ( !thePublicKeysFile.isDirectory() ) {
				final String theMessage = "No directory found for path  \"" + thePublicKeysFile.getAbsolutePath() + "\", though a directory is required!";
				final IOException e = new IOException( theMessage );
				LOGGER.log( Level.SEVERE, theMessage, e );
				throw e;
			}
			for ( File ePublicKeyFile : thePublicKeysFile.listFiles() ) {
				eFileName = ePublicKeyFile.getName();
				if ( eFileName != null && eFileName.toLowerCase().endsWith( KEY_FILE_NAME_SUFFIX ) && eFileName.toLowerCase().contains( KEY_FILE_NAME_CONTAINS ) ) {
					try {
						LOGGER.log( Level.FINE, "Loading public key from file \"" + ePublicKeyFile.getAbsolutePath() + "\"..." );
						ePublicKey = ForwardSecrecyUtility.readPublicKey( ePublicKeyFile );
						eEncryptAlgorithm = new RSA();
						eEncryptAlgorithm.setKey( ePublicKey );
						eEncryptAlgorithm.initEncrypt();
						eSignatureAlgorithm = new RSASignature( new SHA512() );
						eSignatureAlgorithm.setVerifyKey( ePublicKey );
						eSignatureAlgorithm.initVerify();
						_signatureAlgorithmToEncryptAlgorithms.put( eSignatureAlgorithm, eEncryptAlgorithm );
						_signatureAlgorithmToPublicKeyPath.put( eSignatureAlgorithm, ePublicKeyFile.getAbsolutePath() );
					}
					catch ( CryptException e ) {
						LOGGER.log( Level.WARNING, "Unable to load public key from file \"" + ePublicKeyFile.getAbsolutePath() + "\" as of: " + Trap.asMessage( e ), e );
						throw e;
					}
				}
				else {
					LOGGER.log( Level.FINE, "Ignoring non public key file \"" + ePublicKeyFile.getAbsolutePath() + "\" as it neither ends with \"" + KEY_FILE_NAME_SUFFIX + "\" nor it contains \"" + KEY_FILE_NAME_CONTAINS + "\" in its filename." );
				}
			}
		}
		catch ( FileNotFoundException e ) {
			LOGGER.log( Level.WARNING, "Unable to load private keys from folder \"" + thePublicKeysFile.getAbsolutePath() + "\" ass of: " + Trap.asMessage( e ), e );
			throw e;
		}
	}

	// /////////////////////////////////////////////////////////////////////////
	// METHODS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * {@inheritDoc}
	 */
	@Override
	public List getCipherVersions( String aNamespace, String aMessage, String aSignature ) throws SignatureVerificationException {
		try {
			final AsymmetricAlgorithm theEncryptAlgorithm = getEncryptAlgorithm( aMessage, aSignature );

			if ( theEncryptAlgorithm == null ) {
				throw new SignatureVerificationException( "Unable to verify the signature for the aMessage \"" + aMessage + "\" for namespace \"" + aNamespace + "\"!" );
			}
			final List theCipherVersionsFromServer = _decryptionServer.getCipherVersions( aNamespace, aMessage, aSignature );
			final List theCipherVersionsToService = new ArrayList<>();
			String eEncryptedCipher;
			String eDecryptedCipher;
			LOGGER.log( Level.FINE, "Using private key \"" + _privateKeyPath + "\" for decrypting a cipher, previously fetched public key encrypts afterwards ..." );
			for ( CipherVersion eCipherVersion : theCipherVersionsFromServer ) {
				eDecryptedCipher = new String( _decryptAlgorithm.decrypt( eCipherVersion.getCipher(), _base64Converter ) );
				eEncryptedCipher = theEncryptAlgorithm.encrypt( eDecryptedCipher.getBytes(), _base64Converter );
				eCipherVersion = _cipherVersionFactory.create( eCipherVersion.getUniversalId(), eEncryptedCipher );
				theCipherVersionsToService.add( eCipherVersion );
			}
			return theCipherVersionsToService;

		}
		catch ( CryptException e ) {
			throw new SignatureVerificationException( "Unable to verify the signature for the aMessage \"" + aMessage + "\" for namespace \"" + aNamespace + "\"!", e );
		}
	}

	// /////////////////////////////////////////////////////////////////////////
	// HELPER:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Returns the public key verifying the signature for the aMessage. The
	 * public key is used for encrypting the ciphers from the cipher versions.
	 * 
	 * @param aMessage A aMessage being signed by the requester of the cipher
	 *        versions
	 * @param aSignature The signature of the requester so that the according
	 *        public key for encryption can be determined and the origin can be
	 *        verified.
	 * 
	 * @return The public key verifying the signature of the aMessage.
	 * 
	 * @throws CryptException in case there was a problem verifying the
	 *         signature of the aMessage
	 */
	private AsymmetricAlgorithm getEncryptAlgorithm( String aMessage, String aSignature ) throws CryptException {
		for ( SignatureAlgorithm eSignatureAlgorithm : _signatureAlgorithmToEncryptAlgorithms.keySet() ) {
			if ( eSignatureAlgorithm.verify( aMessage.getBytes(), aSignature, _base64Converter ) ) {
				LOGGER.log( Level.FINE, "Using public key \"" + _signatureAlgorithmToPublicKeyPath.get( eSignatureAlgorithm ) + "\" for encryption identifying a signature ..." );
				return _signatureAlgorithmToEncryptAlgorithms.get( eSignatureAlgorithm );
			}
		}
		return null;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy