Implementing the Digital Signature Algorithm (DSA)

  

The JDK 1.4 supports only the DSA and RSA out of the box for providing service providers. The JDK 1.4 is easily extensible to add more service providers from other vendors or to build a custom one for algorithms such as the ECDSA. The ECDSA is a very popular algorithm, and there are service providers for the algorithm from companies such as Certicom and Cryptix. For many organizations, using the DSA and RSA signatures is sufficient. An example of the DSA algorithm is shown in Listing 11-1 using both the JDK 1.4 and an implementation demonstrating the DSA algorithm.

The first step in using the JDK 1.4 framework is to generate a key pair using the java.security.KeyPairGenerator class's getInstance method. By passing in the variable DSA , a key pair for the DSA algorithm is created. If RSA is passed in as the parameter, a pair of RSA keys is generated. Likewise, the java.security.Signature class's getInstance method initializes the signature class by passing in a SHA1withDSA parameter. To generate RSA signatures, the MD5withRSA , MD2withRSA , and SHA1withRSA parameters can be used depending on which message digest needs to be implemented. Whether the signature is being used for generating the signature or verifying the signature will determine which key, public or private, will be initialized into the algorithm.

Tip  

All signature algorithms require that the data be passed through the update method for both verifying and generating the signature. After the message data has been updated, a signature can be returned from the sign method, or a signature can be verified by passing it in the verify method.

One of the features found in the JDK 1.4 is the java.security.SignedObject class. The SignedObject class is created with a digital signature passed into it and a serialized object. The purpose of the SignedObject class is to protect the runtime object with an associated signature. If the integrity of the object is compromised, then the signature detects it and an exception is thrown. The SignedObject provides a deep copy of the serialized object so a digest can be created and the integrity checked. Features like this one and the capability to use a DSA and RSA signature out of the box make Java a powerful programming language. Listing 11-1 shows the RichDSA class, which is a sample application that demonstrates these concepts.

Listing 11-1: The RichDSA class: The DSA Signature sample application
start example
 package com.richware.chap11;     import java.security.*; import javax.crypto.*; import javax.crypto.spec.*; import javax.crypto.interfaces.*; import java.security.spec.*; import java.math.BigInteger; import java.io.*;     /**  * Class RichDSA  * Description: This is an example  * implementation of the DSA  * algorithm.  *  * Copyright:    Copyright (c) 2002 Wiley Publishing, Inc.  * @author Rich Helton <rhelton@richware.com>  * @version 1.0   * DISCLAIMER: Please refer to the disclaimer at the beginning of this book.  */ public class RichDSA  {   /* the key length */   private int l_ = 1024;       /* the private key */   private BigInteger x_;       /* the public key */   private BigInteger y_;       /* the DSA parameters, see spec */   /* h is used to generate g */   private BigInteger h_;   private BigInteger p_;   private BigInteger q_;   private BigInteger g_;       /* q bit length */   int qBitLength_ = 160;       /* the SHA1 message digest */   private MessageDigest md_;       /* To debug or not to debug */   private final boolean DEBUG = false;       /* The certainty, that the generated numbers are prime. */   private final int CERTAINTY = 80;       /* The secure random generator */   private SecureRandom secureRandom_;       /* BigInteger Constants */   private static final BigInteger ZERO =     BigInteger.valueOf(0L);   private static final BigInteger ONE  =     BigInteger.valueOf(1L);   private static final BigInteger TWO  =     BigInteger.valueOf(2L);       /**    * Constructor RichDSA    *    *    */   public RichDSA()    {     initKeys();   }       /**    * Method initKeys    */   public void initKeys()    {     BigInteger x, c, qMultTwo;     try      {       md_ = MessageDigest.getInstance("SHA1");       if (secureRandom_ == null)        {         secureRandom_ = new SecureRandom();       }       int     counter     = 0;       boolean primesFound = false;       while (!primesFound)        {         counter = 0;             /*          * Calculate Q          */         q_ = new BigInteger(qBitLength_, CERTAINTY,                             secureRandom_);         while ((counter < 4096) && (!primesFound))          {           /*            * q must be a divisor of p            * h ^ ((p-1) / q) mod p            * and p being prime are a             * must. X will be shifted to try            * another random and tested for prime.            * See DSA Spec.             * Appendix 2 and 3.            */           x        = reseedX();           qMultTwo = q_.multiply(TWO);           c        = x.mod(qMultTwo);               /*            * Calculate P            */           p_ = x.subtract(c.subtract(ONE));               /*            * If P is long enough and is prime,            * use it            */           if (p_.bitLength() >= (l_))            {             if (DEBUG)              {               System.out.println("Counter :" + counter);             }                 /*              * P must be prime to quit              */             if (p_.isProbablePrime(CERTAINTY))              {               primesFound = true;             }           }               counter++;         }       }           /*        * Calculate H        */       boolean hFound = false;       while (!hFound)        {         h_ = new BigInteger(l_, secureRandom_);         if ((h_.compareTo(ONE) > 0)                  (h_.compareTo(p_.subtract(ONE)) < 0))          {           hFound = true;         }       }           /*        * Generate G, the generator        */       BigInteger pMinusOneOverQ = p_.subtract(ONE).divide(q_);       boolean    gFound         = false;       while (!gFound)        {         g_ = h_.modPow(pMinusOneOverQ, p_);             if (g_.compareTo(ONE) > 0)          {           gFound = true;         }       }           /*        * Generate X, the private key        */       x_ = new BigInteger(qBitLength_ - 1, secureRandom_);       while ((x_.compareTo(BigInteger.ZERO)) == 0)        {         x_ = new BigInteger(qBitLength_ - 1, secureRandom_);       }           /*        * Generate Y, the public key        */       y_ = g_.modPow(x_, p_);           /*        * If DEBUG, print the results        */       if (DEBUG)        {         System.out.println();         System.out.println("p_ :" + p_);         System.out.println();         System.out.println("q_ :" + q_);         System.out.println();         System.out.println("x_ :" + x_);         System.out.println();         System.out.println("y_ :" + y_);         System.out.println();         System.out.println("g_ :" + g_);         System.out.println();         System.out.println("h_ :" + h_);       }     }         /*      * Catches      */     catch (Exception ex)      {       ex.printStackTrace();     }   }       /**    * Method reseedX    * Description reseed X by shifting    *    * @return a new random X    *    */   private BigInteger reseedX()    {     byte[] shiftBytes = new byte[(l_ / 8)];     secureRandom_.nextBytes(shiftBytes);     shiftBytes[0] = (byte) (shiftBytes[0]  128);     return (new BigInteger(1, shiftBytes));   }       /**    * Method sign    * Description: return the     * signature    * @return mostly r and s    *    */   public byte[] sign()    {     try      {       /*        * Calculate the digest, number        */       byte[]     digest = md_.digest();       BigInteger m      = new BigInteger(1, digest);           /*        * Generate k, a random number        * 0 < k < q        */       BigInteger k = new BigInteger(qBitLength_ - 1,                                     secureRandom_);       while (k.compareTo(q_) >= 0)         {         k = new BigInteger(qBitLength_ - 1, secureRandom_);       }           /*        * Inverse of K        */       BigInteger kInv = k.modInverse(q_);           /*        * r =  (g ^ k mod p) mod q        */       BigInteger r = (g_.modPow(k, p_)).mod(q_);           /*        * s =  (k ^ -1(SHA(M) +xr)) mod q        */       BigInteger s =         kInv.multiply((m.add(x_.multiply(r)))).mod(q_);           /*        * If DEBUG, print the results        */       if (DEBUG)        {         System.out.println();         System.out.println("sign:r :" + r);         System.out.println();         System.out.println("sign:s :" + s);         System.out.println();         System.out.println("sign:m :" + m);       }           /*        * Put r and s in a buffer        * with some magic numbers        * to check for corruption        */       byte[] rdata = r.toByteArray();       byte[] sdata = s.toByteArray();       byte[] data  = new byte[6 + rdata.length + sdata.length];       int    i     = 0;           /*        * Put first magic number        */       data[i++] = 0x40;           /*        * Put in length        */       data[i++] = (byte) (data.length - 2);           /*        * Put in Separator        */       data[i++] = 0x02;           /*        * Put in r length        */       data[i++] = (byte) (rdata.length);           /*        * Put r in buffer        */       for (int j = 0; j < rdata.length; j++)        {         data[i++] = rdata[j];       }           /*        * Put in sepaerator        */       data[i++] = 0x02;           /*        * Put in s length        */       data[i++] = (byte) (sdata.length);           /*        * Put s in buffer        */       for (int j = 0; j < sdata.length; j++)        {         data[i++] = sdata[j];       }           return data;     }         /*      * Catches      */     catch (Exception ex)      {       ex.printStackTrace();     }         return null;   }       /**    * Method update    * Description : uppdate the hash    *    * @param b the bytes    * @param offset the offset    * @param len the length    *    * @throws SignatureException    *    */   public void update(byte[] b, int offset, int len)           throws SignatureException    {     md_.update(b, offset, len);     if (DEBUG)      {       System.out.println();       System.out.println("update:Length:" + len);     }   }       /**    * Method verify    * @param data    *    * @return    *    */   public boolean verify(byte[] data)    {     try      {       int i = 0;           /*        * Check for the first        * magic number,        * the length        * and separator        */       if ((data[i++] != 0x40)                (data[i++] != data.length - 2)                (data[i++] != 0x02))        {         throw new SignatureException("Corrupted signature data");       }           /*        * Check r length        */       byte len = data[i++];       if (len > 21)        {         throw new SignatureException("Corrupted signature data");       }           /*        * Get the r buffer        */       byte[] rdata = new byte[len];       for (int j = 0; j < len; j++)        {         rdata[j] = data[i++];       }           /*        * Check separator        */       if (data[i++] != 0x02)        {         throw new SignatureException("Corrupted signature data");       }           /*        * Check s length        */       len = data[i++];       if (len > 21)        {         throw new SignatureException("Corrupted signature data");       }           /*        * Get the s buffer        */       byte[] sdata = new byte[len];       for (int j = 0; j < len; j++)        {         sdata[j] = data[i++];       }           /*        * Get r and s from Buffer        */       BigInteger r = new BigInteger(rdata);       BigInteger s = new BigInteger(sdata);           /*        * reject the signature, if r or s >= q        */       if ((r.compareTo(q_) >= 0)  (s.compareTo(q_) >= 0))        {         return false;       }           /*        * Get the hash value,number        */       byte[]     digest = md_.digest();       BigInteger m      = new BigInteger(1, digest);           /*        * w = (S^-1) mod q        */       BigInteger w = s.modInverse(q_);           /*        * u1 = ((SHA(M)w) mod q        */       BigInteger u1 = m.multiply(w).mod(q_);           /*        * u2 = ((r)w) mod q        */       BigInteger u2 = r.multiply(w).mod(q_);           if (DEBUG)        {         System.out.println();         System.out.println("verify:r :" + r);         System.out.println();         System.out.println("verify:s :" + s);         System.out.println();         System.out.println("verify:m :" + m);         System.out.println();         System.out.println("verify:w :" + w);         System.out.println();         System.out.println("verify:u1 :" + u1);         System.out.println();         System.out.println("verify:u2 :" + u2);         System.out.println();         System.out.println("verify:g :" + g_);         System.out.println();         System.out.println("verify:p :" + p_);         System.out.println();         System.out.println("verify:q :" + q_);         System.out.println();         System.out.println("verify:y :" + y_);       }           BigInteger gu1 = g_.modPow(u1, p_);       BigInteger yu2 = y_.modPow(u2, p_);           /*        * v = (((g)^ul (y) ^u2) mod p) mod q        */       BigInteger v = gu1.multiply(yu2).mod(p_).mod(q_);       return v.equals(r);     }         /*      * Catches      */     catch (Exception ex)      {       ex.printStackTrace();     }         return false;   }       /**    * Method main    * Description: This is a test driver    *    *    * @param args none    *    */   public static void main(String[] args)    {     try      {       String localDirectory  = System.getProperty("user.dir");       System.out.println("Changing directory to Chapter 11");        System.setProperty("user.dir",localDirectory +  "\com\richware\chap11\");       localDirectory  = System.getProperty("user.dir");       String localFile  = localDirectory + args[0];       System.out.println("Openining Chapter 11 plus the file as an  argument: " + localFile);        System.out.println("Initializing Keys... This could take several  minutes....");            /*        * Start the homegrown signer        * it automatically        * generates the keys        */       RichDSA dsa = new RichDSA();       /*        * Start the JDK version        */           /*        * Generate the Keypair        * get the private Key        * get the public Key        */       KeyPairGenerator kpg =         KeyPairGenerator.getInstance("DSA");       SecureRandom     r   = new SecureRandom();       kpg.initialize(1024, r);        KeyPair    kp      = kpg.genKeyPair();       PrivateKey privKey = kp.getPrivate();       PublicKey  pubKey  = kp.getPublic();           /*        * Construct a sign        * and verify Signature        * If the same Signature        * class is used for both        * sign and verify in the same context        * might cause some problems        */       Signature dsaSign   =         Signature.getInstance("SHA1withDSA", "SUN");       Signature dsaVerify =         Signature.getInstance("SHA1withDSA", "SUN");           /*        * Init a sign        * with private Key        * and verify with        * a public Key        */       dsaSign.initSign(privKey);       dsaVerify.initVerify(pubKey);           /*        * Open a File        * and read the text        */       File                inputTextFile =         new File(localFile);       FileInputStream     fis           =         new FileInputStream(inputTextFile);       BufferedInputStream bis           =         new BufferedInputStream(fis);        byte[]              buff          =         new byte[(int) inputTextFile.length()];       int                 len;           /*        * Loop through the File        * pass the date through        * update method for        * hashing        */       while (bis.available() != 0)        {         len = bis.read(buff);             dsa.update(buff, 0, len);         dsaSign.update(buff, 0, len);       }           /*        * Close the file        */       bis.close();       fis.close();           /*        * Get the signatures        * the signature and public Key bytes        * are normally written to file        */       byte[] text_signature = dsa.sign();       byte[] jdk_signature  = dsaSign.sign();           /*        * Open a File        * and read the text        */       inputTextFile = new File(localFile);       fis           = new FileInputStream(inputTextFile);       bis           = new BufferedInputStream(fis);       buff          = new byte[(int) inputTextFile.length()];           /*        * Loop through the File        * pass the date through        * update method for        * hashing        */       while (bis.available() != 0)        {         len = bis.read(buff);             dsa.update(buff, 0, len);         dsaVerify.update(buff, 0, len);       }           /*        * Verify with hash        * public key and        * signatures        */       boolean verifies     = dsa.verify(text_signature);       boolean jdk_verifies = dsaVerify.verify(jdk_signature);           System.out.println("RichDSA Verify : " + verifies);       System.out.println("JDK Verify     : " + jdk_verifies);           /*        * Close the file        */       bis.close();       fis.close();     }         /*      * Catches      */     catch (Exception ex)      {       ex.printStackTrace();     }   } } 
end example
 

If I run Listing 11-1 with the following input string:

 testing the Signature 

The output looks like the following string:

 q**M  fN%E 
  


Java Security Solutions
Java Security Solutions
ISBN: 0764549286
EAN: 2147483647
Year: 2001
Pages: 222

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