Exercise 7-1. Write a PasswordManager class that associates
usernames with passwords and has methods for creating and deleting
username/password pairs, changing the password associated with a
username, and authenticating a user by verifying a supplied password.
PasswordManager should store the usernames and
passwords in a file (or in a database if you've
already read Chapter 18).
Note, however, that the class should not store the passwords as plain
text, as that would allow an intruder who broke into the
PasswordManager system to obtain full access to
all passwords. To prevent this, it is common to use a one-way
function to encrypt passwords. Message digests, such as those used in
Example 7-4, provide exactly this kind of a one-way
function. Computing a message digest for a password is relatively
easy, but going in the opposite direction (from digest to password)
is very difficult or impossible.
Design the PasswordManager class so that instead
of storing the actual password, it stores only a message digest of
the password. To verify a user's password, your
class should compute a digest for the supplied password and compare
it to the stored digest. If the digests match, you can assume that
the passwords also match. (There is actually an infinitesimally small
chance that two different passwords will produce the same message
digest, but you can disregard this possibility.)
Exercise 7-2. Write a network service and client that allow a user to change her
current password that is registered with your
PasswordManager class. If you've
read Chapter 21, modify
PasswordManager so that it runs as a RMI remote
object, and write a client program that uses the remote object to
change a password. If you have not read that chapter yet, write the
password-changing service to run under the Server
class developed in Chapter 5, and use the
GenericClient class from that same chapter to
interact with the service. In either case, create a security policy
file that defines the set of permissions required by your network
service, and use this policy file to enable your service to run with
the -Djava.security.manager option to the Java
interpreter.
Exercise 7-3. The TripleDES class of Example 7-5 uses the
"DESede" algorithm in the default
ECB (electronic code book) mode. This encryption mode is more
vulnerable to certain decryption attacks than CBC (cipher block
chaining) mode. Modify the example so that it uses CBC mode. You
specify the mode as part of the algorithm name: in this case, instead
of specifying DESede as the algorithm, specify
"DESede/CBC/PKCS5Padding".
To encrypt using CBC mode, the Cipher object
creates an initialization vector (IV) of random bytes, which is also
required when decrypting. Modify the encrypt( )
method so that it obtains the IV with the getIV( )
method of the Cipher object and writes the bytes
(and the length) of the IV array to the output stream before it
writes out the encrypted bytes. To do this, you may want to modify
encrypt( ) so that it doesn't use
the CipherOutputStream but instead works with the
Cipher class directly, the way decrypt(
) does. Modify the decrypt( ) method so
that it reads the bytes of the IV and uses them to create a
javax.crypto.spec.IvParameterSpec object, which it
then passes (as an AlgorithmParameterSpec) to one
of the init( ) methods of the
Cipher object.
Exercise 7-4. The TripleDES program stores and reads secret keys
from unprotected files, which is not a very secure way to work with
important keys. Modify the program so that it uses a
KeyStore object to store (and retrieve) the key in
password-protected form. The KeyStore class was
demonstrated in Example 7-4, where it was used to
store PublicKey and PrivateKey
objects for digital signatures. A KeyStore can
also store SecretKey objects, however. Simply pass
the SecretKey to the setKeyEntry(
) method, specifying a name for the key and a password to
protect it with. Since the key is not a
PrivateKey, you should pass
null for the Certificate[ ]
argument to this method.