Public Member Functions | Static Public Member Functions | Private Member Functions | Private Attributes | Static Private Attributes

crypto.AsymmetricCipher Class Reference

Implements asymmetric cipher with public and private keys used to retrieve secret key (used for symmetric ciphering of peer-to-peer datagram packets) from remote peer. More...

Collaboration diagram for crypto.AsymmetricCipher:
Collaboration graph
[legend]

List of all members.

Public Member Functions

 AsymmetricCipher ()
 Generates a pair of keys and serializes public key as Base64 string.
boolean isActive ()
 Returns if cipher is properly initialized.
String getSerializedAndSignedPublicKey ()
 Returns serialized public key used for encryption of datagrams as Base64 string.
void exportPublicKey (String fileName)
 Save public key into file.
String getNamedPublicKey ()
 Returns serializable named publicKey (with comment) encoded as Base64.
byte[] decrypt (byte[] cipherText)
 Decrypts cipher text using the private key.
SignedObject signObject (Serializable object)
 Signs object using private key.
SymmetricCipher deserializeEncryptedSecretKey (String serializedSecretKey)
 Reconstructs secret key from Base64 respresentation of encrypted (using our public key) serialized secret key.

Static Public Member Functions

static boolean saveObject (Serializable object, String fileName, String comment)
 Saves serializable object encoded in Base64 to file.
static Object loadObject (String fileName)
 Loads serializable object encoded in Base64 from file.

Private Member Functions

void destruct ()
 Destructs object (makes it inactive)
boolean loadSavedKeyPair ()
 Load saved key pair.
void saveKeyPair ()
 Saves private/public key pair with description (this.keyPairComment)
void generateKeyPair ()
 Generates a key pair.
void instantiateCipher ()
 Instantiates a cipher.
void serializePublicKey ()
 Serializes the public key, signs it and encodes in Base64 format.
boolean sanityCheck ()
 Sanity check whether PublicEncryptor works with PrivateEncryption.

Private Attributes

PrivateKey privateKey = null
 Private key used for deciphering and signing messages.
PublicKey publicKey = null
 Public key corresponding to our private key.
String keyPairComment = null
 The comment (description) of the key pair.
Cipher cipher = null
 Instance of the decrypting engine based on our private key.
String serializedPublicKey = null
 Our public key: serialized and encoded as Base64 string.

Static Private Attributes

static final String algorithm = "RSA"
 Asymmetric cipher algorithm.
static final int keySize = 1024
 Default key size for algorithm.
static final String padding = "/ECB/PKCS1PADDING"
 Padding to be used when ciphering/deciphering.
static final String digest = "SHA1"
 Message digest used for creating/validating signatures.
static final String privateKeyFile = "mykf-private-key.txt"
 The name of the file holding saved private key.
static final String publicKeyFile = "mykf-public-key.txt"
 The name of the file holding saved public key.

Detailed Description

Implements asymmetric cipher with public and private keys used to retrieve secret key (used for symmetric ciphering of peer-to-peer datagram packets) from remote peer.

Remote peer sends its secret key encrypted with our public key.

Transmission of the SecretKey can be schematically shown:

  Send secret key:
  secretKey >> serialize >> encrypt (with PubKey) >> encode to Base64 >> transmit
  Reconstruct secret key:
  receive >> decode from Base64 >> decrypt (with PrivKey) >> deserialize >> secretKey
  
Author:
Mikica B Kocic

Definition at line 50 of file AsymmetricCipher.java.


Constructor & Destructor Documentation

crypto.AsymmetricCipher.AsymmetricCipher (  )

Generates a pair of keys and serializes public key as Base64 string.

Definition at line 112 of file AsymmetricCipher.java.

References crypto.AsymmetricCipher.destruct(), crypto.AsymmetricCipher.exportPublicKey(), crypto.AsymmetricCipher.generateKeyPair(), crypto.AsymmetricCipher.instantiateCipher(), crypto.AsymmetricCipher.isActive(), crypto.AsymmetricCipher.loadSavedKeyPair(), crypto.AsymmetricCipher.sanityCheck(), crypto.AsymmetricCipher.saveKeyPair(), and crypto.AsymmetricCipher.serializePublicKey().

    {
        boolean loadedKeys = loadSavedKeyPair ();
        boolean sanity = false;
        
        while( true )
        {
            if ( ! loadedKeys  ) {
                generateKeyPair ();
            }
    
            instantiateCipher ();
            
            serializePublicKey ();
            
            if ( ! isActive () ) {
                destruct (); // make sure everyting is clean
                if ( loadedKeys ) {
                    Log.warn( "Load key pair inactive; Generating new key pair..." );
                    continue;
                } else {
                    Log.error( "AsymmetricCipher: Generated key pair inactive." );
                    return;
                }
            }
    
            sanity = sanityCheck ();
            if ( sanity ) {
                break; // everything is ok
            }
            
            if ( loadedKeys )  {
                Log.warn( "Sanity check failed on loaded key pair: Retrying..." );
                loadedKeys = false;
            } else {
                Log.error( "AsymmetricCipher: Sanity check failed on generated key pair." );
                destruct ();
                return;
            }
        }

        /* Save the key pair (if the key pair is generated i.e. not loaded)
         */
        if ( isActive () && sanity && ! loadedKeys ) {
            saveKeyPair ();
            exportPublicKey( null );
        }
    }

Member Function Documentation

byte [] crypto.AsymmetricCipher.decrypt ( byte[]  cipherText )

Decrypts cipher text using the private key.

Emulates CBC (cipher-block chaining) using plain ECB. Why? -- Because JCE does not support RSA/CBC cipher (only RSA/ECB).

See Cipher-block chaining (CBC)

cbc_decryption.png
See also:
PublicEncryptor.encrypt

Definition at line 512 of file AsymmetricCipher.java.

References crypto.AsymmetricCipher.cipher, and crypto.AsymmetricCipher.privateKey.

Referenced by crypto.AsymmetricCipher.deserializeEncryptedSecretKey(), and crypto.AsymmetricCipher.sanityCheck().

    {
        if ( this.cipher == null )  {
            return null;
        }
        
        byte[] output = null;
        
        synchronized( this.cipher )
        {
            try 
            {
                this.cipher.init( Cipher.DECRYPT_MODE, privateKey );

                int blockSize = cipher.getOutputSize( 1 );
                
                byte[] xorBlock = new byte[ blockSize ];
                
                ByteArrayOutputStream bOut = new ByteArrayOutputStream ();

                for ( int pos = 0; pos < cipherText.length; pos += blockSize ) 
                {
                    int len = Math.min( cipherText.length - pos, blockSize );
                    
                    byte[] plainBlock = this.cipher.doFinal( cipherText, pos, len );
                    
                    for ( int i = 0; i < plainBlock.length; ++i ) {
                        plainBlock[i] = (byte)( plainBlock[i] ^ xorBlock[i] );
                    }
                    
                    bOut.write( plainBlock );

                    System.arraycopy( cipherText, pos, xorBlock, 0, blockSize );
                }
                
                output = bOut.toByteArray ();
            }
            catch( Exception e )
            {
                Log.exception( Log.ERROR, e );
            }
        }

        return output;
    }
SymmetricCipher crypto.AsymmetricCipher.deserializeEncryptedSecretKey ( String  serializedSecretKey )

Reconstructs secret key from Base64 respresentation of encrypted (using our public key) serialized secret key.

Definition at line 598 of file AsymmetricCipher.java.

References crypto.AsymmetricCipher.decrypt(), and crypto.PublicEncryptor.verifyObject().

Referenced by crypto.CipherEngine.deserializeEncryptedSecretKey().

    {
        SymmetricCipher result = null;
        
        ByteArrayInputStream bIn = null; 
        ObjectInputStream oIn = null;
        
        try
        {
            byte[] cipherText = Base64.decode( serializedSecretKey );
            byte[] plainText = decrypt( cipherText );
            
            bIn = new ByteArrayInputStream( plainText );
            
            oIn = new ObjectInputStream( bIn );
            
            Object object = oIn.readObject ();
            
            oIn.close ();
            bIn.close ();

            String verificator = null;
            SignedObject signedObject = null;

            if ( object instanceof SignedObject ) 
            {
                signedObject = (SignedObject) object;
                verificator = PublicEncryptor.verifyObject( signedObject );
                object = signedObject.getObject ();
            }
            
            if ( object instanceof SecretKey ) 
            {
                result = new SymmetricCipher( (SecretKey)object, verificator );
            }
            else 
            {
                Log.error( "Invalid object when trying to deserialize encrypted secret key" );
            }
        }
        catch( Exception e ) 
        {
            Log.exception( Log.ERROR, e );
        }
        
        return result;
    }
void crypto.AsymmetricCipher.destruct (  ) [private]

Destructs object (makes it inactive)

Definition at line 164 of file AsymmetricCipher.java.

References crypto.AsymmetricCipher.cipher, crypto.AsymmetricCipher.privateKey, crypto.AsymmetricCipher.publicKey, and crypto.AsymmetricCipher.serializedPublicKey.

Referenced by crypto.AsymmetricCipher.AsymmetricCipher().

    {
        this.privateKey = null;
        this.publicKey = null;
        this.cipher = null;
        this.serializedPublicKey = null;
    }
void crypto.AsymmetricCipher.exportPublicKey ( String  fileName )

Save public key into file.

Parameters:
fileNamefile where to save public key; if null, it will be the default: AssymmetricCipher.publicKeyFile

Definition at line 358 of file AsymmetricCipher.java.

References crypto.CipherEngine.getPrivateKeyDirectory(), crypto.AsymmetricCipher.isActive(), crypto.AsymmetricCipher.keyPairComment, crypto.AsymmetricCipher.publicKey, crypto.AsymmetricCipher.publicKeyFile, and crypto.AsymmetricCipher.saveObject().

Referenced by crypto.AsymmetricCipher.AsymmetricCipher().

    {
        if ( ! isActive () ) {
            return;
        }

        if ( fileName == null ) {
            fileName = CipherEngine.getPrivateKeyDirectory()
                     + AsymmetricCipher.publicKeyFile;
        }

        if ( saveObject( 
                new NamedPublicKey( this.publicKey, this.keyPairComment ), 
                fileName,
                "  " + this.keyPairComment + "\n" ) )
        {
            Log.attn( "Public key exported to '" + fileName + "'" );
        }
    }
void crypto.AsymmetricCipher.generateKeyPair (  ) [private]

Generates a key pair.

Definition at line 239 of file AsymmetricCipher.java.

References crypto.AsymmetricCipher.algorithm, crypto.AsymmetricCipher.keyPairComment, crypto.AsymmetricCipher.keySize, crypto.AsymmetricCipher.privateKey, and crypto.AsymmetricCipher.publicKey.

Referenced by crypto.AsymmetricCipher.AsymmetricCipher().

    {
        this.privateKey = null;
        this.publicKey = null;
        this.keyPairComment = null;
        
        try
        {
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance( algorithm );
            keyGen.initialize( keySize );
            
            KeyPair keyPair = keyGen.generateKeyPair ();
            this.privateKey = keyPair.getPrivate ();
            this.publicKey  = keyPair.getPublic ();
            
            /* Default comment is name of the cipher plus time-stamp 
             */
            Calendar cal = Calendar.getInstance ();
            SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd-HHmmssSSS" );
            this.keyPairComment = algorithm.toLowerCase () + "-key-" 
                                + sdf.format( cal.getTime() ); 

            Log.attn( "Generated a new " + algorithm + "/" 
                    + keySize + " key pair: '" + this.keyPairComment + "'" );
        }
        catch( NoSuchAlgorithmException e )
        {
            StringBuffer algos = new StringBuffer( "Available algorithms:" );
            
            for( String s : Security.getAlgorithms( "Cipher" ) ) {
                algos.append( " " ).append( s );
            }
            
            Log.exception( Log.ERROR, e );
            Log.warn( algos.toString () );
        }
    }
String crypto.AsymmetricCipher.getNamedPublicKey (  )

Returns serializable named publicKey (with comment) encoded as Base64.

Definition at line 381 of file AsymmetricCipher.java.

References crypto.AsymmetricCipher.keyPairComment, and crypto.AsymmetricCipher.publicKey.

Referenced by crypto.CipherEngine.getNamedPublicKey().

    {
        StringBuffer sb = new StringBuffer ();

        try {
            String encodedKey = Base64.encodeObject( new NamedPublicKey( 
                    this.publicKey, this.keyPairComment ), Base64.GZIP );
            
            sb.append( encodedKey );
            sb.append( " " );
            sb.append( this.keyPairComment );
        }
        catch( IOException e )
        {
            Log.exception( Log.ERROR, e );
        }

        return sb.toString ();
    }
String crypto.AsymmetricCipher.getSerializedAndSignedPublicKey (  )

Returns serialized public key used for encryption of datagrams as Base64 string.

Definition at line 347 of file AsymmetricCipher.java.

References crypto.AsymmetricCipher.serializedPublicKey.

Referenced by crypto.CipherEngine.getSignedPublicKey().

    {
        return this.serializedPublicKey;
    }
void crypto.AsymmetricCipher.instantiateCipher (  ) [private]

Instantiates a cipher.

Definition at line 280 of file AsymmetricCipher.java.

References crypto.AsymmetricCipher.algorithm, crypto.AsymmetricCipher.cipher, crypto.AsymmetricCipher.padding, crypto.AsymmetricCipher.privateKey, and crypto.AsymmetricCipher.publicKey.

Referenced by crypto.AsymmetricCipher.AsymmetricCipher().

    {
        this.cipher = null;

        if ( this.privateKey == null || this.publicKey == null ) {
            return; // must have key pair
        }

        try
        {
            this.cipher = Cipher.getInstance( algorithm + padding );

            Log.trace( "Instantiated asymmetric cipher: " + this.cipher.getAlgorithm () );
        }
        catch( NoSuchAlgorithmException e )
        {
            StringBuffer algos = new StringBuffer( "Available algorithms:" );
            
            for( String s : Security.getAlgorithms( "Cipher" ) ) {
                algos.append( " " ).append( s );
            }
            
            Log.exception( Log.ERROR, e );
            Log.warn( algos.toString () );
        }
        catch( NoSuchPaddingException e )
        {
            Log.exception( Log.ERROR, e );
        }
    }
boolean crypto.AsymmetricCipher.isActive (  )
static Object crypto.AsymmetricCipher.loadObject ( String  fileName ) [static]

Loads serializable object encoded in Base64 from file.

Definition at line 437 of file AsymmetricCipher.java.

Referenced by crypto.AsymmetricCipher.loadSavedKeyPair().

    {
        Object object = null;

        try {
            StringBuffer sb = new StringBuffer ();
            
            BufferedReader in = new BufferedReader( new FileReader( fileName ) );
            String line;
            while( ( line = in.readLine () ) != null ) {
                sb.append( line );
            }
            in.close ();

            object = Base64.decodeToObject( sb.toString () );
        }
        catch( FileNotFoundException e )
        {
            Log.exception( Log.TRACE, e );
        }
        catch( IOException e )
        {
            Log.exception( Log.WARN, e );
        }
        catch( ClassNotFoundException e )
        {
            Log.exception( Log.ERROR, e );
        }

        return object;
    }
boolean crypto.AsymmetricCipher.loadSavedKeyPair (  ) [private]

Load saved key pair.

Definition at line 175 of file AsymmetricCipher.java.

References crypto.NamedKeyPair.comment, crypto.CipherEngine.getPrivateKeyDirectory(), crypto.AsymmetricCipher.keyPairComment, crypto.AsymmetricCipher.loadObject(), crypto.NamedKeyPair.privateKey, crypto.AsymmetricCipher.privateKey, crypto.AsymmetricCipher.privateKeyFile, crypto.NamedKeyPair.publicKey, and crypto.AsymmetricCipher.publicKey.

Referenced by crypto.AsymmetricCipher.AsymmetricCipher().

    {
        this.privateKey = null;
        this.publicKey = null;
        
        String keyFilePath = CipherEngine.getPrivateKeyDirectory() 
                           + AsymmetricCipher.privateKeyFile;

        Object oPair = loadObject( keyFilePath );
        
        if ( oPair != null && ( oPair instanceof NamedKeyPair ) ) 
        {
            NamedKeyPair keyPair = (NamedKeyPair) oPair;
            this.privateKey = keyPair.privateKey;
            this.publicKey = keyPair.publicKey;
            this.keyPairComment = keyPair.comment;
            
            Log.attn( "Loaded private key '" + this.keyPairComment 
                    + "' from file '" + AsymmetricCipher.privateKeyFile + "'" );
        }

        return ( this.privateKey != null && this.publicKey != null ); 
    }
boolean crypto.AsymmetricCipher.sanityCheck (  ) [private]

Sanity check whether PublicEncryptor works with PrivateEncryption.

Definition at line 472 of file AsymmetricCipher.java.

References crypto.AsymmetricCipher.decrypt(), crypto.PublicEncryptor.encrypt(), and crypto.AsymmetricCipher.serializedPublicKey.

Referenced by crypto.AsymmetricCipher.AsymmetricCipher().

    {
        /* Random data 
         */
        byte[] plainText = new byte[ 2048 ];
        for ( int i = 0; i < plainText.length; ++i ) {
            plainText[i] = (byte)( Math.random() * 256 );
        }
        
        /* Simulate encryption at remote end and local decryption
         */
        PublicEncryptor testPublic = new PublicEncryptor( this.serializedPublicKey, null );
        byte[] cipherText = testPublic.encrypt( plainText );
        byte[] output =  this.decrypt( cipherText );
        
        if ( ! java.util.Arrays.equals( plainText, output ) )
        {
            Log.error( "Public encryption / private decryption sanity check failed." );

            Log.trace( Log.toHex( plainText ) );
            Log.trace( Log.toHex( cipherText ) );
            Log.trace( Log.toHex( output ) );
            
            return false;
        }
        
        return true;
    }
void crypto.AsymmetricCipher.saveKeyPair (  ) [private]

Saves private/public key pair with description (this.keyPairComment)

Definition at line 202 of file AsymmetricCipher.java.

References crypto.CipherEngine.getPrivateKeyDirectory(), crypto.AsymmetricCipher.isActive(), crypto.AsymmetricCipher.keyPairComment, crypto.AsymmetricCipher.privateKey, crypto.AsymmetricCipher.privateKeyFile, crypto.AsymmetricCipher.publicKey, and crypto.AsymmetricCipher.saveObject().

Referenced by crypto.AsymmetricCipher.AsymmetricCipher().

    {
        if ( ! isActive () ) {
            return;
        }

        String keyFilePath = CipherEngine.getPrivateKeyDirectory() 
                           + AsymmetricCipher.privateKeyFile;
        
        if ( saveObject( 
                new NamedKeyPair( this.publicKey, this.privateKey, this.keyPairComment ),
                keyFilePath, null ) )
        {
            Log.attn( "Private key saved as '" + keyFilePath + "'" );
            
            /* Change file permissions using native OS 'chmod' command (ignoring Windows), 
             * so that no one but the owner might read its contents.
             */
            String osName = System.getProperty( "os.name" ).toLowerCase();
            if ( ! osName.matches( "^.*windows.*$" ) ) 
            {
                try 
                {
                    Runtime.getRuntime().exec( new String[] { "chmod", "400", keyFilePath } );
                }
                catch( IOException e ) 
                {
                    Log.trace( "Failed to do chmod; OS = " + osName );
                    Log.exception( Log.TRACE, e );
                }
            }
        }
    }
static boolean crypto.AsymmetricCipher.saveObject ( Serializable  object,
String  fileName,
String  comment 
) [static]

Saves serializable object encoded in Base64 to file.

Definition at line 404 of file AsymmetricCipher.java.

Referenced by crypto.AsymmetricCipher.exportPublicKey(), and crypto.AsymmetricCipher.saveKeyPair().

    {
        boolean result = false;

        try {
            String text = Base64.encodeObject( object, Base64.GZIP );
            
            BufferedWriter out = new BufferedWriter( new FileWriter( fileName ) );
            
            out.write( text );
            
            if ( comment != null ) {
                out.write( comment );
            }

            out.flush ();
            out.close ();
            
            Log.trace( "Saved " + object.getClass().toString () + " into " + fileName );
            
            result = true;
        }
        catch( IOException e )
        {
            Log.exception( Log.ERROR, e );
        }

        return result;
    }
void crypto.AsymmetricCipher.serializePublicKey (  ) [private]

Serializes the public key, signs it and encodes in Base64 format.

Definition at line 314 of file AsymmetricCipher.java.

References crypto.AsymmetricCipher.cipher, crypto.AsymmetricCipher.privateKey, crypto.AsymmetricCipher.publicKey, crypto.AsymmetricCipher.serializedPublicKey, and crypto.AsymmetricCipher.signObject().

Referenced by crypto.AsymmetricCipher.AsymmetricCipher().

    {
        if ( this.privateKey == null || this.publicKey == null || this.cipher == null ) {
            return; // must have key pair and cipher
        }

        try
        {
            SignedObject signedPublicKey = this.signObject( this.publicKey );
  
            this.serializedPublicKey = Base64.encodeObject( signedPublicKey, Base64.GZIP );

            Log.trace( "Serialized Public Key in Base64; length = " 
                    + this.serializedPublicKey.length () );
        }
        catch( IOException e )
        {
            Log.exception( Log.ERROR, e );
        }
    }
SignedObject crypto.AsymmetricCipher.signObject ( Serializable  object )

Signs object using private key.

Definition at line 561 of file AsymmetricCipher.java.

References crypto.AsymmetricCipher.digest, and crypto.AsymmetricCipher.privateKey.

Referenced by crypto.CipherEngine.getSignedSecretKey(), and crypto.AsymmetricCipher.serializePublicKey().

    {
        String signatureAlgorithm = digest + "with" + this.privateKey.getAlgorithm ();
        
        Signature signature = null;
        SignedObject result = null;

        try 
        {
            signature = Signature.getInstance( signatureAlgorithm );
            signature.initSign( this.privateKey );
            result = new SignedObject( object, this.privateKey, signature ); 
        }
        catch( NoSuchAlgorithmException e )
        {
            Log.exception( Log.ERROR, e );
        }
        catch( InvalidKeyException e )
        {
            Log.exception( Log.ERROR, e );
        }
        catch( SignatureException e )
        {
            Log.exception( Log.ERROR, e );
        }
        catch( IOException e )
        {
            Log.exception( Log.ERROR, e );
        }
        
        return result;
    }

Member Data Documentation

final String crypto.AsymmetricCipher.algorithm = "RSA" [static, private]

Asymmetric cipher algorithm.

Definition at line 55 of file AsymmetricCipher.java.

Referenced by crypto.AsymmetricCipher.generateKeyPair(), and crypto.AsymmetricCipher.instantiateCipher().

Cipher crypto.AsymmetricCipher.cipher = null [private]
final String crypto.AsymmetricCipher.digest = "SHA1" [static, private]

Message digest used for creating/validating signatures.

Definition at line 72 of file AsymmetricCipher.java.

Referenced by crypto.AsymmetricCipher.signObject().

String crypto.AsymmetricCipher.keyPairComment = null [private]
final int crypto.AsymmetricCipher.keySize = 1024 [static, private]

Default key size for algorithm.

Definition at line 60 of file AsymmetricCipher.java.

Referenced by crypto.AsymmetricCipher.generateKeyPair().

final String crypto.AsymmetricCipher.padding = "/ECB/PKCS1PADDING" [static, private]

Padding to be used when ciphering/deciphering.

JCE does not support RSA/CBC so the CBC mode is built on the top of ECB in AsymmetricCipher.decrypt().

Definition at line 67 of file AsymmetricCipher.java.

Referenced by crypto.AsymmetricCipher.instantiateCipher().

PrivateKey crypto.AsymmetricCipher.privateKey = null [private]
final String crypto.AsymmetricCipher.privateKeyFile = "mykf-private-key.txt" [static, private]

The name of the file holding saved private key.

Definition at line 77 of file AsymmetricCipher.java.

Referenced by crypto.AsymmetricCipher.loadSavedKeyPair(), and crypto.AsymmetricCipher.saveKeyPair().

PublicKey crypto.AsymmetricCipher.publicKey = null [private]
final String crypto.AsymmetricCipher.publicKeyFile = "mykf-public-key.txt" [static, private]

The name of the file holding saved public key.

Definition at line 82 of file AsymmetricCipher.java.

Referenced by crypto.AsymmetricCipher.exportPublicKey().


The documentation for this class was generated from the following file: