• Main Page
  • Related Pages
  • Packages
  • Classes
  • Files
  • File List

crypto/SymmetricCipher.java

Go to the documentation of this file.
00001 
00002 package crypto;
00003 
00004 import java.io.IOException;
00005 import java.io.UnsupportedEncodingException;
00006 import java.security.InvalidParameterException;
00007 import java.security.NoSuchAlgorithmException;
00008 
00009 import javax.crypto.Cipher;
00010 import javax.crypto.KeyGenerator;
00011 import javax.crypto.NoSuchPaddingException;
00012 import javax.crypto.SecretKey;
00013 import javax.crypto.spec.IvParameterSpec;
00014 
00015 import utils.Base64;
00016 import utils.Log;
00017 
00018 /**
00019  *  Instances of the Symmetric cipher class arew used to cipher peer-to-peer datagram 
00020  *  packets. Cipher's secret key is exchanged using asymmetric cipher.
00021  *  
00022  *  @author Mikica B Kocic
00023  */
00024 public class SymmetricCipher
00025 {
00026     /**
00027      *  Cipher-Block Chaining mode.
00028      *  \see http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation
00029      */
00030     private final static String mode = "/CBC";
00031     
00032     /**
00033      *  PKCS5 padding.
00034      *  \see http://www.ietf.org/rfc/rfc2898.txt
00035      */
00036     private final static String padding = "/PKCS5Padding";
00037 
00038     /**
00039      *  Instance of the cipher used to encrypt/decrypt data.
00040      */
00041     private Cipher cipher = null;
00042     
00043     /**
00044      *  Secret key used to encrypt/decrypt data.
00045      */
00046     private SecretKey secretKey = null;
00047 
00048     /**
00049      *  Contains name of the verificator (i.e the name associated with authorized public
00050      *  key that has verified this public key). Not null indicates that the public key 
00051      *  was successfully verified.
00052      */
00053     private String verificator = null;
00054 
00055     /**
00056      *  Wraps existing secret key with information about verificatory (if any).
00057      */
00058     public SymmetricCipher( SecretKey secretKey, String verificator )
00059     {
00060         this.secretKey = secretKey;
00061         this.verificator = verificator;
00062         
00063         try
00064         {
00065             this.cipher = Cipher.getInstance( secretKey.getAlgorithm () + mode + padding );
00066             
00067             Log.trace( "New remote symmetric cipher: " + this.cipher.getAlgorithm () );
00068         }
00069         catch( NoSuchAlgorithmException e )
00070         {
00071             Log.exception( Log.ERROR, e );
00072         }
00073         catch( NoSuchPaddingException e )
00074         {
00075             Log.exception( Log.ERROR, e );
00076         }
00077         
00078         if ( this.cipher == null ) {
00079             this.secretKey = null;
00080         }
00081     }
00082 
00083     /**
00084      *  Generates a new secret key using specified algorithm and key size.
00085      */
00086     public SymmetricCipher( String algorithm, int keySize, boolean attnReport )
00087     {
00088         this.secretKey = null;
00089         
00090         try
00091         {
00092             KeyGenerator keyGen = KeyGenerator.getInstance( algorithm );
00093             keyGen.init( keySize );
00094             this.secretKey = keyGen.generateKey ();
00095             
00096             this.cipher = Cipher.getInstance( secretKey.getAlgorithm () + mode + padding );
00097             
00098             Log.trace( "New local symmetric cipher: " + this.cipher.getAlgorithm () );
00099             
00100             if ( attnReport ) {
00101                 Log.attn( "New symmetric cipher: " + algorithm + "/" + keySize );
00102             }
00103         }
00104         catch( InvalidParameterException e )
00105         {
00106             Log.exception( Log.ERROR, e );
00107             if ( attnReport ) {
00108                 Log.attn( "Error: Invalid parameter: " + e.getMessage () );
00109             }
00110         }
00111         catch( NoSuchAlgorithmException e )
00112         {
00113             Log.exception( Log.ERROR, e );
00114             if ( attnReport ) {
00115                 Log.attn( "Error: No such algorithm: " + e.getMessage () );
00116             }
00117         }
00118         catch( NoSuchPaddingException e )
00119         {
00120             Log.exception( Log.ERROR, e );
00121             if ( attnReport ) {
00122                 Log.attn( "Error: No such padding: " + e.getMessage () );
00123             }
00124         }
00125 
00126         if ( this.cipher == null ) {
00127             this.secretKey = null;
00128         }
00129     }
00130 
00131     /**
00132      *  Returns secret key.
00133      */
00134     public SecretKey getSecretKey ()
00135     {
00136         return secretKey;
00137     }
00138     
00139     /**
00140      *  Returns if cipher is properly initialized
00141      */
00142     public boolean isActive ()
00143     {
00144         return this.cipher != null;
00145     }
00146 
00147     /**
00148      *  Returns if public key was verified
00149      */
00150     public boolean isVerified ()
00151     {
00152         return this.verificator != null;
00153     }
00154 
00155     /**
00156      *  Returns if name of the verificator from authorized keys that verified this public key
00157      *  
00158      *  @return name of the verificator; May be null indicating not verified public key
00159      */
00160     public String getVerificatorName ()
00161     {
00162         return this.verificator;
00163     }
00164     
00165     /**
00166      *  Returns description of the cipher algorithm
00167      */
00168     public String getAlgorithmDesc ()
00169     {
00170         if ( secretKey == null ) {
00171             return "[Inactive]";
00172         }
00173         
00174         return this.secretKey.getAlgorithm ();
00175     }
00176 
00177     /**
00178      *  Encrypts random preamble of the given length appended with
00179      *  the input plain text.
00180      */
00181     public byte[] encrypt( int randomPreambleLen, byte[] plainText )
00182     {
00183         if ( this.cipher == null ) {
00184             return null;
00185         }
00186         
00187         /* Generate random preamble */
00188         byte[] preamble = new byte[ randomPreambleLen ];
00189         for ( int i = 0; i < randomPreambleLen; ++i ) {
00190             preamble[i] = (byte)( Math.random () * 0x100 - 0x100 );
00191         }
00192 
00193         /* IV specification for the CBC */
00194         byte[] ivBytes = new byte[8];
00195         IvParameterSpec ivSpec = new IvParameterSpec( ivBytes );
00196 
00197         byte[] cipherText = null;
00198 
00199         synchronized( this.cipher )
00200         {
00201             try
00202             {
00203                 this.cipher.init( Cipher.ENCRYPT_MODE, this.secretKey, ivSpec );
00204                 
00205                 int ptLength = ivBytes.length + preamble.length + plainText.length;
00206                 cipherText = new byte[ cipher.getOutputSize( ptLength ) ];
00207 
00208                 int ctLength = cipher.update( ivBytes, 0, ivBytes.length, cipherText, 0 );
00209 
00210                 ctLength += cipher.update( preamble, 0, preamble.length, cipherText, ctLength );
00211                 
00212                 ctLength += cipher.update( plainText, 0, plainText.length, cipherText, ctLength );
00213 
00214                 ctLength += cipher.doFinal( cipherText, ctLength );
00215             }
00216             catch( Exception e )
00217             {
00218                 Log.exception( Log.PDU, e );
00219             }
00220         }
00221         
00222         return cipherText;
00223     }
00224 
00225     /**
00226      *  Decrypts cipher text first then discards random preamble of the given length.
00227      */
00228     public byte[] decrypt( int randomPreambleLen, byte[] cipherText )
00229     {
00230         if ( this.cipher == null ) {
00231             return null;
00232         }
00233 
00234         byte[] plainText = null;
00235         
00236         synchronized( this.cipher )
00237         {
00238             try
00239             {
00240                 byte[] ivBytes = new byte[8];
00241                 IvParameterSpec ivSpec = new IvParameterSpec( ivBytes );
00242 
00243                 this.cipher.init( Cipher.DECRYPT_MODE, this.secretKey, ivSpec );
00244                 
00245                 byte[] buf = new byte[ cipher.getOutputSize( cipherText.length ) ];
00246 
00247                 int bufLen = cipher.update( cipherText, 0, cipherText.length, buf, 0 );
00248 
00249                 bufLen += cipher.doFinal( buf, bufLen );
00250 
00251                 /* Remove the IV and random preamble from the start of the message
00252                  */
00253                 plainText = new byte[ bufLen - ivBytes.length - randomPreambleLen ];
00254 
00255                 System.arraycopy( buf, ivBytes.length + randomPreambleLen, plainText, 0, plainText.length );
00256             }
00257             catch( Exception e )
00258             {
00259                 Log.exception( Log.PDU, e );
00260             }
00261         }
00262         
00263         return plainText;
00264     }
00265     
00266     /**
00267      *  Encrypts text message with random preamble and returns Base64 encoded
00268      *  cipher text.
00269      */
00270     public String encrypt( String plainText )
00271     {
00272         String encodedCipherText = null;
00273 
00274         /* Send encrypted message to peer 
00275          */
00276         try 
00277         {
00278             byte[] plainBin = ( "[BEGIN]" + plainText ).getBytes( "UTF8" );
00279 
00280             byte[] cipherText = this.encrypt( /*randomPreambleLen*/ 256, plainBin );
00281             
00282             if ( cipherText != null ) 
00283             {
00284                 encodedCipherText = Base64.encodeBytes( cipherText );
00285             }
00286         }
00287         catch( UnsupportedEncodingException e )
00288         {
00289             Log.exception( Log.TRACE, e );
00290         }
00291         
00292         return encodedCipherText;
00293     }
00294     
00295     /**
00296      *  Decodes Base64 encoded cipher text, decrypts text message and discards
00297      *  random preamble.
00298      */
00299     public String decrypt( String encodedCipherText )
00300     {
00301         String clearText = null;
00302         
00303         try 
00304         {
00305             byte[] cipherText = Base64.decode( encodedCipherText );
00306             byte[] data = this.decrypt( /*randomPreambleLen*/ 256, cipherText );
00307             if ( data != null ) 
00308             {
00309                 String msg = new String( data, "UTF8" );
00310                 if ( msg.startsWith( "[BEGIN]" ) ) 
00311                 {
00312                     clearText = msg.substring( 7 ); // skip prefix
00313                 }
00314             }
00315         }
00316         catch( UnsupportedEncodingException e ) 
00317         {
00318             Log.exception( Log.TRACE, e );
00319         }
00320         catch( IOException e )
00321         {
00322             Log.exception( Log.TRACE, e );
00323         }
00324         
00325         return clearText;
00326     }
00327 }

Generated on Thu Dec 16 2010 14:44:42 for VoIP Kryptofon by  doxygen 1.7.2