The JavaTM Tutorial

| Start of Tutorial > Start of Trail > Start of Lesson | Search Feedback Form

Trail: Putting It All Together
Lesson: BINGO!

## Signing and Verifying the BINGO Cards

> When a Player joins a game by calling the `mayIPlay` method, the Game generates the cards for the Player to play with. Before sending the cards back to the Player, the Game digitally signs the cards using the APIs in the `java.security` package.

> Later, when a Player claims to have a winning card, the Game verifies the signature to make sure the card was created by this Game for the current game.

> Let's look at the code in the Game that does this. All of the code related to signing and verifying cards is in `NotaryPublic`, an object created and used by the `RingMaster`.

> Note: This section discusses how BINGO uses the security APIs in the JDK to sign cards and verify signatures. It does not talk about the general use of the security APIs. For that, see our online security trail Security in JDK 1.1.

### Setting Up to Sign and Verify Cards

> Following is the constructor for the `NotaryPublic` class:

> >     NotaryPublic() {
> >         KeyPair pair = null;
> >         try {
> >             KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
> >             keyGen.initialize(1024, new SecureRandom());
> >             pair = keyGen.generateKeyPair();
> >         } catch (Exception e) {
> >             ErrorMessages.error("Cannot sign cards. Continuing anyway.");
> >         }
> >         priv = pair.getPrivate();
> >         pub = pair.getPublic();
> >     }

> This constructor generates a public and private key pair and then assigns each member of the pair to separate member variables. The private key of a key pair is used to generate signatures. The public key of a key pair is used to verify them. The `NotaryPublic` uses the same key pair, the one created in the constructor, to generate and verify the signatures for all of the cards created for this Game.

### Signing the Cards

> `NotaryPublic` contains a method `signTheCard` that generates a signature for the `Card` passed into it:

> >     private void signTheCard(Card c, int gameNumber)
> >         throws NoSuchAlgorithmException,
> >     	   InvalidKeyException,
> >     	   SignatureException
> >     {
> >         Signature dsa = Signature.getInstance("SHA/DSA");
> >         byte[] values = new byte[Card.SIZE*Card.SIZE+1];

> >         dsa.initSign(priv);

> >         for (int i = 0; i < Card.SIZE; i++)
> >             for (int j = 0; j < Card.SIZE; j ++)
> >     	    values[Card.SIZE*i + j] =
> >                     (byte)c.boardValues[i][j].number;
> >         values[values.length-1] = (byte)gameNumber;

> >         dsa.update(values);
> >         c.setSignature(dsa.sign());
> >     }

> This method creates a `Signature` object and, using the private key from the `KeyPair`, initializes the `Signature` object for signing. Next, the method fills a byte array with the values from the card and the current game number. The method uses this byte array to `update` the `Signature` object. Then, the method gets the signature from the `Signature` object and sets the signature on the `Card` to it.

> Note that each value on the `Card` and the current game number are converted to bytes. Each value on a `Card` is in the range 1 to 75 and easily fits into a byte. However, if the game number exceeds 255 (the Game does not protect against this), then the conversion from an `int` to a `byte` will truncate the [PENDING: check this] high byte of the integer representing the game number. This could allow a sneaky Player to use a card signed for one game in another game.

> The `Signature` class supports a version of the `update` method that lets you update the signature a byte at a time. However, this is not efficient (too many method calls), so the `signTheCard` method creates and fills a byte array and updates the signature with single method call instead. Generally speaking this is a better way of updating signatures.

> The `signTheCard` method gets called whenever a Player registers for a game. The Player remotely calls the `mayIPlay` method in `RegistrarImpl`. If registration is allowed, `mayIPlay` generates cards for the player and calls `signTheCard` once for each `Card` to sign it. The `Card`s are passed back to the Player along with their signatures.

> When a player detects a BINGO and clicks the "I Won" button, the Player notifies the Game of the win by remotely calling the `BINGO` method and passing in the winning card. The `Card` contains its signature which must be verified by the `NotaryPublic's` `verifyTheSignature` method to win.

### Verifying the Signature

> When a Player has a winning card, it calls the remote method `BINGO` in `RegistrarImpl`. This method calls `RingMaster.verify` which subsequently calls the `verifyTheSignature` method in the `NotaryPublic` to make sure that the `Card` was created and signed by this Game for the current game:

> >     boolean verifyTheSignature(Card c, int gameNumber) {
> >         try {
> >             Signature dsa = Signature.getInstance("SHA/DSA");
> >             byte[] values = new byte[Card.SIZE*Card.SIZE+1];

> >             dsa.initVerify(pub);

> >             for (int i = 0; i < Card.SIZE; i ++)
> >     	    for (int j = 0; j < Card.SIZE; j ++)
> >     	        values[Card.SIZE*i + j] =
> >                         (byte)c.boardValues[i][j].number;
> >             values[values.length-1] = (byte)gameNumber;

> >             dsa.update(values);
> >             return dsa.verify(c.getSignature());
> >         } catch (Exception e) {
> >             return false;
> >         }
> >     }

> The `verifyTheSignature` method is very similar to `signTheCard` but worth a few notes. This method also creates a `Signature` but instead of using the private key from the `KeyPair`, it uses the public key, and initializes the `Signature` for verification with the public key.

> As with `signTheCard`, this method creates and fills a byte array with the values from the `Card` and the current game number, then updates the `Signature` object with the byte array. Finally, the method verifies the signature on the card by passing the `Card`'s signature to the `Signature` object's `verify` method.

> If the values on the `Card` are different than when the card was signed then the signature won't verify. Also, if the game number is different than when the card was signed the signature won't verify. This protects against Players trying to cheat by generating cards based on the announced balls, or using a card that was signed for a different game.

| Start of Tutorial > Start of Trail > Start of Lesson | Search Feedback Form

Copyright 1995-2004 Sun Microsystems, Inc. All rights reserved.