/*::.
==================================================================================================================================
=================================================¦ Copyright © 2004 Allen Baker ¦=================================================
                                                 +------------------------------+
File:       BaseCipher.java
Originator: Allen Baker (2004.02.01)
LayoutRev:  5
================================================================================================================================== */



/*.
==========================================================================================
Package
------------------------------------------------------------------------------------------ */
package cosmicabyss.com.lib;



/*.
==========================================================================================
Imports
------------------------------------------------------------------------------------------ */
import java.io.*;
import java.util.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.lang.reflect.*;


/*::
======================================================================================================================== *//**
This class ...

<P>
   <DL>
      <DT>
         <B>
            Example usage:
         </B>
         <DD>
            <BLOCKQUOTE>
               <PRE id="unindent">
                  no example provided
               </PRE>
            </BLOCKQUOTE>
         </DD>
      </DT>
      <DT>
         <B>
            View Source:
         </B>
         <DD>
            <A href="BaseCipher.java.html">
               BaseCipher.java
            </A>
         </DD>
      </DT>
      <DT>
         <B>
            Author:
         </B>
         <DD>
            <A href="mailto:sourcecode.v01@cosmicabyss.com">
               Allen Baker
            </A>
         </DD>
      </DT>
   </DL>
*//*
======================================================================================================================== */
public class BaseCipher
   {



   /*:.
   ==============================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Initialization  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ============================================================================================================== */



   static
      {
//      /*.
//      ==========================================================================================
//      Make sure all the class constants and variables are initialized the first time the JVM
//      loads this class code and before anything else in this class is accessed
//      ------------------------------------------------------------------------------------------ */
//      initializeClassConstantsAndVariables();
      /*.
      ==========================================================================================
      Adds the BouncyCastle provider to the next position available. This statement is in lieu
      of configuring the BouncyCastle provider into the Java environment according to the
      following procedure:

         The provider can also be configured as part of your environment via static registration
         by adding an entry to the java.security properties file:

            (found in $JAVA_HOME/jre/lib/security/java.security, where $JAVA_HOME is the
            location of your JDK/JRE distribution).

         You'll find detailed instructions in the file but basically it comes down to adding a
         line:

            Security.provider.<n>=org.bouncycastle.jce.provider.BouncyCastleProvider

         Where <n> is the preference you want the provider at (1 being the most prefered).
      ------------------------------------------------------------------------------------------ */
//      Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
//      removeCryptographyRestrictions();
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   Due to import control restrictions by the governments of a few countries, the jurisdiction policy
   files shipped with the JDK specify that "strong" but limited cryptography may be used. That means
   the JDK has a deliberate key size restriction by default. So you cannot perform an encryption with
   key of more than 128 bits.

   This method bypasses these bufoonish Governments' inefficacious requirements, and installs
   "Unlimited Strength" JCE policy files when deploying an application that uses larger keys.

   From
      <BLOCKQUOTE>
         <PRE id="unindent">
            http://stackoverflow.com/questions/1179672/how-to-avoid-installing-unlimited-strength-jce-policy-files-when-deploying-an
         </PRE>
      </BLOCKQUOTE>

   Here is why it is needed:

      I have an app that uses 256-bit AES encryption which is not supported by Java out of the box. I
      know to get this to function correctly, I install the JCE unlimited strength jars in the security
      folder. This is fine for me being the developer, I can install them.

      However, since this app will be distributed, end users most likely will not have these policy
      files installed. Having the end user download these just to make the app function is not an
      attractive solution.

      Is there a way to make my app run without overwriting files on the end user machine? A third
      party software that can handle it without the policy files installed? Or a way to just reference
      these policy files from within a JAR?

   Here are the settings that the Unlimited Strength JCE Policy file uses to eliminate the
   restrictions:
      <BLOCKQUOTE>
         <PRE id="unindent">
            JceSecurity.isRestricted = false;
            JceSecurity.defaultPolicy.perms.clear();
            JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
         </PRE>
      </BLOCKQUOTE>

   And here's how this code bypasses the restriction:

      It uses reflection to make directly the modifications that would be made by the Java platform if
      the Unlimited Strength JCE Policy file were installed.

      Simply call removeCryptographyRestrictions() from a static initializer or such before performing
      any cryptographic operations.

      The JceSecurity.isRestricted = false part is all that is needed to use 256-bit ciphers directly;
      however, without the two other operations, Cipher.getMaxAllowedKeyLength() will still keep
      reporting 128, and 256-bit TLS cipher suites won't work.

      This code works on Oracle JRE 7 and 8, and automatically skips the process on OpenJDK where it's
      not needed. Being an ugly hack after all, it likely doesn't work on other vendors' VMs.

      It also doesn't work on Oracle JRE 6, because the private JCE classes are obfuscated there. The
      obfuscation does not change from version to version though, so it is technically possible to
      support JRE 6 too.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#000">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   private static void removeCryptographyRestrictions()
      {
      if (!isRestrictedCryptography())
         {
         cOut.println("Cryptography restrictions removal not needed");
         return;
         }
      try
         {
         /*.
         ==========================================================================================
         Do the following, but with reflection to bypass access checks:
            <BLOCKQUOTE>
               <PRE id="unindent">
                  JceSecurity.isRestricted = false;
                  JceSecurity.defaultPolicy.perms.clear();
                  JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
               </PRE>
            </BLOCKQUOTE>
         ------------------------------------------------------------------------------------------ */
         final Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
         final Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
         final Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");

         final Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
         isRestrictedField.setAccessible(true);
         isRestrictedField.set(null, false);

         final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
         defaultPolicyField.setAccessible(true);
         final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);

         final Field perms = cryptoPermissions.getDeclaredField("perms");
         perms.setAccessible(true);
         ((Map<?, ?>) perms.get(defaultPolicy)).clear();

         final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
         instance.setAccessible(true);
         defaultPolicy.add((Permission) instance.get(null));

         System.out.println("Successfully removed cryptography restrictions");
         }
      catch (final Exception e)
         {
         System.out.println("Failed to remove cryptography restrictions\n\t" + e);
         }
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns true if the JRE on which this program is running restricts cryptography.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#001">View source</A>

   @return
      This method returns true if the JRE on which this program is running restricts cryptography,
      otherwise, it returns false.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private static boolean isRestrictedCryptography()
      {
      /*.
      ==========================================================================================
      This simply matches the Oracle JRE, but not OpenJDK.
      ------------------------------------------------------------------------------------------ */
      return "Java(TM) SE Runtime Environment".equals(System.getProperty("java.runtime.name"));
      }



   /*:.
   ==============================================================================================================
   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[  Methods  ]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   ============================================================================================================== */



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the BaseCipher class.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#002">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public BaseCipher() throws Exception
      {
      iPassword         = new Password();
      iCipherIterations = DFLT_ITER;
      iSalt             = initializedSalt(iPassword.xstring());
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the BaseCipher class.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#003">View source</A>

   @param
      pPassword is the password that will be used to encrypt and/or decrypt the file.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public BaseCipher
      (
      Password  pPassword
      )
      throws Exception
      {
      if (pPassword == null)
         {
         throw new Exception("pPassword is null");
         }
      iPassword         = pPassword;
      iCipherIterations = DFLT_ITER;
      iSalt             = initializedSalt(iPassword.xstring());
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the BaseCipher class.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#004">View source</A>

   @param
      pPassword is the password that will be used to encrypt and/or decrypt the file.
   @param
      pCipherIterations is the number of times the file will be run through the encryption and/or
      decryption algorithm when the encrypt and/or decrypt method is called. If this value is less than
      1, the file will be run through 2 times.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public BaseCipher
      (
      Password  pPassword,
      int       pCipherIterations
      )
      throws Exception
      {
      if (pPassword == null)
         {
         throw new Exception("pPassword is null");
         }
      iPassword         = pPassword;
      iCipherIterations = (pCipherIterations>0)? pCipherIterations : DFLT_ITER;
      iSalt             = initializedSalt(iPassword.xstring());
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sets the ConsoleStream for this object.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#005">View source</A>

   @return
      A reference to this object

   @param
      pConsole is a ConsoleStream through which this objects sends its output.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public BaseCipher setOut(ConsoleStream pConsole)
      {
      cOut = pConsole;
      return this;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This boilerplate method is used for testing an instantiated object of this class and may include any
   code the developer chooses.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#006">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public BaseCipher test() throws Exception
      {
      cOut.titledPrintf
         (
         "\"HELLO WORLD!\"",
         "%s  %s  %s",
         "I'm an object of the", CLASS_NAME, "class, and I approved this message."
         );
      return this;
      }



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Protected  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an initial salt value.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#007">View source</A>

   @param
      pPassword is the password used to initialize the salt. The same password must be used to
      initialize the decryption and encryption salt.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   protected <Type> XString initializedSalt(Type pPassword) throws Exception
      {
      XString  salt = DFLT_SALT_SEED;

      salt = salt.concat(pPassword);
      salt = (new Digest("SHA-512",salt)).xstring();
      salt = salt.asciiHash();

      return salt;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns a Queue of passwords.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#008">View source</A>

   @return
      The Queue of passwords
   *//*
   ---------------------------------------------------------------------------------------------------- */
   protected Queue<XString> passwordQueue() throws Exception
      {
      /*.
      ==========================================================================================
      Each iteration of the encryption will use a different hash of the password. Generate the
      hashes and store them in a data structure that allows them to be used in a specific order.
      The decryption algorithm generates the same hashes but stores them in a data structure
      that allows them to be used in the reverse order than they are used in this algorithm.
      ------------------------------------------------------------------------------------------ */
      Password        pwdObj        = new Password(iPassword.xstring());
      Queue<XString>  passwordQueue = new Queue<XString>();
      for (int i=0; i<iCipherIterations; i++)
         {
         passwordQueue.add(pwdObj.iterate().xstring());
         }
      return passwordQueue;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns a Queue of salts.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#009">View source</A>

   @return
      The Queue of salts
   *//*
   ---------------------------------------------------------------------------------------------------- */
   protected Queue<XString> saltQueue() throws Exception
      {
      /*.
      ==========================================================================================
      Each iteration of the encryption will use a different salt. Generate the salts and store
      them in a data structure that allows them to be used in a specific order. The decryption
      algorithm generates the same salts but stores them in a data structure that allows them to
      be used in the reverse order than they are used in this algorithm.
      ------------------------------------------------------------------------------------------ */
      Password        sltObj    = new Password(iSalt);
      Queue<XString>  saltQueue = new Queue<XString>();
      for (int i=0; i<iCipherIterations; i++)
         {
         saltQueue.add(sltObj.iterate().xstring());
         }
      return saltQueue;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns a Queue of algorithms.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#010">View source</A>

   @return
      The Queue of algorithms
   *//*
   ---------------------------------------------------------------------------------------------------- */
   protected Queue<XString> algorithmQueue() throws Exception
      {
      iLongestAlgoName = 0;
      /*.
      ==========================================================================================
      Each iteration of the encryption will use a different algorithm. Select the algorithms and
      store them in a data structure that allows them to be used in a specific order. The
      decryption algorithm generates the same algorithms but stores them in a data structure
      that allows them to be used in the reverse order than they are used in this algorithm.
      ------------------------------------------------------------------------------------------ */
      Queue<XString>  algorithmQueue = new Queue<XString>();
      for (int i=0; i<iCipherIterations; i++)
         {
         int      idx       = i % DFLT_ALGRTM.length;
         XString  algorithm = DFLT_ALGRTM[idx];
         algorithmQueue.add(algorithm);
         iLongestAlgoName = UMath.max(iLongestAlgoName,algorithm.length());
         }
      return algorithmQueue;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns a Stack of passwords.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#011">View source</A>

   @return
      The Stack of passwords
   *//*
   ---------------------------------------------------------------------------------------------------- */
   protected Stack<XString> passwordStack() throws Exception
      {
      /*.
      ==========================================================================================
      Each iteration of the decryption will use a different hash of the password. Generate the
      hashes and store them in a data structure that allows them to be used in a specific order.
      The encryption algorithm generates the same hashes but stores them in a data structure
      that allows them to be used in the reverse order than they are used in this algorithm.
      ------------------------------------------------------------------------------------------ */
      Password        pwdObj        = new Password(iPassword.xstring());
      Stack<XString>  passwordStack = new Stack<XString>();
      for (int i=0; i<iCipherIterations; i++)
         {
         passwordStack.push(pwdObj.iterate().xstring());
         }
      return passwordStack;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns a Stack of salts.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#012">View source</A>

   @return
      The Stack of salts
   *//*
   ---------------------------------------------------------------------------------------------------- */
   protected Stack<XString> saltStack() throws Exception
      {
      /*.
      ==========================================================================================
      Each iteration of the decryption will use a different salt. Generate the salts and store
      them in a data structure that allows them to be used in a specific order. The encryption
      algorithm generates the same salts but stores them in a data structure that allows them to
      be used in the reverse order than they are used in this algorithm.
      ------------------------------------------------------------------------------------------ */
      Password        sltObj    = new Password(iSalt);
      Stack<XString>  saltStack = new Stack<XString>();
      for (int i=0; i<iCipherIterations; i++)
         {
         saltStack.push(sltObj.iterate().xstring());
         }
      return saltStack;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns a Stack of algorithms.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#013">View source</A>

   @return
      The Stack of algorithms
   *//*
   ---------------------------------------------------------------------------------------------------- */
   protected Stack<XString> algorithmStack() throws Exception
      {
      iLongestAlgoName = 0;
      /*.
      ==========================================================================================
      Each iteration of the decryption will use a different algorithm. Select the algorithms and
      store them in a data structure that allows them to be used in a specific order. The
      encryption algorithm generates the same algorithms but stores them in a data structure
      that allows them to be used in the reverse order than they are used in this algorithm.
      ------------------------------------------------------------------------------------------ */
      Stack<XString>  algorithmStack = new Stack<XString>();
      for (int i=0; i<iCipherIterations; i++)
         {
         int     idx       = i % DFLT_ALGRTM.length;
         XString algorithm = DFLT_ALGRTM[idx];
         algorithmStack.push(algorithm);
         iLongestAlgoName = UMath.max(iLongestAlgoName,algorithm.length());
         }
      return algorithmStack;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns a Cipher object that is initialized and ready to perform encryption.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#014">View source</A>

   @return
      An initialized Cipher object that is ready to perform encryption.

   @param
      pPassword is the password that will be used to encrypt the file. The same password must be used
      to decrypt the file.
   @param
      pSalt is a random XString that the PBE cipher uses to randomize the key. It's a bit like a second
      password; and in fact, the user can treat it as exactly that -- a second password..
   @param
      pAlgorithm is the name of the cipher algorithm that will be used to encrypt the file. An example
      of a valid value for pAlgorithm is: "PBEWithMD5AndDES"
   *//*
   ---------------------------------------------------------------------------------------------------- */
   protected <Type1,Type2,Type3> Cipher encryptionCipher
      (
      Type1  pPassword,
      Type2  pSalt,
      Type3  pAlgorithm
      )
      throws Exception
      {
      /*.
      ==========================================================================================
      Check the parameters.
      ------------------------------------------------------------------------------------------ */
      if (pPassword == null)
         {
         throw new Exception("pPassword is null");
         }
      if (pSalt == null)
         {
         throw new Exception("pSalt is null");
         }
      if (pAlgorithm == null)
         {
         throw new Exception("pAlgorithm is null");
         }
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      String  pwd = UString.toString(pPassword);
      String  slt = UString.toString(pSalt);
      String  alg = UString.toString(pAlgorithm);
      /*.
      ==========================================================================================
      Create a key from the password
      ------------------------------------------------------------------------------------------ */
      PBEKeySpec        pbeKeySpec = new PBEKeySpec(pwd.toCharArray());
//      SecretKeyFactory  keyFactory = SecretKeyFactory.getInstance(alg,"BC");
      SecretKeyFactory  keyFactory = SecretKeyFactory.getInstance(alg);
      SecretKey         pbeKey     = keyFactory.generateSecret(pbeKeySpec);
      /*.
      ==========================================================================================
      Create a salt value
      ------------------------------------------------------------------------------------------ */
      byte[]  salt = slt.getBytes();
      /*.
      ==========================================================================================
      In order to use Password-Based Encryption (PBE) as defined in PKCS #5, we have to specify
      a salt and an iteration count. The same salt and iteration count that are used for
      encryption must be used for decryption. Since the encryption and decription are done by
      different programs that don't share data, the default salt and iteration cannot be used.
      They must be specified and done so the same way in both programs.
      ------------------------------------------------------------------------------------------ */
      PBEParameterSpec  pbeParamSpec;
      pbeParamSpec = new PBEParameterSpec(salt, DFLT_KEY_ITER);
      /*.
      ==========================================================================================
      Create a cipher object
      ------------------------------------------------------------------------------------------ */
//      Cipher  theCipher = Cipher.getInstance(alg,"BC");
      Cipher  theCipher = Cipher.getInstance(alg);
      theCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
      return theCipher;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns a Cipher object that is initialized and ready to perform decryption.

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#015">View source</A>

   @return
      An initialized Cipher object that is ready to perform decryption.

   @param
      pPassword is the password that will be used to decrypt the file. The same password must be used
      to decrypt the file.
   @param
      pSalt is a random XString that the PBE cipher uses to randomize the key. It's a bit like a second
      password; and in fact, the user can treat it as exactly that -- a second password..
   @param
      pAlgorithm is the name of the cipher algorithm that will be used to decrypt the file. An example
      of a valid value for pAlgorithm is: "PBEWithMD5AndDES"
   *//*
   ---------------------------------------------------------------------------------------------------- */
   protected <Type1,Type2,Type3> Cipher decryptionCipher
      (
      Type1  pPassword,
      Type2  pSalt,
      Type3  pAlgorithm
      )
      throws Exception
      {
      /*.
      ==========================================================================================
      Check the parameters.
      ------------------------------------------------------------------------------------------ */
      if (pPassword == null)
         {
         throw new Exception("pPassword is null");
         }
      if (pSalt == null)
         {
         throw new Exception("pSalt is null");
         }
      if (pAlgorithm == null)
         {
         throw new Exception("pAlgorithm is null");
         }
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      String  pwd = UString.toString(pPassword);
      String  slt = UString.toString(pSalt);
      String  alg = UString.toString(pAlgorithm);
      /*.
      ==========================================================================================
      Create a key from the password
      ------------------------------------------------------------------------------------------ */
      PBEKeySpec        pbeKeySpec = new PBEKeySpec(pwd.toCharArray());
//      SecretKeyFactory  keyFactory = SecretKeyFactory.getInstance(alg,"BC");
      SecretKeyFactory  keyFactory = SecretKeyFactory.getInstance(alg);
      SecretKey         pbeKey     = keyFactory.generateSecret(pbeKeySpec);
      /*.
      ==========================================================================================
      Create a salt value
      ------------------------------------------------------------------------------------------ */
      byte[]  salt = slt.getBytes();
      /*.
      ==========================================================================================
      In order to use Password-Based Encryption (PBE) as defined in PKCS #5, we have to specify
      a salt and an iteration count. The same salt and iteration count that are used for
      encryption must be used for decryption. Since the encryption and decription are done by
      different programs that don't share data, the default salt and iteration cannot be used.
      They must be specified and done so the same way in both programs.
      ------------------------------------------------------------------------------------------ */
      PBEParameterSpec  pbeParamSpec;
      pbeParamSpec = new PBEParameterSpec(salt, DFLT_KEY_ITER);
      /*.
      ==========================================================================================
      Create a cipher object
      ------------------------------------------------------------------------------------------ */
//      Cipher  theCipher = Cipher.getInstance(alg,"BC");
      Cipher  theCipher = Cipher.getInstance(alg);
try {
      theCipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);
// ??? , theCipher.GetIV() ???


// sample //  if(theCipher.getIV()!=null) {
// sample //  theCipher.init(Cipher.DECRYPT_MODE, sk, new IvParameterSpec(theCipher.getIV()));
// sample //  }else{
// sample //  theCipher.init(Cipher.DECRYPT_MODE, sk);
// sample //  }



} catch (Exception e) {
   cOut.println(e.getMessage());
}
      return theCipher;
      }



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Private  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*.
   ==========================================================================================
   Class Constants
      CLASS_NAME:
         The name of this class
      DFLT_LINE_LEN:
         The default line length for word wrapping
      DFLT_ALGRTM:
         Default component algorithms
      DFLT_SALT_SEED:
         Default key salt
      DFLT_ITER:
         Default cipher iterations
      DFLT_KEY_ITER:
         Default key iterations
   ------------------------------------------------------------------------------------------ */
   private static final XString    CLASS_NAME    = new XString(BaseCipher.class.getName());
   private static final int        DFLT_LINE_LEN = ConsoleMessage.defaultLineLength();
   private static final XString[]  DFLT_ALGRTM   =
      {
//      new XString("PBEwithHmacSHA512andAES_256"        ),
//      new XString("PBEwithHmacSHA512andAES_128"        ),
//      new XString("PBEwithHmacSHA384andAES_256"        ),
//      new XString("PBEwithHmacSHA384andAES_128"        ),
//      new XString("PBEwithHmacSHA256andAES_256"        ),
//      new XString("PBEwithHmacSHA256andAES_128"        ),
//      new XString("PBEwithHmacSHA224andAES_256"        ),
//      new XString("PBEwithHmacSHA224andAES_128"        ),
//      new XString("PBEwithHmacSHA1andAES_256"          ),
      new XString("PBEwithHmacSHA1andAES_128"          ),
//      new XString("PBEwithMD5andTripleDES"             ),
      };
   /*.
   ==========================================================================================
   <BLOCKQUOTE>
      <PRE id="unindent">
         256 PBEwithHmacSHA512andAES_256
         256 PBEwithHmacSHA384andAES_256
         256 PBEwithHmacSHA256andAES_256
         256 PBEwithHmacSHA224andAES_256
         256 PBEwithHmacSHA1andAES_256
         128 PBEwithSHA1andRC4_128
         128 PBEwithSHA1andRC2_128
         128 PBEwithHmacSHA512andAES_128
         128 PBEwithHmacSHA384andAES_128
         128 PBEwithHmacSHA256andAES_128
         128 PBEwithHmacSHA224andAES_128
         128 PBEwithHmacSHA1andAES_128
          64 PBEwithMD5andDES
          40 PBEwithSHA1andRC4_40
          40 PBEwithSHA1andRC2_40
           0 PBEwithSHA1andDESede
           0 PBEwithMD5andTripleDES
      </PRE>
   </BLOCKQUOTE>
   ------------------------------------------------------------------------------------------ */
   /*.
   ==========================================================================================
   <BLOCKQUOTE>
      <PRE id="unindent">
         private static final XString  DFLT_SALT_SEED = new XString("PogonomyrmexRugosus.PheidoleObtusospinosa");
         private static final XString  DFLT_SALT_SEED = new XString("wHb3fgoStNA7BfhIvmKTU450hUK3bbQ3jrna2wohLfSBhTrIGC17iLtWzxiHF1MaoipBkNomXmjBXOHeTtOPVC2zhcwAJBNiOKhBnGWrlnluzKuifyvM0BHXSHhIsxb4");
      </PRE>
   </BLOCKQUOTE>
   ------------------------------------------------------------------------------------------ */
   private static final XString  DFLT_SALT_SEED = new XString("wHb3fgoStNA7BfhIvmKTU450hUK3bbQ3jrna2wohLfSBhTrIGC17iLtWzxiHF1MaoipBkNomXmjBXOHeTtOPVC2zhcwAJBNiOKhBnGWrlnluzKuifyvM0BHXSHhIsxb4");
   private static final int      DFLT_ITER      = 9;
   private static final int      DFLT_KEY_ITER  = 173;
   /*.
   ==========================================================================================
   Class variables
      cOut:
         Console output.
   ------------------------------------------------------------------------------------------ */
   private static ConsoleStream  cOut = ConsoleStream.getSingleton();
   /*.
   ==========================================================================================
   Instance variables
      iLongestAlgoName:
         The number of characters in the longest algorithm name
      iPassword:
         The computed password (I know it's not secure to store it this way)
      iSalt:
         The key salt
      iCipherIterations:
         The number of cipher iterations
   ------------------------------------------------------------------------------------------ */
   protected int       iLongestAlgoName = 0;
   private   Password  iPassword;
   private   XString   iSalt;
   protected int       iCipherIterations;



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Inner Classes  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Public Static Methods  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method allows this class file to be unit tested as a standalone application. It's the method
   that's called when the class is invoked from the command line by using the java application launcher
   - "java". Main() is not a required method, but the practice of putting one in each class and
   wrapping class test code within it allows easy unit testing of the class; and main does not need to
   be removed when testing is complete.

   <P>
      <DL>
         <DT>
            <B>
               Command line usage:
            </B>
            <DD>
               Java cosmicabyss.com.lib.BaseCipher
            </DD>
         </DT>
      </DL>

   <P><B>Implementation: </B><A HREF="BaseCipher.java.html#016">View source</A>

   @param
      pArgs contains the command line arguments with which this class was invoked as an application.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static void main(String[] pArgs) throws Exception
      {
      /*.
      ==========================================================================================
      Greetings !
      ------------------------------------------------------------------------------------------ */
      cOut.banner(CLASS_NAME);
      /*.
      ==========================================================================================
      Get a list of all the password-based encryption algorithms supported by the BC provider
      ------------------------------------------------------------------------------------------ */
//      ArrayList<String> cipherAlgorithms = Util.getJceProviderFilteredImplementations("BC", "Cipher", "PBE");
      ArrayList<String> cipherAlgorithms = Util.getJceProviderFilteredImplementations("*", "Cipher", "PBE");
      for (int i=0; i<cipherAlgorithms.size(); i++)
         {
         int  keySize = Util.getJceCipherKeySize(cipherAlgorithms.get(i));
         }
      /*.
      ==========================================================================================
      Create an object and send its output to the ConsoleStream
      ------------------------------------------------------------------------------------------ */
      BaseCipher  obj = new BaseCipher();
      /*.
      ==========================================================================================
      Test code
      ------------------------------------------------------------------------------------------ */
      obj.test();
      /*.
      ==========================================================================================
      Build the data structures that will be needed to test the algorithm names
      ------------------------------------------------------------------------------------------ */
      PBEKeySpec        pbeKeySpec    = new PBEKeySpec("pwdStr".toCharArray());
      byte[]            salt          = "saltStr".getBytes();
      PBEParameterSpec  pbeParamSpec  = new PBEParameterSpec(salt, DFLT_KEY_ITER);
      /*.
      ==========================================================================================
      Test each algorithm name to see if it is valid; if it is, add it to our results array
      ------------------------------------------------------------------------------------------ */
      ArrayList<String>  results = new ArrayList<String>();
      for (int i=0; i<cipherAlgorithms.size(); i++)
         {
         String  algorithmName = cipherAlgorithms.get(i);
         /*.
         ==========================================================================================
         If the algorithm name is valid (supported by the installed provider), then no exception
         will be thrown.
         ------------------------------------------------------------------------------------------ */
         try
            {
            /*.
            ==========================================================================================
            Create a key from the password
            ------------------------------------------------------------------------------------------ */
//            SecretKeyFactory  keyFactory = SecretKeyFactory.getInstance(algorithmName,"BC");
            SecretKeyFactory  keyFactory = SecretKeyFactory.getInstance(algorithmName);
            SecretKey         pbeKey     = keyFactory.generateSecret(pbeKeySpec);
            /*.
            ==========================================================================================
            Create a cipher object
            ------------------------------------------------------------------------------------------ */
//            Cipher  theCipher = Cipher.getInstance(algorithmName,"BC");
            Cipher  theCipher = Cipher.getInstance(algorithmName);
            /*.
            ==========================================================================================
            If no exception has been thrown by now, then BouncyCastle appears to think the algorithm
            name we've constructed is valid. Store the name in an array which will be sorted and
            output.
            ------------------------------------------------------------------------------------------ */
            int     keySize       = Util.getJceCipherKeySize(algorithmName);
            String  keySizeString = (new XString(keySize)).alignRight(4, ' ').toString();
            results.add(keySizeString + " " + algorithmName);
            }
         /*.
         ==========================================================================================
         If an exception was thrown, then the algorithm name isn't valid. Just ignore it then and
         go on to the next one.
         ------------------------------------------------------------------------------------------ */
         catch (Exception e)
            {
            }
         }
      /*.
      ==========================================================================================
      Sort the array of valid names into descending order according to key size in bits
      ------------------------------------------------------------------------------------------ */
      results = Util.sortedArrayList(results);
      results = Util.reversedArrayList(results);
      /*.
      ==========================================================================================
      Print the sorted array to cOut
      ------------------------------------------------------------------------------------------ */
      int  count = 0;
      cOut.println("     KEY");
      cOut.println("IDX SIZE ALGORITHM NAME");
      cOut.println("--- ---- ------------------------------------");
      for (String algorithmName : results)
         {
         count++;
         String  countString = (new XString(count)).alignRight(3, ' ').toString();
         cOut.println(countString + " " + algorithmName);
         }
      }



   }  // class BaseCipher



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Notes  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */