|
|
Start of Tutorial > Start of Trail > Start of Lesson |
Search
Feedback Form |
When a Player joins a game by calling themayIPlaymethod, 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 thejava.securitypackage.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.
Following is the constructor for theNotaryPublicclass: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. TheNotaryPublic() { 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(); }NotaryPublicuses 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.
NotaryPubliccontains a methodsignTheCardthat generates a signature for theCardpassed into it:This method creates aprivate 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()); }Signatureobject and, using the private key from theKeyPair, initializes theSignatureobject 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 toupdatetheSignatureobject. Then, the method gets the signature from theSignatureobject and sets the signature on theCardto it.Note that each value on the
Cardand the current game number are converted to bytes. Each value on aCardis 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 anintto abytewill 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
Signatureclass supports a version of theupdatemethod that lets you update the signature a byte at a time. However, this is not efficient (too many method calls), so thesignTheCardmethod 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
signTheCardmethod gets called whenever a Player registers for a game. The Player remotely calls themayIPlaymethod inRegistrarImpl. If registration is allowed,mayIPlaygenerates cards for the player and callssignTheCardonce for eachCardto sign it. TheCards 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
BINGOmethod and passing in the winning card. TheCardcontains its signature which must be verified by theNotaryPublic'sverifyTheSignaturemethod to win.
When a Player has a winning card, it calls the remote methodBINGOinRegistrarImpl. This method callsRingMaster.verifywhich subsequently calls theverifyTheSignaturemethod in theNotaryPublicto make sure that theCardwas created and signed by this Game for the current game:Theboolean 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; } }verifyTheSignaturemethod is very similar tosignTheCardbut worth a few notes. This method also creates aSignaturebut instead of using the private key from theKeyPair, it uses the public key, and initializes theSignaturefor verification with the public key.As with
signTheCard, this method creates and fills a byte array with the values from theCardand the current game number, then updates theSignatureobject with the byte array. Finally, the method verifies the signature on the card by passing theCard's signature to theSignatureobject'sverifymethod.If the values on the
Cardare 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.