org.refcodes.forwardsecrecy.PublicKeyDecryptionServerWrapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of refcodes-forwardsecrecy Show documentation
Show all versions of refcodes-forwardsecrecy Show documentation
Artifact for the refcodes forward secrecy framework design.
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;
}
}