Digital Signatures


As we said earlier, applets were what started the craze over the Java platform. In practice, people discovered that although they could write animated applets like the famous "nervous text" applet, applets could not do a whole lot of useful stuff in the JDK 1.0 security model. For example, because applets under JDK 1.0 were so closely supervised, they couldn't do much good on a corporate intranet, even though relatively little risk attaches to executing an applet from your company's secure intranet. It quickly became clear to Sun that for applets to become truly useful, it was important for users to be able to assign different levels of security, depending on where the applet originated. If an applet comes from a trusted supplier and it has not been tampered with, the user of that applet can then decide whether to give the applet more privileges.

This added control is now possible because of the applet-signing mechanism in Java 1.1. To give more trust to an applet, we need to know two things:

  1. Where did the applet come from?

  2. Was the code corrupted in transit?

In the past 50 years, mathematicians and computer scientists have developed sophisticated algorithms for ensuring the integrity of data and for electronic signatures. The java.security package contains implementations of many of these algorithms. Fortunately, you don't need to understand the underlying mathematics to use the algorithms in the java.security package. In the next sections, we show you how message digests can detect changes in data files and how digital signatures can prove the identity of the signer.

Message Digests

A message digest is a digital fingerprint of a block of data. For example, the so-called SHA1 (secure hash algorithm #1) condenses any data block, no matter how long, into a sequence of 160 bits (20 bytes). As with real fingerprints, one hopes that no two messages have the same SHA1 fingerprint. Of course, that cannot be truethere are only 2160 SHA1 fingerprints, so there must be some messages with the same fingerprint. But 2160 is so large that the probability of duplication occurring is negligible. How negligible? According to James Walsh in True Odds: How Risks Affect Your Everyday Life [Merritt Publishing 1996], the chance that you will die from being struck by lightning is about one in 30,000. Now, think of nine other people, for example, your nine least favorite managers or professors. The chance that you and all of them will die from lightning strikes is higher than that of a forged message having the same SHA1 fingerprint as the original. (Of course, more than 10 people, none of whom you are likely to know, will die from lightning. However, we are talking about the far slimmer chance that your particular choice of people will be wiped out.)

A message digest has two essential properties:

  1. If one bit or several bits of the data are changed, then the message digest also changes.

  2. A forger who is in possession of a given message cannot construct a fake message that has the same message digest as the original.

The second property is again a matter of probabilities, of course. Consider the following message by the billionaire father:

"Upon my death, my property shall be divided equally among my children; however, my son George shall receive nothing."

That message has an SHA1 fingerprint of

 2D 8B 35 F3 BF 49 CD B1 94 04 E0 66 21 2B 5E 57 70 49 E1 7E 

The distrustful father has deposited the message with one attorney and the fingerprint with another. Now, suppose George can bribe the lawyer holding the message. He wants to change the message so that Bill gets nothing. Of course, that changes the fingerprint to a completely different bit pattern:

 2A 33 0B 4B B3 FE CC 1C 9D 5C 01 A7 09 51 0B 49 AC 8F 98 92 

Can George find some other wording that matches the fingerprint? If he had been the proud owner of a billion computers from the time the Earth was formed, each computing a million messages a second, he would not yet have found a message he could substitute.

A number of algorithms have been designed to compute these message digests. The two best-known are SHA1, the secure hash algorithm developed by the National Institute of Standards and Technology, and MD5, an algorithm invented by Ronald Rivest of MIT. Both algorithms scramble the bits of a message in ingenious ways. For details about these algorithms, see, for example, Cryptography and Network Security by William Stallings [Prentice Hall 1998]. Note that recently, subtle regularities have been discovered in MD5. Most cryptographers recommend avoiding it and using SHA1 for that reason. (Both algorithms are easy to compute.)

The Java programming language implements both SHA1 and MD5. The MessageDigest class is a factory for creating objects that encapsulate the fingerprinting algorithms. It has a static method, called getInstance, that returns an object of a class that extends the MessageDigest class. This means the MessageDigest class serves double duty:

  • As a factory class

  • As the superclass for all message digest algorithms

For example, here is how you obtain an object that can compute SHA fingerprints.

 MessageDigest alg = MessageDigest.getInstance("SHA-1"); 

(To get an object that can compute MD5, use the string "MD5" as the argument to getInstance.)

After you have obtained a MessageDigest object, you feed it all the bytes in the message by repeatedly calling the update method. For example, the following code passes all bytes in a file to the alg object created above to do the fingerprinting:

 InputStream in = . . . int ch; while ((ch = in.read()) != -1)    alg.update((byte) ch); 

Alternatively, if you have the bytes in an array, you can update the entire array at once:

 byte[] bytes = . . .; alg.update(bytes); 

When you are done, call the digest method. This method pads the inputas required by the fingerprinting algorithmdoes the computation, and returns the digest as an array of bytes.

 byte[] hash = alg.digest(); 

The program in Example 9-17 computes a message digest, using either SHA or MD5. You can load the data to be digested from a file, or you can type a message in the text area. Figure 9-10 shows the application.

Example 9-17. MessageDigestTest.java
   1. import java.io.*;   2. import java.security.*;   3. import java.awt.*;   4. import java.awt.event.*;   5. import javax.swing.*;   6.   7. /**   8.    This program computes the message digest of a file   9.    or the contents of a text area.  10. */  11. public class MessageDigestTest  12. {  13.    public static void main(String[] args)  14.    {  15.       JFrame frame = new MessageDigestFrame();  16.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  17.       frame.setVisible(true);  18.    }  19. }  20.  21. /**  22.    This frame contains a menu for computing the message  23.    digest of a file or text area, radio buttons to toggle between  24.    SHA-1 and MD5, a text area, and a text field to show the  25.    message digest.  26. */  27. class MessageDigestFrame extends JFrame  28. {  29.    public MessageDigestFrame()  30.    {  31.       setTitle("MessageDigestTest");  32.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);  33.  34.       JPanel panel = new JPanel();  35.       ButtonGroup group = new ButtonGroup();  36.       addRadioButton(panel, "SHA-1", group);  37.       addRadioButton(panel, "MD5", group);  38.  39.       add(panel, BorderLayout.NORTH);  40.       add(new JScrollPane(message), BorderLayout.CENTER);  41.       add(digest, BorderLayout.SOUTH);  42.       digest.setFont(new Font("Monospaced", Font.PLAIN, 12));  43.  44.       setAlgorithm("SHA-1");  45.  46.       JMenuBar menuBar = new JMenuBar();  47.       JMenu menu = new JMenu("File");  48.       JMenuItem fileDigestItem = new JMenuItem("File digest");  49.       fileDigestItem.addActionListener(new  50.          ActionListener()  51.          {  52.             public void actionPerformed(ActionEvent event) { loadFile(); }  53.          });  54.       menu.add(fileDigestItem);  55.       JMenuItem textDigestItem = new JMenuItem("Text area digest");  56.       textDigestItem.addActionListener(new  57.          ActionListener()  58.          {  59.             public void actionPerformed(ActionEvent event)  60.             {  61.                String m = message.getText();  62.                computeDigest(m.getBytes());  63.             }  64.          });  65.       menu.add(textDigestItem);  66.       menuBar.add(menu);  67.       setJMenuBar(menuBar);  68.    }  69.  70.    /**  71.       Adds a radio button to select an algorithm.  72.       @param c the container into which to place the button  73.       @param name the algorithm name  74.       @param g the button group  75.    */  76.    public void addRadioButton(Container c, final String name,  ButtonGroup g)  77.    {  78.       ActionListener listener = new  79.          ActionListener()  80.          {  public void actionPerformed(ActionEvent event) { setAlgorithm(name); }  81.          };  82.       JRadioButton b = new JRadioButton(name, g.getButtonCount() == 0);  83.       c.add(b);  84.       g.add(b);  85.       b.addActionListener(listener);  86.    }  87.  88.    /**  89.       Sets the algorithm used for computing the digest.  90.       @param alg the algorithm name  91.    */  92.    public void setAlgorithm(String alg)  93.    {  94.       try  95.       {  96.          currentAlgorithm = MessageDigest.getInstance(alg);  97.          digest.setText("");  98.       }  99.       catch (NoSuchAlgorithmException e) 100.       { 101.          digest.setText("" + e); 102.       } 103.    } 104. 105.    /** 106.       Loads a file and computes its message digest. 107.    */ 108.    public void loadFile() 109.    { 110.       JFileChooser chooser = new JFileChooser(); 111.       chooser.setCurrentDirectory(new File(".")); 112. 113.       int r = chooser.showOpenDialog(this); 114.       if (r == JFileChooser.APPROVE_OPTION) 115.       { 116.          try 117.          { 118.             String name = chooser.getSelectedFile().getAbsolutePath(); 119.             computeDigest(loadBytes(name)); 120.          } 121.          catch (IOException e) 122.          { 123.             JOptionPane.showMessageDialog(null, e); 124.          } 125.       } 126.    } 127. 128.    /** 129.       Loads the bytes in a file. 130.       @param name the file name 131.       @return an array with the bytes in the file 132.    */ 133.    public byte[] loadBytes(String name) throws IOException 134.    { 135.       FileInputStream in = null; 136. 137.       in = new FileInputStream(name); 138.       try 139.       { 140.          ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 141.          int ch; 142.          while ((ch = in.read()) != -1) 143.             buffer.write(ch); 144.          return buffer.toByteArray(); 145.       } 146.       finally 147.       { 148.          in.close(); 149.       } 150.    } 151. 152.    /** 153.       Computes the message digest of an array of bytes 154.       and displays it in the text field. 155.       @param b the bytes for which the message digest should 156.       be computed. 157.    */ 158.    public void computeDigest(byte[] b) 159.    { 160.       currentAlgorithm.reset(); 161.       currentAlgorithm.update(b); 162.       byte[] hash = currentAlgorithm.digest(); 163.       String d = ""; 164.       for (int i = 0; i < hash.length; i++) 165.       { 166.          int v = hash[i] & 0xFF; 167.          if (v < 16) d += "0"; 168.          d += Integer.toString(v, 16).toUpperCase() + " "; 169.       } 170.       digest.setText(d); 171.    } 172. 173.    private JTextArea message = new JTextArea(); 174.    private JTextField digest = new JTextField(); 175.    private MessageDigest currentAlgorithm; 176.    private static final int DEFAULT_WIDTH = 400; 177.    private static final int DEFAULT_HEIGHT = 300; 178. } 

Figure 9-10. Computing a message digest



 java.security.MessageDigest 1.1 

  • static MessageDigest getInstance(String algorithm)

    returns a MessageDigest object that implements the specified algorithm. Throws NoSuchAlgorithmException if the algorithm is not provided.

    Parameters:

    algorithm

    The name of the algorithm, such as "SHA-1" or "MD5"


  • void update(byte input)

  • void update(byte[] input)

  • void update(byte[] input, int offset, int len)

    update the digest, using the specified bytes.

  • byte[] digest()

    completes the hash computation, returns the computed digest, and resets the algorithm object.

  • void reset()

    resets the digest.

Message Signing

In the last section, you saw how to compute a message digest, a fingerprint for the original message. If the message is altered, then the fingerprint of the altered message will not match the fingerprint of the original. If the message and its fingerprint are delivered separately, then the recipient can check whether the message has been tampered with. However, if both the message and the fingerprint were intercepted, it is an easy matter to modify the message and then recompute the fingerprint. After all, the message digest algorithms are publicly known, and they don't require secret keys. In that case, the recipient of the forged message and the recomputed fingerprint would never know that the message has been altered. In this section, you will see how digital signatures can authenticate a message. When a message is authenticated, you know

  • The message was not altered;

  • The message came from the claimed sender.

To help you understand how digital signatures work, we explain a few concepts from the field called public key cryptography. Public key cryptography is based on the notion of a public key and private key. The idea is that you tell everyone in the world your public key. However, only you hold the private key, and it is important that you safeguard it and don't release it to anyone else. The keys are matched by mathematical relationships, but it is believed to be practically impossible to compute one from the other. That is, even though everyone knows your public key, they can't compute your private key in your lifetime, no matter how many computing resources they have available.

It may seem difficult to believe that nobody can compute the private key from the public keys, but nobody has ever found an algorithm to do this for the encryption algorithms that are in common use today. If the keys are sufficiently long, brute forcesimply trying all possible keyswould require more computers than can be built from all the atoms in the solar system, crunching away for thousands of years. Of course, it is possible that someone could come up with algorithms for computing keys that are much more clever than brute force. For example, the RSA algorithm (the encryption algorithm invented by Rivest, Shamir, and Adleman) depends on the difficulty of factoring large numbers. For the last 20 years, many of the best mathematicians have tried to come up with good factoring algorithms, but so far with no success. For that reason, most cryptographers believe that keys with a "modulus" of 2,000 bits or more are currently completely safe from any attack.

There are two kinds of public/private key pairs: for encryption and for authentication. If anyone sends you a message that was encrypted with your public encryption key, then you can decrypt it with your private decryption key, but nobody else can. Conversely, if you sign a message with your private authentication key, then anyone else can verify the signature by checking with your public key. The verification passes only for messages that you signed, and it fails if anyone else used his or her key to sign the message. (Kahn remarks in the new edition of his book The Codebreakers that this was the first new idea in cryptography in hundreds of years.)

Many cryptographic algorithms, such as RSA and DSA (the Digital Signature Algorithm), use this idea. The exact structure of the keys and what it means for them to match depend on the algorithm. For example, here is a matching pair of public and private DSA keys.

Public key:

 p: fca682ce8e12caba26efccf7110e526db078b05edecbcd1eb4a208f3ae16 17ae01f35b91a47e6df63413c5e12ed0899bcd132acd50d99151bdc43ee7375 92e17 q: 962eddcc369cba8ebb260ee6b6a126d9346e38c5 g: 678471b27a9cf44ee91a49c5147db1a9aaf244f05a434d6486931d2d1427 1b9e35030b71fd73da179069b32e2935630e1c2062354d0da20a6c416e50be794ca4 y: c0b6e67b4ac098eb1a32c5f8c4c1f0e7e6fb9d832532e27d0bdab9ca2d2a 8123ce5a8018b8161a760480fadd040b927281ddb22cb9bc4df596d7de4d1b977d50 

Private key:

 p: fca682ce8e12caba26efccf7110e526db078b05edecbcd1eb4a208f3ae16 17ae01f35b91a47e6df63413c5e12ed0899bcd132acd50d99151bdc43ee737592e17 q: 962eddcc369cba8ebb260ee6b6a126d9346e38c5 g: 678471b27a9cf44ee91a49c5147db1a9aaf244f05a434d6486931d2d1427 1b9e35030b71fd73da179069b32e2935630e1c2062354d0da20a6c416e50be794ca4 x: 146c09f881656cc6c51f27ea6c3a91b85ed1d70a 

These keys have a mathematical relationship, but the exact nature of the relationship is not interesting for practical programming. (If you are interested, you can look it up in The Handbook of Applied Cryptography at http://www.cacr.math.uwaterloo.ca/hac/.)

The obvious question is how to generate the pair of keys. Usually, you do this by feeding the result of some random process to a deterministic procedure that returns the key pair to you. Luckily, how to get a random key pair for public key cryptography is not a question anyone but cryptographers and mathematicians need to worry about.

Here is how it works in practice. Suppose Alice wants to send Bob a message, and Bob wants to know this message came from Alice and not an impostor. Alice writes the message and then signs the message digest with her private key. Bob gets a copy of her public key. Bob then applies the public key to verify the signature. If the verification passes, then Bob can be assured of two facts:

  1. The original message has not been altered.

  2. The message was signed by Alice, the holder of the private key that matches the public key that Bob used for verification.

See Figure 9-11.

Figure 9-11. Public key signature exchange with DSA


You can see why security for private keys is all-important. If someone steals Alice's private key or if a government can require her to turn it over, then she is in trouble. The thief or a government agent can impersonate her by sending messages, money transfer instructions, and so on, that others will believe came from Alice.

Let us put the DSA algorithm to work. Actually, there are three algorithms:

  1. To generate a key pair

  2. To sign a message

  3. To verify a signature

Of course, you generate a key pair only once and then use it for signing and verifying many messages. To generate a new random key pair, make sure you use truly random numbers. For example, the regular random number generator in the Random class, seeded by the current date and time, is not random enough. (The jargon says the basic random number generator in java.util is not "cryptographically secure.") For example, supposing the computer clock is accurate to 1/10 of a second; then, there are at most 864,000 seeds per day. If an attacker knows the day a key was issued (as can often be deduced from the expiration date), then it is an easy matter to generate all possible seeds for that day.

The SecureRandom class generates random numbers that are far more secure than those produced by the Random class. You still need to provide a seed to start the number sequence at a random spot. The best method for doing this is to obtain random input from a hardware device such as a white-noise generator. Another reasonable source for random input is to ask the user to type away aimlessly on the keyboard, but each keystroke should contribute only one or two bits to the random seed. Once you gather such random bits in an array of bytes, you pass it to the setSeed method.

 SecureRandom secrand = new SecureRandom(); byte[] b = new byte[20]; // fill with truly random bits secrand.setSeed(b); 

If you don't seed the random number generator, then it will compute its own 20-byte seed by launching threads, putting them to sleep, and measuring the exact time when they are awakened.

NOTE

This is an innovative algorithm that, at this point, is not known to be safe. And, in the past, algorithms that relied on timing other components of the computer, such as hard disk access time, were later shown not to be completely random.


Once you seed the generator, you can then draw random bytes with the nextBytes method.

 byte[] randomBytes = new byte[64]; secrand.nextBytes(randomBytes); 

Actually, to compute a new DSA key, you don't compute the random numbers yourself. You just pass the random number generator object to the DSA key generation algorithm.

To make a new key pair, you need a KeyPairGenerator object. Just as with the MessageDigest class of the preceding section, the KeyPairGenerator class is both a factory class and the superclass for actual key-pair-generation algorithms. To get a DSA key pair generator, you call the getInstance method with the string "DSA".

 KeyPairGenerator keygen = KeyPairGenerator.getInstance("DSA"); 

The returned object is actually an object of the class sun.security.provider.DSAKeyPairGenerator, which is a subclass of KeyPairGenerator.

To generate keys, you must initialize the key generation algorithm object with the key strength and a secure random number generator. Note that the key strength is not the length of the generated keys but the size of one of the building blocks of the key. In the case of DSA, it is the number of bits in the modulus, one of the mathematical quantities that makes up the public and private keys. Suppose you want to generate a key with a modulus of 512 bits:

 SecureRandom secrand = new SecureRandom(); secrand.setSeed(...); keygen.initialize(512, secrand); 

Now you are ready to generate key pairs.

 KeyPair keys = keygen.generateKeyPair(); KeyPair morekeys = keygen.generateKeyPair(); 

Each key pair has a public and a private key.

 PublicKey pubkey = keys.getPublic(); PrivateKey privkey = keys.getPrivate(); 

To sign a message, you need a signature algorithm object. You use the Signature factory class:

 Signature signalg = Signature.getInstance("DSA"); 

Signature algorithm objects can be used both to sign and to verify a message. To prepare the object for message signing, use the initSign method and pass the private key to the signature algorithm.

 signalg.initSign(privkey); 

Now, use the update method to add bytes to the algorithm objects, in the same way as with the message digest algorithm.

 byte[] bytes = . . .; while (. . .) signalg.update(bytes); 

Finally, compute the signature with the sign method. The signature is returned as an array of bytes.

 byte[] signature = signalg.sign(); 

The recipient of the message must obtain a DSA signature algorithm object and prepare it for signature verification by calling the initVerify method with the public key as parameter.

 Signature verifyalg = Signature.getInstance("DSA"); verifyalg.initVerify(pubkey); 

Then, send the message to the algorithm object.

 byte[] bytes = . . .; while (. . .) verifyalg.update(bytes); 

Finally, verify the signature.

 boolean check = verifyalg.verify(signature); 

If the verify method returns TRue, then the signature was a valid signature of the message that was signed with the matching private key. That is, both the sender and the contents of the message have been authenticated.

Example 9-18 demonstrates the key generation, signing, and verification processes. Run the program like this:

 java SignatureTest -genkey public.key private.key java SignatureTest -sign sample.txt sample.txt.signed private.key java SignatureTest -verify sample.txt.signed public.key 

The program should print the message "verified". Then use a hex editor to make a small change to sample.txt.signed, and run the last command again. You should now get a message "not verified".

Example 9-18. SignatureTest.java

[View full width]

  1. import java.io.*;  2. import java.security.*;  3.  4. /**  5.    This program demonstrates how to sign a message with a private DSA key  6.    and verify it with the matching public key. Usage:  7.    java SignatureTest -genkey public private  8.    java SignatureTest -sign message signed private  9.    java SignatureTest -verify signed public 10. */ 11. public class SignatureTest 12. { 13.    public static void main(String[] args) 14.    { 15.       try 16.       { 17.          if (args[0].equals("-genkey")) 18.          { 19.             KeyPairGenerator pairgen = KeyPairGenerator.getInstance("DSA"); 20.             SecureRandom random = new SecureRandom(); 21.             pairgen.initialize(KEYSIZE, random); 22.             KeyPair keyPair = pairgen.generateKeyPair(); 23.             ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream (args[1])); 24.             out.writeObject(keyPair.getPublic()); 25.             out.close(); 26.             out = new ObjectOutputStream(new FileOutputStream(args[2])); 27.             out.writeObject(keyPair.getPrivate()); 28.             out.close(); 29.          } 30.          else if (args[0].equals("-sign")) 31.          { 32.             ObjectInputStream keyIn = new ObjectInputStream(new FileInputStream(args[3])); 33.             PrivateKey privkey = (PrivateKey) keyIn.readObject(); 34.             keyIn.close(); 35. 36.             Signature signalg = Signature.getInstance("DSA"); 37.             signalg.initSign(privkey); 38. 39.             File infile = new File(args[1]); 40.             InputStream in = new FileInputStream(infile); 41.             int length = (int) infile.length(); 42.             byte[] message = new byte[length]; 43.             in.read(message, 0, length); 44.             in.close(); 45. 46.             signalg.update(message); 47.             byte[] signature = signalg.sign(); 48. 49.             DataOutputStream out = new DataOutputStream(new FileOutputStream(args[2])); 50.             int signlength = signature.length; 51.             out.writeInt(signlength); 52.             out.write(signature, 0, signlength); 53.             out.write(message, 0, length); 54.             out.close(); 55.          } 56.          else if (args[0].equals("-verify")) 57.          { 58.             ObjectInputStream keyIn = new ObjectInputStream(new FileInputStream(args[2])); 59.             PublicKey pubkey = (PublicKey) keyIn.readObject(); 60.             keyIn.close(); 61. 62.             Signature verifyalg = Signature.getInstance("DSA"); 63.             verifyalg.initVerify(pubkey); 64. 65.             File infile = new File(args[1]); 66.             DataInputStream in = new DataInputStream(new FileInputStream(infile)); 67.             int signlength = in.readInt(); 68.             byte[] signature = new byte[signlength]; 69.             in.read(signature, 0, signlength); 70. 71.             int length = (int) infile.length() - signlength - 4; 72.             byte[] message = new byte[length]; 73.             in.read(message, 0, length); 74.             in.close(); 75. 76.             verifyalg.update(message); 77.             if (!verifyalg.verify(signature)) 78.                System.out.print("not "); 79.             System.out.println("verified"); 80.          } 81.       } 82.       catch (Exception e) 83.       { 84.          e.printStackTrace(); 85.       } 86.    } 87. 88.    private static final int KEYSIZE = 512; 89. } 


 java.security.KeyPairGenerator 1.1 

  • static KeyPairGenerator getInstance(String algorithm)

    returns a KeyPairGenerator object that implements the specified algorithm. Throws a NoSuchAlgorithmException if the algorithm is not provided.

    Parameters:

    algorithm

    The name of the algorithm, such as "DSA"


  • void initialize(int strength, SecureRandom random)

    initializes this KeyPairGenerator.

    Parameters:

    strength

    An algorithm-specific measurement, typically, the number of bits of one of the algorithm parameters

     

    random

    The source of random bits for generating keys


  • KeyPair generateKeyPair()

    generates a new key pair.


 java.security.KeyPair 1.1 

  • PrivateKey getPrivate()

    returns the private key from the key pair.

  • PublicKey getPublic()

    returns the public key from the key pair.


 java.security.Signature 1.1 

  • static Signature getInstance(String algorithm)

    returns a Signature object that implements the specified algorithm. Throws NoSuchAlgorithmException if the algorithm is not provided.

    Parameters:

    algorithm

    The name of the algorithm, such as "DSA"


  • void initSign(PrivateKey privateKey)

    initializes this object for signing. Throws InvalidKeyException if the key type does not match the algorithm type.

    Parameters:

    privateKey

    The private key of the identity whose signature is being computed


  • void update(byte input)

  • void update(byte[] input)

  • void update(byte[] input, int offset, int len)

    update the message buffer, using the specified bytes.

  • byte[] sign()

    completes the signature computation and returns the computed signature.

  • void initVerify(PublicKey publicKey)

    initializes this object for verification. Throws InvalidKeyException if the key type does not match the algorithm type.

    Parameters:

    publicKey

    The public key of the identity to be verified


  • boolean verify(byte[] signature)

    checks whether the signature is valid.

Message Authentication

Suppose you get a message from your friend, signed by your friend with his private key, using the method we just showed you. You may already have his public key, or you can easily get it by asking him for a copy or by getting it from your friend's web page. Then, you can verify that the message was in fact authored by your friend and has not been tampered with. Now, suppose you get a message from a stranger who claims to represent a famous software company, urging you to run the program that is attached to the message. The stranger even sends you a copy of his public key so you can verify that he authored the message. You check that the signature is valid. This proves that the message was signed with the matching private key and that it has not been corrupted.

Be careful: You still have no idea who wrote the message. Anyone could have generated a pair of public and private keys, signed the message with the private key, and sent the signed message and the public key to you. The problem of determining the identity of the sender is called the authentication problem.

The usual way to solve the authentication problem is simple. Suppose the stranger and you have a common acquaintance you both trust. Suppose the stranger meets your acquaintance in person and hands over a disk with the public key. Your acquaintance later meets you, assures you that he met the stranger and that the stranger indeed works for the famous software company, and then gives you the disk (see Figure 9-12). That way, your acquaintance vouches for the authenticity of the stranger.

Figure 9-12. Authentication through a trusted intermediary


In fact, your acquaintance does not actually need to meet you. Instead, he can apply his private signature to the stranger's public key file (see Figure 9-13).

Figure 9-13. Authentication through a trusted intermediary's signature


When you get the public key file, you verify the signature of your acquaintance, and because you trust him, you are confident that he did check the stranger's credentials before applying his signature.

However, you may not have a common acquaintance. Some trust models assume that there is always a "chain of trust"a chain of mutual acquaintances so that you trust every member of that chain. In practice, of course, that isn't always true. You may trust your acquaintance, Alice, and you know that Alice trusts Bob, but you don't know Bob and aren't sure that you trust him. Other trust models assume that there is a benevolent big brother in whom we all trust. The best known of these companies is VeriSign, Inc. (http://www.verisign.com).

You will often encounter digital signatures that are signed by one or more entities who will vouch for the authenticity, and you will need to evaluate to what degree you trust the authenticators. You might place a great deal of trust in VeriSign, perhaps because you saw their logo on many web pages or because you heard that they require multiple people with black attaché cases to come together into a secure chamber whenever new master keys are to be minted.

However, you should have realistic expectations about what is actually being authenticated. The CEO of VeriSign does not personally meet every individual or company representative when authenticating a public key. You can get a "class 1" ID simply by filling out a web form and paying a small fee. The key is mailed to the e-mail address included in the certificate. Thus, you can be reasonably assured that the e-mail address is genuine, but the requestor could have filled in any name and organization. There are more stringent classes of IDs. For example, with a "class 3" ID, VeriSign will require an individual requestor to appear before a notary public, and it will check the financial rating of a corporate requestor. Other authenticators will have different procedures. Thus, when you receive an authenticated message, it is important that you understand what, in fact, is being authenticated.

The X.509 Certificate Format

One of the most common formats for signed certificates is the X.509 format. X.509 certificates are widely used by VeriSign, Microsoft, Netscape, and many other companies, for signing e-mail messages, authenticating program code, and certifying many other kinds of data. The X.509 standard is part of the X.500 series of recommendations for a directory service by the international telephone standards body, the CCITT. In its simplest form, an X.509 certificate contains the following data:

  • Version of certificate format

  • Serial number of certificate

  • Signature algorithm identifier (algorithm ID + parameters of the algorithm used to sign the certificate)

  • Name of the signer of the certificate

  • Period of validity (begin/end date)

  • Name of the identity being certified

  • Public key of identity being certified (algorithm ID + parameters of the algorithm + public key value)

  • Signature (hash code of all preceding fields, encoded with private key of signer)

Thus, the signer guarantees that a certain identity has a particular public key.

Extensions to the basic X.509 format make it possible for the certificates to contain additional information.

The precise structure of X.509 certificates is described in a formal notation, called "abstract syntax notation #1" or ASN.1. Figure 9-14 shows the ASN.1 definition of version 3 of the X.509 format. The exact syntax is not important for us, but, as you can see, ASN.1 gives a precise definition of the structure of a certificate file. The basic encoding rules, or BER, describe precisely how to save this structure in a binary file. That is, BER describes how to encode integers, character strings, bit strings, and constructs such as SEQUENCE, CHOICE, and OPTIONAL.

NOTE

You can find more information on ASN.1 in ASN.1Communication Between Heterogeneous Systems by Olivier Dubuisson [Academic Press 2000] (http://www.oss.com/asn1/dubuisson.html) and ASN.1 Complete by John Larmouth [Morgan Kaufmann Publishers 1999] (http://www.nokalva.com/asn1/larmouth.html).


Actually, the BER rules are not unique; there are several ways of specifying some elements. The distinguished encoding rules (DER) remove these ambiguities. For a readable description of the BER encoding format, we recommend A Layman's Guide to a Subset of ASN.1, BER, and DER by Burton S. Kaliski, Jr., available from ftp://ftp.rsa.com/pub/pkcs/ps/layman.ps. For the source code of a useful program for dumping BER encoded files, go to http://www.cs.auckland.ac.nz/~pgut001/dumpasn1.c.

Figure 9-14. ASN.1 definition of X.509v3
 [Certificate  ::=  SEQUENCE  {         tbsCertificate        TBSCertificate,         signatureAlgorithm    AlgorithmIdentifier,         signature             BIT STRING  }    TBSCertificate  ::=  SEQUENCE  {         version         [0]   EXPLICIT Version DEFAULT v1,         serialNumber          CertificateSerialNumber,         signature             AlgorithmIdentifier,         issuer                Name,         validity              Validity,         subject               Name,         subjectPublicKeyInfo  SubjectPublicKeyInfo,         issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,                               -- If present, version must be v2                                   or v3         subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,                              -- If present, version must be v2                                   or v3         extensions      [3]         EXPLICIT Extensions OPTIONAL                                    -- If present, version must be v3         }    Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }    CertificateSerialNumber  ::=  INTEGER    Validity ::= SEQUENCE {         notBefore      CertificateValidityDate,         notAfter       CertificateValidityDate }    CertificateValidityDate ::= CHOICE {         utcTime        UTCTime,         generalTime    GeneralizedTime }    UniqueIdentifier  ::=  BIT STRING    SubjectPublicKeyInfo  ::=  SEQUENCE  {         algorithm             AlgorithmIdentifier,         subjectPublicKey      BIT STRING  }    Extensions  ::=  SEQUENCE OF Extension    Extension  ::=  SEQUENCE  {         extnID      OBJECT IDENTIFIER,         critical    BOOLEAN DEFAULT FALSE,         extnValue   OCTET STRING  } 

NOTE

Peter Gutmann's web site (http://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt) contains an entertaining and informative description of the many discrepancies in the X.509 format as implemented by different vendors.


Certificate Generation

The JDK comes with the keytool program, which is a command-line tool to generate and manage a set of certificates. We expect that ultimately the functionality of this tool will be embedded in other, more user-friendly programs. But right now, we use keytool to show how Alice can sign a document and send it to Bob, and how Bob can verify that the document really was signed by Alice and not an impostor. We do not discuss all of the keytool featuressee the JDK documentation for complete information.

The keytool program manages keystores, databases of certificates and private keys. Each entry in the keystore has an alias. Here is how Alice creates a keystore, alice.store, and generates a key pair with alias alice.

 keytool -genkey -keystore alice.store -alias alice 

When creating or opening a keystore, you are prompted for a keystore password. For this example, just use password. If you were to use the keytool-generated keystore for any serious purpose, you would need to choose a good password and safeguard this fileit contains private signature keys.

When generating a key, you are prompted for the following information:

 Enter keystore password:  password What is your first and last name?   [Unknown]:  Alice Lee What is the name of your organizational unit?   [Unknown]:  Engineering Department What is the name of your organization?   [Unknown]:  ACME Software What is the name of your City or Locality?   [Unknown]:  Cupertino What is the name of your State or Province?   [Unknown]:  California What is the two-letter country code for this unit?   [Unknown]:  US Is <CN=Alice Lee, OU=Engineering Department, O=ACME Software, L=Cupertino, ST=California, C=US> correct?   [no]:  Y 

The keytool uses X.500 distinguished names, with components Common Name (CN), Organizational Unit (OU), Organization (O), Location (L), State (ST), and Country (C) to identify key owners and certificate issuers.

Finally, specify a key password, or press ENTER to use the keystore password as the key password.

Suppose Alice wants to give her public key to Bob. She needs to export a certificate file:

 keytool -export -keystore alice.store -alias alice -file alice.cert 

Now Alice can send the certificate to Bob. When Bob receives the certificate, he can print it:

 keytool -printcert -file alice.cert 

The printout looks like this:

[View full width]

Owner: CN=Alice Lee, OU=Engineering Department, O=ACME Software, L=Cupertino, ST=California, C=US Issuer: CN=Alice Lee, OU=Engineering Department, O=ACME Software, L=Cupertino, ST=California, C=US Serial number: 4145f46b Valid from: Mon Sep 13 12:26:35 PDT 2004 until: Sun Dec 12 11:26:35 PST 2004 Certificate fingerprints: MD5: B6:12:34:88:7C:5E:63:28:21:0D:DC:C9:F1:C1:9C:94 SHA1: 05:AF:B6:0B:8A:D6:86:0A:0D:06:97:FA:9A:63:F4:09:0B:64:68:10

This certificate is self-signed. Therefore, Bob cannot use another trusted certificate to check that this certificate is valid. Instead, he can call Alice and have her read the certificate fingerprint over the phone.

NOTE

Some certificate issuers publish certificate fingerprints on their web sites. For example, the JRE includes the keystore cacerts in the jre/lib/security directory. It contains certificates from Thawte and VeriSign. To list the contents of a keystore, use the -list option:


keytool -list -v -keystore jre/lib/security/cacerts

The password for this keystore is changeit. One of the certificates in this keystore is

[View full width]

Owner: OU=VeriSign Trust Network, OU="(c) 1998 VeriSign, Inc. - For authorized use only", OU=Class 1 Public Primary Certification Authority - G2, O="VeriSign, Inc.", C=US Issuer: OU=VeriSign Trust Network, OU="(c) 1998 VeriSign, Inc. - For authorized use only", OU=Class 1 Public Primary Certification Authority - G2, O="VeriSign, Inc.", C=US Serial number: 4cc7eaaa983e71d39310f83d3a899192 Valid from: Sun May 17 17:00:00 PDT 1998 until: Tue Aug 01 16:59:59 PDT 2028 Certificate fingerprints: MD5: DB:23:3D:F9:69:FA:4B:B9:95:80:44:73:5E:7D:41:83 SHA1: 27:3E:E1:24:57:FD:C4:F9:0C:55:E8:2B:56:16:7F:62:F5:32:E5:47

You can check that your certificate is valid by visiting the web site http://www.verisign.com/repository/root.html.


Once Bob trusts the certificate, he can import it into his keystore.

 keytool -import -keystore bob.store -alias alice -file alice.cert 

CAUTION

Never import into a keystore a certificate that you don't fully trust. Once a certificate is added to the keystore, any program that uses the keystore assumes that the certificate can be used to verify signatures.


Now Alice can start sending signed documents to Bob. The jarsigner tool signs and verifies JAR files. Alice simply adds the document to be signed into a JAR file.

 jar cvf document.jar document.txt 

Then she uses the jarsigner tool to add the signature to the file. She needs to specify the keystore, the JAR file, and the alias of the key to use.

 jarsigner -keystore alice.store document.jar alice 

When Bob receives the file, he uses the -verify option of the jarsigner program.

 jarsigner -verify -keystore bob.store document.jar 

Bob does not need to specify the key alias. The jarsigner program finds the X.500 name of the key owner in the digital signature and looks for matching certificates in the keystore.

If the JAR file is not corrupted and the signature matches, then the jarsigner program prints

 jar verified. 

Otherwise, the program displays an error message.

Certificate Signing

In the preceding section, you saw how to use a self-signed certificate to distribute a public key to another party. However, the recipient of the certificate needed to ensure that the certificate was valid by verifying the fingerprint with the issuer. More commonly, a certificate is signed by a trusted intermediary.

The JDK does not contain tools for certificate signing. In this section, you see how to write a program that signs a certificate with a private key from a keystore. This program is useful in its own right, and it shows you how to write programs that access the contents of certificates and keystores.

Before looking inside the program code, let's see how to put it to use. Suppose Alice wants to send her colleague Cindy a signed message. But Cindy doesn't want to call up everyone who sends her a signature file to verify the signature fingerprint. There needs to be an entity that Cindy trusts to verify signatures. In this example, we suppose that Cindy trusts the Information Resources Department at ACME Software to perform this service. To simulate this process, you'll need to create an added keystore acmesoft.store. Generate a key and export the self-signed certificate.

 keytool -genkey -keystore acmesoft.store -alias acmeroot keytool -export -alias acmeroot -keystore acmesoft.store -file acmeroot.cert 

Then add it to Cindy's keystore.

 keytool -import -alias acmeroot -keystore cindy.store -file acmeroot.cert 

Cindy still needs to verify the fingerprint of that root certificate, but from now on, she can simply accept all certificates that are signed by it.

For Alice to send messages to Cindy and to everyone else at ACME Software, she needs to bring her certificate to the Information Resources Department and have it signed. However, the keytool program in the JDK does not have this functionality. That is where the certificate signer program in Example 9-19 comes in. The program reads a certificate file and signs it with a private key in a keystore. An authorized staff member at ACME Software would verify Alice's identity and generate a signed certificate as follows:

 java CertificateSigner -keystore acmesoft.store -alias acmeroot    -infile alice.cert -outfile alice_signedby_acmeroot.cert 

The certificate signer program must have access to the ACME Software keystore, and the staff member must know the keystore password. Clearly, this is a sensitive operation.

Now Alice gives the file alice_signedby_acmeroot.cert file to Cindy and to anyone else in ACME Software. Alternatively, ACME Software can simply store the file in a company directory. Remember, this file contains Alice's public key and an assertion by ACME Software that this key really belongs to Alice.

NOTE

The keytool program supports a different mechanism for key signing. The -certreq option produces a certificate request in a standard format that can be processed by certificate authorities such as VeriSign, or by local authorities running software such as the Netscape certificate server.


When Cindy imports the signed certificate into her keystore, the keystore verifies that the key was signed by a trusted root key that is already present in the keystore and she is not asked to verify the certificate fingerprint.

Once Cindy has added the root certificate and the certificates of the people who regularly send her documents, she never has to worry about the keystore again.

CAUTION

This scenario is for illustration only. The keytool is really not suitable as a tool for end users. The keytool silently accepts certificates that are signed by a party that it already trusts, and it asks the user to confirm those that it cannot verify. It never rejects a certificate. It is all too easy for a confused user to accept an invalid certificate.


Now let us look at the source code of Example 9-19. First, we load the keystore. The getInstance factory method of the KeyStore class creates a keystore instance of the appropriate type. The keytool-generated keystore has a type of "JKS". The provider of this keystore type is "SUN".

 KeyStore store = KeyStore.getInstance("JKS", "SUN"); 

Now, we load the keystore data. The load method requires an input stream and a password. Note that the password is specified as a char[] array, not a string. The JVM can keep strings around for a long time before they are garbage-collected. Hackers could potentially find these strings, for example, by examining the contents of swap files. However, character arrays can be cleared immediately after they are used.

Here is the code for loading the keystore. Note that we fill the password with spaces immediately after use.

 InputStream in = . . .; char[] password = . . .; store.load(in, password); Arrays.fill(password, ' '); in.close(); 

Next, we use the getKey method to retrieve the private key for signing. The getKey method requires the key alias and key password. Its return type is Key, and we cast it to PrivateKey since we know that the retrieved key is a private key.

 char[] keyPassword = . . .; PrivateKey issuerPrivateKey = (PrivateKey) store.getKey(alias, keyPassword); Arrays.fill(keyPassword, ' '); 

Now we are ready to read in the certificate that needs to be signed. The CertificateFactory class can read in certificates from an input stream. First, we get a factory of the appropriate type:

 CertificateFactory factory = CertificateFactory.getInstance("X.509"); 

Then, we call the generateCertificate method with an input stream:

 in = new FileInputStream(inname); X509Certificate inCert = (X509Certificate) factory.generateCertificate(in); in.close(); 

The return type of the generateCertificate method is the abstract Certificate class that is the superclass of concrete classes such as X509Certificate. Because we know that the input file actually contains an X509Certificate, we use a cast.

CAUTION

Two types are called Certificate: a deprecated interface in the java.security package and an abstract class in the java.security.cert package, which is the class that you want to use. If you import both the java.security and the java.security.cert package into your program, be sure to resolve the ambiguity and explicitly reference the java.security.cert.Certificate class.


The purpose of this program is to sign the bytes in the certificate. We retrieve the bytes with the getTBSCertificate method:

 byte[] inCertBytes = inCert.getTBSCertificate(); 

Next, we need the distinguished name of the issuer, which we insert into the signed certificate. The name is stored in the issuer certificate in the keystore. We fetch certificates from the keystore with the getCertificate method. Since certificates are public information, we supply only the alias, not a password.

 X509Certificate issuerCert = (X509Certificate) store.getCertificate(alias); 

The getCertificate method has return type Certificate, but once again we know that the returned value is actually X509Certificate. We obtain the issuer identity from the certificate by calling the getSubjectDN method. That method returns an object of some type that implements the Principal interface. Conceptually, a principal is a real-world entity such as a person, organization, or company.

 Principal issuer = issuerCert.getSubjectDN(); 

We also retrieve the name of the signing algorithm.

 String issuerSigAlg = issuerCert.getSigAlgName() 

Now we must leave the realm of the standard security library. The standard library contains no methods for generating new certificates. Libraries for certificate generation are available from third-party vendors such as RSA Security, Inc., and the Legion of Bouncy Castle (http://www.bouncycastle.org). However, we use the classes in the sun.security.x509 package. The usual caveats apply. This package might not be supplied by third-party vendors of Java technology, and Sun Microsystems might change the behavior at any time.

The following code segment carries out these steps:

1.

Generates a certificate information object from the bytes in the certificate that is to be signed;

2.

Sets the issuer name;

3.

Creates the certificate and signs it with the issuer's private key;

4.

Saves the signed certificate to a file.

 X509CertInfo info = new X509CertInfo(inCertBytes); info.set(X509CertInfo.ISSUER, new CertificateIssuerName((X500Name) issuer)); X509CertImpl outCert = new X509CertImpl(info); outCert.sign(issuerPrivateKey, issuerSigAlg); outCert.derEncode(out); 

We do not discuss the use of these classes in detail because we do not recommend the use of Sun libraries for production code. A future version of the JDK may contain classes for certificate generation. In the meantime, you may want to rely on third-party libraries or simply use existing certificate generation software.

Example 9-19. CertificateSigner.java
   1. import java.io.*;   2. import java.security.*;   3. import java.security.cert.*;   4. import java.util.*;   5.   6. import sun.security.x509.X509CertInfo;   7. import sun.security.x509.X509CertImpl;   8. import sun.security.x509.X500Name;   9. import sun.security.x509.CertificateIssuerName;  10.  11. /**  12.    This program signs a certificate, using the private key of  13.    another certificate in a keystore.  14. */  15. public class CertificateSigner  16. {  17.    public static void main(String[] args)  18.    {  19.       String ksname = null; // the keystore name  20.       String alias = null; // the private key alias  21.       String inname = null; // the input file name  22.       String outname = null; // the output file name  23.       for (int i = 0; i < args.length; i += 2)  24.       {  25.          if (args[i].equals("-keystore"))  26.             ksname = args[i + 1];  27.          else if (args[i].equals("-alias"))  28.             alias = args[i + 1];  29.          else if (args[i].equals("-infile"))  30.             inname = args[i + 1];  31.          else if (args[i].equals("-outfile"))  32.             outname = args[i + 1];  33.          else usage();  34.       }  35.  36.       if (ksname == null || alias == null || inname == null || outname == null) usage();  37.  38.       try  39.       {  40.          PushbackReader console = new PushbackReader(new InputStreamReader(System.in));  41.  42.          KeyStore store = KeyStore.getInstance("JKS", "SUN");  43.          InputStream in = new FileInputStream(ksname);  44.          System.out.print("Keystore password: ");  45.          System.out.flush();  46.          char[] password = readPassword(console);  47.          store.load(in, password);  48.          Arrays.fill(password, ' ');  49.          in.close();  50.  51.          System.out.print("Key password for " + alias + ": ");  52.          System.out.flush();  53.          char[] keyPassword = readPassword(console);  54.          PrivateKey issuerPrivateKey = (PrivateKey) store.getKey(alias, keyPassword);  55.          Arrays.fill(keyPassword, ' ');  56.  57.          if (issuerPrivateKey == null) error("No such private key");  58.  59.          in = new FileInputStream(inname);  60.  61.          CertificateFactory factory = CertificateFactory.getInstance("X.509");  62.  63.          X509Certificate inCert = (X509Certificate) factory.generateCertificate(in);  64.          in.close();  65.          byte[] inCertBytes = inCert.getTBSCertificate();  66.  67.          X509Certificate issuerCert = (X509Certificate) store.getCertificate(alias);  68.          Principal issuer = issuerCert.getSubjectDN();  69.          String issuerSigAlg = issuerCert.getSigAlgName();  70.  71.          FileOutputStream out = new FileOutputStream(outname);  72.  73.          X509CertInfo info = new X509CertInfo(inCertBytes);  74.          info.set(X509CertInfo.ISSUER, new CertificateIssuerName((X500Name) issuer));  75.  76.          X509CertImpl outCert = new X509CertImpl(info);  77.          outCert.sign(issuerPrivateKey, issuerSigAlg);  78.          outCert.derEncode(out);  79.  80.          out.close();  81.       }  82.       catch (Exception e)  83.       {  84.          e.printStackTrace();  85.       }  86.    }  87.  88.    /**  89.       Reads a password.  90.       @param in the reader from which to read the password  91.       @return an array of characters containing the password  92.    */  93.    public static char[] readPassword(PushbackReader in)  94.       throws IOException  95.    {  96.       final int MAX_PASSWORD_LENGTH = 100;  97.       int length = 0;  98.       char[] buffer = new char[MAX_PASSWORD_LENGTH];  99. 100.       while (true) 101.       { 102.          int ch = in.read(); 103.          if (ch == '\r' || ch == '\n' || ch == -1 104.             || length == MAX_PASSWORD_LENGTH) 105.          { 106.             if (ch == '\r') // handle DOS "\r\n" line ends 107.             { 108.                ch = in.read(); 109.                if (ch != '\n' && ch != -1) in.unread(ch); 110.             } 111.             char[] password = new char[length]; 112.             System.arraycopy(buffer, 0, password, 0, length); 113.             Arrays.fill(buffer, ' '); 114.             return password; 115.          } 116.          else 117.          { 118.             buffer[length] = (char) ch; 119.             length++; 120.          } 121.       } 122.    } 123. 124.    /** 125.       Prints an error message and exits. 126.       @param message 127.    */ 128.    public static void error(String message) 129.    { 130.       System.out.println(message); 131.       System.exit(1); 132.    } 133. 134.    /** 135.       Prints a usage message and exits. 136.    */ 137.    public static void usage() 138.    { 139.       System.out.println("Usage: java CertificateSigner" 140.          + " -keystore keyStore -alias issuerKeyAlias" 141.          + " -infile inputFile -outfile outputFile"); 142.       System.exit(1); 143.    } 144. } 


 java.security.KeyStore 1.2 

  • static getInstance(String type)

  • static getInstance(String type, String provider)

    construct a keystore object of the given type. If no provider is specified, the default provider is used. To work with keystores generated by the keytool, specify "JKS" as the type and "SUN" as the provider.

  • void load(InputStream in, char[] password)

    loads a keystore from a stream. The password is kept in a character array so that it does not become part of the JVM string pool.

  • Key getKey(String alias, char[] password)

    returns a private key with the given alias that is stored in this keystore.

  • Certificate getCertificate(String alias)

    returns a certificate for a public key with the given alias that is stored in this keystore.


 java.security.cert.CertificateFactory 1.2 

  • CertificateFactory getInstance(String type)

    creates a certificate factory for the given type. The type is a certificate type such as "X509".

  • Certificate generateCertificate(InputStream in)

    loads a certificate from an input stream.


 java.security.cert.Certificate 1.2 

  • PublicKey getPublicKey()

    returns the public key that is being guaranteed by this certificate.

  • byte[] getEncoded()

    gets the encoded form of this certificate.

  • String getType()

    returns the type of this certificate, such as "X509".


 java.security.cert.X509Certificate 1.2 

  • Principal getSubjectDN()

  • Principal getIssuerDN()

    get the owner (or subject) and issuer distinguished names from the certificate.

  • Date getNotBefore()

  • Date getNotAfter()

    get the validity period start and end dates of the certificate.

  • BigInteger getSerialNumber()

    gets the serial number value from the certificate.

  • String getSigAlgName()

  • byte[] getSignature()

    get the signature algorithm name and the owner signature from the certificate.

  • byte[] getTBSCertificate()

    gets the DER-encoded certificate information that needs to be signed by the certificate issuer.



    Core JavaT 2 Volume II - Advanced Features
    Building an On Demand Computing Environment with IBM: How to Optimize Your Current Infrastructure for Today and Tomorrow (MaxFacts Guidebook series)
    ISBN: 193164411X
    EAN: 2147483647
    Year: 2003
    Pages: 156
    Authors: Jim Hoskins

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