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.
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.
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.
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.
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:
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. 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.
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 .
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.
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.
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.
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.
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.
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 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(); } } } 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:
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 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; } }); } } Java Security Solutions ISBN: 0764549286
EAN: 2147483647 Year: 2001
Pages: 222 Authors: Rich Helton, Johennie Helton
flylib.com © 2008-2017. If you may any questions please contact us: flylib@qtcs.net |