Implementing a CipherSpi

  

The previous chapter showed an RSA algorithm extended using a javax.crypto.CipherSpi class. The CipherSpi class is used to add ciphers inside the JCE. To be extended, the cipher must be implemented in the methods of the CipherSpi class in order for these methods to be accessed by the client. Some of the methods may not be implemented; they simply return a null or throw an exception, such as engineGetIV and engineGetParameters, which are algorithm-specific.

Some ciphers may not require the Initialization Vector (IV) or parameters like p , q , and g to have variables initialize key parameters. The RSA cipher requires two keys: a public key and a private key. The key that is initialized in the cipher will determine whether the RSA is encrypting or decrypting. The RSA algorithm is a special case because many ciphers will use the symmetric key, which is a single key for both encrypting and decrypting. Because the cipher engine may not know if it is in an encrypting or decrypting mode, the mode must be passed in during the initialization of the cipher engine.

Another element that is always required during the initialization of the cipher engine is the key or certificate. An X.509 certificate contains public keys and can be used by some algorithms instead of the java.security.Key class. Many cipher keys, like the RSA public key and RSA private key, are extended from the Key class. The key is normally extended into a java.security.KeyPair for the asymmetric key or javax.crypto.SecretKey class for the symmetric key. All ciphers require some kind of key for operation.

Cross-Reference  

See Chapter 4 for information on key generation.

The key generation must always be accomplished before initializing the cipher because a key must be passed in during initialization. The same service provider may write both the key engine and cipher engine to ensure integration. If you follow specifications and algorithms exactly, you can mix and match keys and cipherengines. The cipher engine that I produced in the previous chapter uses the RSA public key and RSA private key that come standard with the JCE, which is now included in the JDK 1.4.

Hint  

To execute an addition to the JCE, you must store the CipherSpi and Provider classes as a JAR file in the $JRE/lib/ext directory for access to Java extension. The JAR file must be signed by a trusted certificate stored in the local KeyStore . The JAR file must also be in the CLASSPATH for execution. These measures help limit tampering to JCE files and changing ciphers. A trusted certificate can be inserted into the KeyStore . See Chapter 8 for information on the KeyStore .

Listing 13-1, later in the chapter, shows the test example. To get the instance of the key pair generator, the KeyPairGenerator.getInstance("RSA") method is called to retrieve the first key algorithm available from a service provider. The service provider that is loaded is of no consequence as long as it loads a valid RSA key pair. There is no RSA cipher that is shipped with the JCE part of the JDK 1.4, so one had to be extended using the cipher SPI layer.

Cross-Reference  

Chapter 4 provides information on key service providers.

After the keys are created using the javax.crypto . KeyGenerator , java.security.KeyPairGenerator , java.security.KeyFactory , or javax.crypto.SecretKeyFactory packages, the cipher engine can be initialized.

Tip  

Notice that the asymmetric keys use the java.security package, and the symmetric key uses the javax.crypto package.

The key to initializing the cipher engine is the implementation of the associated java.security.Provider class. The associated provider implementation is given in Listing 13-2. The provider is needed to load the cipher engine. When the Cipher.getInstance("RSA", "RichWare") method is executed, an instance of the cipher engine needs to be created. If everything is created correctly, calling the method cipher.init(Cipher.ENCRYPT_MODE, publKey, _random), from Listing 13-1, will call the RichRSACipher SPI's engineInit method. To get to this point, the JCE will have to successfully do the following:

  • Find the RichWare provider class RichProvider

  • Find the successful association of RSA

  • Successfully load the associated class com.richware.RichRSACipher (dicussed in Listing 12-1) to use as the SPI interface

When the getInstance method is called from the Java application, the javax.crypto.Cipher class will call JCE methods to search down the SPI chain, find the cipher property for the RSA mapping, and load the associated class specified in the Provider class as com.richware.RichRSACipher .

The JCE internal code is more restrictive than other SPI interfaces. The JCE will check to see if the proper security settings are set for cipher engines. The JCE does this extra work to avoid interceptions and corruptions of cipher algorithms. A good point of attack for any hacker is to monitor the cipher engine for plaintext, or try to modify it to send plaintext to a pickup location. The JCE must ensure that the provider and cipher engine classes are signed in a JAR file by a trusted certificate. Basically, this means that the cipher jar can be trusted.

For simplicity, the JAR file can be copied into the $JRE/lib/ext subdirectory. The $JRE/lib/ext subdirectory is a subdirectory that is normally used for extending the JDK 1.4. The extending subdirectory is given special permissions from the default java.policy file that is normally called by the Java virtual machine. The following code demonstrates how to grant permissions to the $JRE/lib/ext subdirectory:

 grant codeBase "file:${java.home}/lib/ext/*" {  permission java.security.AllPermission; }; 

Figure 13-1 demonstrates the mapping of the CipherSpi and Provider classes. In the example in Listing 13-1, the RichWare provider is added to the SPI chain in the second entry by using the Security.insertProviderAt(new com.richware.RichProvider(), 2) method. The cipher's getInstance method will pass in the entry to be searched in the RichProvider class. The entry in the provider class is Cipher.RSA to define that it is an RSA implementation of the CipherSpi . The getInstance method looks up the RichWare provider that is initialized as RichWare in the RichProvider class because it is passed as one of the parameters.

click to expand
Figure 13-1: The Provider and CipherSpi mapping

The getInstance method may be more defined than just specifying the RSA algorithm lookup for the cipher. There may be multiple RSA algorithms in the CipherSpi , or multiple CipherSpi s that define different types of the RSA algorithms. The question becomes how to find the correct algorithm other than by using the algorithm name and provider name . The answer requires a further examination of the CipherSpi interface.

The previous chapter showed that when selecting a cipher algorithm, the operation mode (like ECB) , and the padding (such as PKCS1_V1_5) , parameters are used to define the algorithm. These parameters can be used when creating the cipher instance. The mapping in the provider can include a specific association of Cipher.RSA/ECB/PKCS1_V1_5 to map to a different CipherSpi implementation class. The getInstance method can include the operation mode, or both the operation mode and padding to map to a specific implementation of the algorithm.

Tip  

Because JCE extensions are little more discrete, not passing the getInstance method the provider name may cause problems when extending the ciphers in the JCE.

If the provider does not include the padding and operation mode, but the getInstance method has specified a specific implementation, the loader will set the cipher engine to use the padding and operation mode specified. If the getInstance method specifies the padding and operation mode of the algorithm, the loader will create the cipher engine and then execute the engineSetPadding and engineSetMode methods. The loader will pass the desired padding in the engineSetPadding method. If the cipher engine does not support the padding type, it will throw a NoSuchPaddingException . The loader will pass the desired operation mode in the engineSetMode method. If the cipher engine does not support the operation mode, it will throw a NoSuchAlgorithmException .

Cross-Reference  

See Chapter 12 for the types of operation modes and paddings.

Some algorithms may also require the IV vector implementation, the java.security.AlgorithmParameters implementation, the java.security.spec.AlgorithmParameterSpec implementation, and the java.security.SecureRandom classes. Using these classes is highly dependent on the algorithm of the cipher. For example, Blowfish uses an IV.

Tip  

The SPI layer requires a discrete operation to be implemented from the application. All the methods must be implemented from the CipherSpi class even though some of them return nulls. The developer has limited control on the operation of the SPI. For most implementations , it will simply be a matter of providing the java.security.Provider interface to add the algorithm into the SPI chain and to put the cipher algorithm in the CipherSpi class that the JCE will load.

A service provider also provides the AlgorithmParameters class because the algorithm parameters are closely tied to the ciphers and the keys. Using the IVParameterSpec , I could generate the IV to initialize the Blowfish cipher. IV is similar to a second key that will change the output of the cipher. Another example of using the algorithm parameter could be a matrix of points along an ECC curve to avoid computations . For RSA, an algorithm parameter may be the p , q , and g to use the Chinese Remainder Theorem for faster RSA calculations.

Cross-Reference  

Chapter 5 describes ECC, and Chapter 4 describes the RSA algorithm.

The purpose of the algorithm parameters is to initialize a cipher engine. The AlgorithmParameterSpec class will define how the algorithm parameters are constructed . The only other class that may apply during initialization is the java.security.SecureRandom class. If the users of the algorithm wish to initialize a random class with special seeds to ensure more of a pure randomization, they may pass that class to the cipher algorithm for when the cipher engine generates any randomized numbers .

By using any combination of these classes for initialization of cipher engines, the cipher engine could start with predefined variables that have been groomed by an organization without changing the cipher engine. Many cipher engines may not support these algorithms; the user must check with the service provider to see what parameters it is supporting for the cipher engine.

Cross-Reference  

The Blowfish cipher is discussed in detail in Chapter 14.

All the work that has been done thus far is to get the initialization of the cipher engine started. The opmode of the initialization, not the creation of the Cipher class, will determine whether the cipher engine is encrypting or decrypting. The opmode parameter for the init method tells the cipher engine whether it is encrypting or decrypting.

Tip  

The opmode in this case is not the operation mode of the algorithm, but the operation mode of the cipher engine.

After the cipher has been fully initialized and knows if it is encrypting or decrypting, a byte array can be passed in the cipher engine to be encrypted or decrypted. The opmodes are defined as Cipher.DECRYPT_MODE and Cipher.UNWRAP_MODE for decryption and Cipher.ENCRYPT_MODE and Cipher.WRAP_MODE for encryption. To perform the operation, either encryption or decryption, with the cipher engine, the update and doFinal methods are used. The update method of the Cipher class will call the engineUpdate method of the CipherSpi implementation. The doFinal method of the Cipher class will call the engineDoFinal method of the CipherSpi implementation.

Tip  

In Listing 13-1, the sun.misc.BASE64Encoder class is used for representing an array of bytes as ASCII characters .

The difference between the two methods is that the doFinal method is used when the last piece of the message needs to be operated. The update method can be used to partially encrypt or decrypt pieces of the message sequentially. Listing 13-1, as I mentioned earlier in this chapter, provides the TestRSACiphers class, which is a test example for com.richware.RichRSACipher that provides the SPI interface. The com.richware.RichRSACipher class was described in Chapter 12. In addition, the TestRSACiphers class uses the com.richware.RichProvider , which is included in Listing 13-2.

Listing 13-1: The TestRSACiphers class: RSA test for the cipher
start example
 package com.richware.chap13;     import java.io.*; import java.math.BigInteger; import java.security.*; import java.security.interfaces.*; import java.security.spec.*; import javax.crypto.*; import sun.misc.*;     /**  * Class TestRSACiphers  * Description: This is an example to  * test the RSA cipher.  *  * Copyright:    Copyright (c) 2002  * Company:      HungryMinds  * @author Rich Helton <rhelton@richware.com>  * @version 1.0   * DISCLAIMER: Please refer to the disclaimer at the beginning of this book.  */ public final class TestRSACipher  {   /**    * Constructor TestRSACipher    */   public TestRSACipher() {}       /**    * Method main    * Description: This is a Sample JAAS application    * @param args none    *    */   public static void main(String[] args)    {     try      {       String       message = "This is a test, hackers beware.";       SecureRandom _random = new SecureRandom();           /*        * Generate the RSA Keys        */       KeyPairGenerator kpg =         KeyPairGenerator.getInstance("RSA");       kpg.initialize(1024);       System.out.println("Generating a key pair...");       KeyPair keyPair = kpg.generateKeyPair();       System.out.println("Done generating keys.\n");           /*        * Get the public and private keys.        */       PublicKey  publKey = keyPair.getPublic();       PrivateKey privKey = keyPair.getPrivate();           /*        * Create a base-64 encoder for displaying binary data.        */       BASE64Encoder encoder = new BASE64Encoder();           /*        * Register the provider.        */       Security         .insertProviderAt(new RichProvider(), 2);           /*        * Create a byte array from the message.        */       byte[] messageBytes = message.getBytes("UTF8");           /*        * Create the cipher algorithms        * cipher is formatted algorithm/mode/padding or algorithm        */       Cipher cipher  = Cipher.getInstance("RSA", "RichWare");       /*         * Create an algorithm for decryption        */              Cipher cipher2 = Cipher.getInstance("RSA/ECB/PKCS1_V1_5",                                           "RichWare");           /*        * Encrypt the message with the public key.        */       cipher.init(Cipher.ENCRYPT_MODE, publKey, _random);           byte[] encryptedMessage =         cipher.doFinal(messageBytes, 0, messageBytes.length);       System.out.println("Encrypted message:\n"                          + encoder.encode(encryptedMessage));           /*        * Decrypt the message with the private key.        */       cipher2.init(Cipher.DECRYPT_MODE, privKey, _random);       byte[] decryptedMessage       =         cipher2.doFinal(encryptedMessage, 0,                         encryptedMessage.length);       String decryptedMessageString =         new String(decryptedMessage, "UTF8");       System.out.println("\nDecrypted message: "                          + decryptedMessageString);           /*        * Check that the decrypted message and the original        * message are the same.        */       if (decryptedMessageString.equals(message))        {         System.out.println("\nTest succeeded.");       }       else        {         System.out.println("\nTest failed.");       }     }         /*      * Catches      */     catch (Exception ex)      {       ex.printStackTrace();     }   } } 
end example
 

Listing 13-1 requires a provider, which is Listing 13-2, RichProvider . In order for the client (in this case TestRSACipher ) to find its provider ( RichProvider ) the following steps need to happen:

  1. Get a Code-Signing Certificate:

    • Use keytool to generate a DSA keypair

    • Use keytool to generate a certificate signing request

    • Send the CSR, contact information, and other required documentation to the JCE Code Signing Certification Authority

    • Use keytool to import the certificates received from the CA

  2. JAR the RichProvider code

  3. Sign with a trusted certification RichProvider

    Cross-Reference  

    Chapter 8 explains the the keytool utility and the jarsigner utility.

  4. Install RichProvider

    You have two choices:

    • You can install the signed jar file as a bundle extension

    • You can add the signed jar file in your CLASSPATH

  5. Register RichProvider

    You have two choices to register a provider:

    • Statically: add the provider to your list of approved providers by editing the security properties file

    • Dynamically: this registration is not persistent and can only be done by code

  6. Set RichProvider Permissions

  7. Run TestRSACiphers Program

    Tip  

    Steps 1 through 3 create a trusted jar file before you to install the provider. You can find a signed RichProvider at www.richware.com .

The list of service providers is found in the java.security file that is stored in the $JRE/lib/security directory as described in Chapter 7.

The above information is found in the JDK 1.4 documentation and is critical in order for you to be able to run the sample code. Listing 13-2 is the provider implementation for Listing 13-1.

Listing 13-2: The RichProvider class: The Provider implementation
start example
 package com.richware.chap13;     import java.security.*;     /**  * Class RichProvider  * Description: This is an example to  * load a provider for ciphers.  *  * Copyright:    Copyright (c) 2002 Wiley Publishing, Inc.  * @author Rich Helton <rhelton@richware.com>  * @version 1.0   * DISCLAIMER: Please refer to the disclaimer at the start of this book.  */ public final class RichProvider extends java.security.Provider  {   private static final String NAME    = "RichWare";   private static final double VERSION = 1.0;   private static final String INFO    = "RichWare Ciphers";       /**    * Constructor RichProvider    */   public RichProvider()    {     super(NAME, VERSION, INFO);     /*      * Need to execute as a PrivilegedAction      * for security reasons.      */     AccessController.doPrivileged(new PrivilegedAction()      {       public Object run()        {         put("Cipher.RSA", "com.richware.chap12.RichRSACipher");         put("Cipher.RC4", "com.richware.chap13.RichRC4Cipher");         put("KeyGenerator.RC4", "com.richware.chap13.RichRC4KeyGenerator");         return null;       }     });   } } 
end example
 
  


Java Security Solutions
Java Security Solutions
ISBN: 0764549286
EAN: 2147483647
Year: 2001
Pages: 222

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net