package org.openslx.satellitedaemon; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.util.Properties; import java.util.Random; import org.apache.log4j.Logger; import org.openslx.encryption.AsymKeyHolder; import org.openslx.satellitedaemon.filetransfer.ThriftConnection; import org.openslx.satellitedaemon.util.Util; public class Identity { private static Logger log = Logger.getLogger( Identity.class ); private static final Properties properties = new Properties(); private static AsymKeyHolder akh = null; public static String getOrganizationName() { return properties.getProperty( "ORGANIZATION_NAME" ); } private static BigInteger getModulus() { return toBigInt( properties.getProperty( "MODULUS" ) ); } private static BigInteger getPublicExponent() { return toBigInt( properties.getProperty( "PUBLIC_EXPONENT" ) ); } private static BigInteger getPrivateExponent() { return toBigInt( properties.getProperty( "PRIVATE_EXPONENT" ) ); } /** * Load properties */ static { InputStreamReader stream = null; try { // Load all entries of the config file into properties stream = new InputStreamReader( new FileInputStream( "config/identity.properties" ), StandardCharsets.UTF_8 ); properties.load( stream ); stream.close(); } catch ( IOException e ) { log.error( "Could not load identity.properties. Exiting." ); System.exit( 2 ); } finally { Util.streamClose( stream ); } Util.notNullOrEmptyFatal( getOrganizationName(), "Organiziation Name must not be empty!" ); try { akh = new AsymKeyHolder( getPrivateExponent(), getPublicExponent(), getModulus() ); } catch ( InvalidKeySpecException e ) { log.error( "InvalidKeySpecException", e ); } catch ( NoSuchAlgorithmException e ) { log.error( "NoSuchAlgorithmException", e ); } } /** * Get private key for this server. If none exists yet, create a new one. * * @return */ public static PrivateKey getPrivateKey() { if ( akh != null ) { return akh.getPrivateKey(); } akh = new AsymKeyHolder(); return akh.getPrivateKey(); } /** * Get public key for this server. If none exists yet, create a new one. * * @return */ public static PublicKey getPublicKey() { if ( akh != null ) return akh.getPublicKey(); akh = new AsymKeyHolder(); return akh.getPublicKey(); } /** * Get bit - length of key. * * @return */ public static int keySize( BigInteger modulus ) { return modulus.bitLength(); } /** * Checks if given modulus, private exponent and public exponent are valid * values for key pair. Idea is to encrypt and decrypt random text and compare * the result with initial text. * * @param mod * @param privExp * @param pubExp * @return True, if mod, privExp and pubExp are valid values. */ public static boolean isValidKeyPair( BigInteger mod, BigInteger privExp, BigInteger pubExp ) { // First check given values (modulus, privExp, pubExp). if ( ( mod == null ) || ( privExp == null ) || ( pubExp == null ) ) { log.error( "Given arguments not valid: got NULL for modulus, private or public exponent." ); return false; } // Testing encryption and description with given public and private key. // Idea: creating random text for encrypting and decrypting again. Random rnd = new Random(); int size = rnd.nextInt( keySize( mod ) - 1 ); BigInteger text = new BigInteger( size, rnd ); // Encrypt. BigInteger cipher = text.modPow( pubExp, mod ); // Decrypt again. BigInteger decrypted = cipher.modPow( privExp, mod ); boolean isPassed = text.equals( decrypted ); return isPassed; } /** * Generate new identity with given organization name and new key pair. * Write new identity to "config/identity.properties". * * @param organizationName * @return true, if successful. */ public static boolean generateIdentity( String organizationName ) { // generate new key pair. Identity.akh = new AsymKeyHolder(); return writeIdToFile( organizationName, akh.getModulus(), akh.getPrivateExponent(), akh.getPublicExponent() ); } /** * Import given identity with organization name, modulus, private and public * exponent and store this identity to "config/identity.properties". * * @param organizationName * @param modulus * @param privateExp * @param publicExp * @return true, if successful. */ public static boolean importIdentity( String organizationName, BigInteger modulus, BigInteger privateExp, BigInteger publicExp ) { return writeIdToFile( organizationName, modulus, privateExp, publicExp ); } /** * Submit new satellite - ipAddress to master with organizationId, ipAddress * and key - information. * @param ipAddress * @return true, if successful. */ public static boolean submitKey( String ipAddress ) { RSAPublicKey pubKey = (RSAPublicKey)getPublicKey(); RSAPrivateKey privKey = (RSAPrivateKey)getPrivateKey(); assert ( pubKey.getModulus() == privKey.getModulus() ); if ( !Identity.isValidKeyPair( privKey.getModulus(), privKey.getPrivateExponent(), pubKey.getPublicExponent() ) ) return false; return ThriftConnection.registerSatellite( getOrganizationName(), ipAddress, pubKey.getModulus().toString(), pubKey.getPublicExponent().toString() ); } /** * Update already existing satellite - ipAddress in master - Db. * @param ipAddress * @return true, if successful. */ public static boolean updateAddress( String ipAddress ) { return ThriftConnection.updateSatelliteAddress( ipAddress ); } /** * Write given organization name, modulus, public and private exponent to * "config/identity.properties". * * @param organizationName * @param modulus * @param privateExp * @param publicExp * @return true, if successful. */ private static boolean writeIdToFile( String organizationName, BigInteger modulus, BigInteger privateExp, BigInteger publicExp ) { File configFile = new File( "config/identity.properties" ); FileOutputStream stream = null; try { stream = new FileOutputStream( configFile ); } catch ( FileNotFoundException e ) { log.error( "FileNotFoundException", e ); return false; } // create strings for writing to file. String orgNameString = "ORGANIZATION_NAME=" + organizationName + "\n"; String modString = "MODULUS=" + modulus.toString() + "\n"; String privExpString = "PRIVATE_EXPONENT=" + privateExp.toString() + "\n"; String pubExpString = "PUBLIC_EXPONENT=" + publicExp.toString() + "\n"; try { stream.write( orgNameString.getBytes() ); stream.write( modString.getBytes() ); stream.write( privExpString.getBytes() ); stream.write( pubExpString.getBytes() ); return true; } catch ( IOException e ) { log.error( "IOException", e ); return false; } finally { try { stream.close(); } catch ( IOException e ) { } } } /** * Check modulus, privExp and pubExp for not being null. * * @return */ private static boolean checkMembers() { return ( ( getModulus() != null ) && ( getPrivateExponent() != null ) && ( getPublicExponent() != null ) ); } /** * Get BigInteger of read String number. * * @param str * @return */ private static BigInteger toBigInt( String str ) { try { return new BigInteger( str ); } catch ( Exception e ) { return null; } } }