The Java Security API makes heavy use of both secret key and public key cryptography. The basics of creating and managing keys are provided by the Key interface and the KeyPair, KeyGenerator, and KeyPairGenerator classes in the java.security package.
Before we delve into how keys are supported in the Security API, it's important to understand the basics of public key cryptography algorithms, and the differences between them and the more well known secret key algorithms. Most of the features in the Java Security API, and most modern security APIs in general, support both secret key and public key algorithms. Readers that are already familiar with the concept of public key cryptography can skip over the next few sections.
Early cryptographic systems used secret key methods for encoding private data. Secret key cryptography depends on the use of private keys for authentication and encryption. Two parties have to exchange secret keys with each other over some private, secure communications channel, and these keys are used to encode and decode messages. Each party has to have the other party's secret key in order to decode their messages. If attackers manage to steal or intercept a secret key, then they can listen in on communications or even impersonate one of the parties by sending encrypted messages using the stolen secret key.
Secret key cryptography has the advantage of simplicity. Algorithms for implementing secret key schemes are generally simpler and faster than the more complicated public key schemes. But there are some obvious problems with secret key cryptography. The basis for the security of the system is the secret key itself, but the secret key must be given to any agent that needs to communicate securely with you. This opens up the possibility of keys being stolen in transit, and finding a separate, secure way to transmit secret keys may be inconvenient, expensive, or just impossible.
In 1976, Diffie and Hellman published a paper describing a means for two parties to share cryptographic keys over a public communications channel without compromising the security of their private transmissions.[1] Essentially, the technique involves the use of two keys by each party, a private key and a public key. A message encrypted with one party's public key can only be decrypted with that party's private key. At the start of a conversation, two parties independently choose random private keys, which they keep to themselves. Then they generate a public key that is based on their private key. This public key can be freely shared with anyone, and can be used to encrypt messages to the party that owns the corresponding private key. Having the public key doesn't do an information thief any good, since it can't be used on its own to decrypt messages--you have to have the corresponding private key to do that. Public key encryption techniques are sometimes called asymmetric techniques, since a different process and key are used for encrypting messages than for decrypting messages.
[1]Diffie & Hellman, "New Directions in Cryptography," IEEE Transactions on Information Theory IT-22, November 1976.
The original form of public key cryptography described by Diffie and Hellman was vulnerable to the host-spoofing attack we described earlier, where a third party impersonates one or both of the communicating parties in order to steal the information being transmitted. Since public keys are transmitted as clear data, if a third party were to insert itself between the two agents on the network, it could intercept the public keys from each agent, and replace them with its own public key or keys. The two parties would then encrypt messages using the hostile party's keys. The hostile party could intercept these messages, decrypt them with its private keys, and re-encrypt them using the original intercepted public keys. This information theft could go on indefinitely, since the two parties wouldn't have any sign that a third party had injected itself into their conversation (except maybe some additional latency). To close this security hole, the concept of public key certificates was developed, to allow each party to verify the correspondence of a public key with a particular person or organization. A certificate contains some person's name and public key, and has been encrypted by the private key of a certification authority. The certification authority is an organization responsible for verifying the person's identity; it has made its public key widely available, so it can be used to decrypt the certificates it issues. When starting a network session, each party sends its certificate. When the certificate is received, the certifying authority's public key can be used to check the certificate for authenticity. If it checks out, then we can be assured that the public key for the other party is authentic. So a third party can't impersonate another by issuing a false public key, since they don't have a certificate matching the fake public key to the person they're trying to impersonate.
Public key cryptography, when it's extended to include certificates for authenticating the owner of public keys, is a powerful way to authenticate agents and carry out secure communications with them. And we can carry out secure communications without the need for secondary, private channels for secret key transmissions. The main disadvantage to public key methods is the additional overhead involved in encoding and decoding information. Since it relies on a more complex mathematical algorithm, secure public key I/O involves using more CPU time per kilobyte of data transferred and received.
The Key interface represents a cryptographic key. The key could be a public or private key for public key algorithms such as RSA, or it could be a secret key for algorithms based on them, such as some block-cipher algorithms. A Key has an algorithm associated with it, and the name of the algorithm can be gotten using the getAlgorithm() method. Key generation is algorithm-specific, and knowing the algorithm used to generate a key can be important later. There are PublicKey and PrivateKey subclasses of Key that represent the public and private elements of a key pair, and there is also a SecretKey subclass for secret keys. These subclasses don't add any functionality to the Key interface. They just provide a way to distinguish one type of key from another.
The KeyPair class is a holder for a PublicKey and its corresponding PrivateKey. You can get the private and public elements of the KeyPair using the getPrivate() and getPublic() methods.
Key pairs are generated through the KeyPairGenerator class. A KeyPairGenerator instance is created using the static getInstance() method, using an algorithm name to specify what type of keys are needed:
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
Every key-pair generator has a strength factor and a random number generator parameter that can be changed using the initialize() methods on KeyPairGenerator before generating any keys:
keyPairGen.initialize(1024, new SecureRandom());
The SecureRandom class is a pseudo-random number generator provided in the java.security package. The strength and random-number generator parameters are interpreted and used in algorithm-specific ways by the actual implementations of the KeyPairGenerator class. You can also initialize a key-pair generator with algorithm-specific parameters by casting the generated KeyPairGenerator object to its subtype, and calling specialized initialize() methods for the subclass. For example, we created a generator for the RSA algorithm, so we can cast our KeyPairGenerator to an RSAKeyPairGenerator and call its initialize method, which takes a strength parameter, a public exponent for the key generation, and a random-number generator:
RSAKeyPairGenerator rsaKeyPairGen = (RSAKeyPairGenerator)keyPairGen; BigInteger myExp = ... // Create custom exponent rsaPairKeyGen.initialize(1024, myExp, new SecureRandom());
You should only need to do this algorithm-specific initialization if you have specific values for the algorithm parameters that are required by your application. Implementations of the KeyPairGenerator class will generally provide default values for all of their algorithm parameters.
Once the KeyPairGenerator is created and, if necessary, initialized, then you create a new KeyPair using the generateKeyPair() method:
KeyPair myKeyPair = keyPairGen.generateKeyPair();
Secret key algorithms require the use of SecretKey objects instead of public/private key-pairs. SecretKeys are generated using the KeyGenerator class. A KeyGenerator is created for a particular algorithm, using the static getInstance() method on KeyGenerator:
KeyGenerator keyGen = KeyGenerator.getInstance("DES");
The KeyGenerator can be initialized, if necessary, with a random-number generator if the default one is not sufficient:
keyGen.initialize(new SecureRandom(mySeed));
Finally, a new SecretKey can be generated using the generateKey() method:
SecretKey myKey = keyGen.generateKey();
The public, private, and secret keys generated this way can be used to create digital signatures, create message digests, and encrypt data, as described in the coming sections.
Copyright © 2001 O'Reilly & Associates. All rights reserved.