Source for gnu.java.security.key.rsa.RSAKeyPairGenerator

   1: /* RSAKeyPairGenerator.java --
   2:    Copyright 2001, 2002, 2003, 2006, 2010 Free Software Foundation, Inc.
   3: 
   4: This file is a part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2 of the License, or (at
   9: your option) any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; if not, write to the Free Software
  18: Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  19: USA
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version.  */
  37: 
  38: 
  39: package gnu.java.security.key.rsa;
  40: 
  41: import gnu.java.security.Configuration;
  42: import gnu.java.security.Registry;
  43: import gnu.java.security.key.IKeyPairGenerator;
  44: import gnu.java.security.util.PRNG;
  45: 
  46: import java.math.BigInteger;
  47: import java.security.KeyPair;
  48: import java.security.PrivateKey;
  49: import java.security.PublicKey;
  50: import java.security.SecureRandom;
  51: import java.security.spec.RSAKeyGenParameterSpec;
  52: import java.util.Map;
  53: import java.util.logging.Logger;
  54: 
  55: /**
  56:  * A key-pair generator for asymetric keys to use in conjunction with the RSA
  57:  * scheme.
  58:  * <p>
  59:  * Reference:
  60:  * <ol>
  61:  * <li><a
  62:  * href="http://www.cosic.esat.kuleuven.ac.be/nessie/workshop/submissions/rsa-pss.zip">
  63:  * RSA-PSS Signature Scheme with Appendix</a>, part B. Primitive specification
  64:  * and supporting documentation. Jakob Jonsson and Burt Kaliski. </li>
  65:  * <li><a href="http://www.cacr.math.uwaterloo.ca/hac/">Handbook of Applied
  66:  * Cryptography</a>, Alfred J. Menezes, Paul C. van Oorschot and Scott A.
  67:  * Vanstone. Section 11.3 RSA and related signature schemes.</li>
  68:  * </ol>
  69:  */
  70: public class RSAKeyPairGenerator
  71:     implements IKeyPairGenerator
  72: {
  73:   private static final Logger log = Configuration.DEBUG ?
  74:                 Logger.getLogger(RSAKeyPairGenerator.class.getName()) : null;
  75: 
  76:   /** The BigInteger constant 1. */
  77:   private static final BigInteger ONE = BigInteger.ONE;
  78: 
  79:   /** The BigInteger constant 2. */
  80:   private static final BigInteger TWO = BigInteger.valueOf(2L);
  81: 
  82:   /** Property name of the length (Integer) of the modulus of an RSA key. */
  83:   public static final String MODULUS_LENGTH = "gnu.crypto.rsa.L";
  84: 
  85:   /**
  86:    * Property name of an optional {@link SecureRandom} instance to use. The
  87:    * default is to use a classloader singleton from {@link PRNG}.
  88:    */
  89:   public static final String SOURCE_OF_RANDOMNESS = "gnu.crypto.rsa.prng";
  90: 
  91:   /**
  92:    * Property name of an optional {@link RSAKeyGenParameterSpec} instance to use
  93:    * for this generator's <code>n</code>, and <code>e</code> values. The
  94:    * default is to generate <code>n</code> and use a fixed value for
  95:    * <code>e</.code> (Fermat's F4 number).
  96:    */
  97:   public static final String RSA_PARAMETERS = "gnu.crypto.rsa.params";
  98: 
  99:   /**
 100:    * Property name of the preferred encoding format to use when externalizing
 101:    * generated instance of key-pairs from this generator. The property is taken
 102:    * to be an {@link Integer} that encapsulates an encoding format identifier.
 103:    */
 104:   public static final String PREFERRED_ENCODING_FORMAT = "gnu.crypto.rsa.encoding";
 105: 
 106:   /** Default value for the modulus length. */
 107:   private static final int DEFAULT_MODULUS_LENGTH = 1024;
 108: 
 109:   /** Default encoding format to use when none was specified. */
 110:   private static final int DEFAULT_ENCODING_FORMAT = Registry.RAW_ENCODING_ID;
 111: 
 112:   /** The desired bit length of the modulus. */
 113:   private int L;
 114: 
 115:   /**
 116:    * This implementation uses, by default, Fermat's F4 number as the public
 117:    * exponent.
 118:    */
 119:   private BigInteger e = BigInteger.valueOf(65537L);
 120: 
 121:   /** The optional {@link SecureRandom} instance to use. */
 122:   private SecureRandom rnd = null;
 123: 
 124:   /** Our default source of randomness. */
 125:   private PRNG prng = null;
 126: 
 127:   /** Preferred encoding format of generated keys. */
 128:   private int preferredFormat;
 129: 
 130:   // implicit 0-arguments constructor
 131: 
 132:   public String name()
 133:   {
 134:     return Registry.RSA_KPG;
 135:   }
 136: 
 137:   /**
 138:    * Configures this instance.
 139:    *
 140:    * @param attributes the map of name/value pairs to use.
 141:    * @exception IllegalArgumentException if the designated MODULUS_LENGTH value
 142:    *              is less than 1024.
 143:    */
 144:   public void setup(Map attributes)
 145:   {
 146:     if (Configuration.DEBUG)
 147:       log.entering(this.getClass().getName(), "setup", attributes);
 148:     // do we have a SecureRandom, or should we use our own?
 149:     rnd = (SecureRandom) attributes.get(SOURCE_OF_RANDOMNESS);
 150:     // are we given a set of RSA params or we shall use our own?
 151:     RSAKeyGenParameterSpec params = (RSAKeyGenParameterSpec) attributes.get(RSA_PARAMETERS);
 152:     // find out the modulus length
 153:     if (params != null)
 154:       {
 155:         L = params.getKeysize();
 156:         e = params.getPublicExponent();
 157:       }
 158:     else
 159:       {
 160:         Integer l = (Integer) attributes.get(MODULUS_LENGTH);
 161:         L = (l == null ? DEFAULT_MODULUS_LENGTH : l.intValue());
 162:       }
 163:     if (L < 1024)
 164:       throw new IllegalArgumentException(MODULUS_LENGTH);
 165: 
 166:     // what is the preferred encoding format
 167:     Integer formatID = (Integer) attributes.get(PREFERRED_ENCODING_FORMAT);
 168:     preferredFormat = formatID == null ? DEFAULT_ENCODING_FORMAT
 169:                                        : formatID.intValue();
 170:     if (Configuration.DEBUG)
 171:       log.exiting(this.getClass().getName(), "setup");
 172:   }
 173: 
 174:   /**
 175:    * <p>
 176:    * The algorithm used here is described in <i>nessie-pss-B.pdf</i> document
 177:    * which is part of the RSA-PSS submission to NESSIE.
 178:    * </p>
 179:    *
 180:    * @return an RSA keypair.
 181:    */
 182:   public KeyPair generate()
 183:   {
 184:     if (Configuration.DEBUG)
 185:       log.entering(this.getClass().getName(), "generate");
 186:     BigInteger p, q, n, d;
 187:     // 1. Generate a prime p in the interval [2**(M-1), 2**M - 1], where
 188:     // M = CEILING(L/2), and such that GCD(p, e) = 1
 189:     int M = (L + 1) / 2;
 190:     BigInteger lower = TWO.pow(M - 1);
 191:     BigInteger upper = TWO.pow(M).subtract(ONE);
 192:     byte[] kb = new byte[(M + 7) / 8]; // enough bytes to frame M bits
 193:     step1: while (true)
 194:       {
 195:         nextRandomBytes(kb);
 196:         p = new BigInteger(1, kb).setBit(0);
 197:         if (p.compareTo(lower) >= 0 && p.compareTo(upper) <= 0
 198:             && p.isProbablePrime(80) && p.gcd(e).equals(ONE))
 199:           break step1;
 200:       }
 201:     // 2. Generate a prime q such that the product of p and q is an L-bit
 202:     // number, and such that GCD(q, e) = 1
 203:     step2: while (true)
 204:       {
 205:         nextRandomBytes(kb);
 206:         q = new BigInteger(1, kb).setBit(0);
 207:         n = p.multiply(q);
 208:         if (n.bitLength() == L && q.isProbablePrime(80) && q.gcd(e).equals(ONE))
 209:           break step2;
 210:         // TODO: test for p != q
 211:       }
 212:     // TODO: ensure p < q
 213:     // 3. Put n = pq. The public key is (n, e).
 214:     // 4. Compute the parameters necessary for the private key K (see
 215:     // Section 2.2).
 216:     BigInteger phi = p.subtract(ONE).multiply(q.subtract(ONE));
 217:     d = e.modInverse(phi);
 218:     // 5. Output the public key and the private key.
 219:     PublicKey pubK = new GnuRSAPublicKey(preferredFormat, n, e);
 220:     PrivateKey secK = new GnuRSAPrivateKey(preferredFormat, p, q, e, d);
 221:     KeyPair result = new KeyPair(pubK, secK);
 222:     if (Configuration.DEBUG)
 223:       log.exiting(this.getClass().getName(), "generate", result);
 224:     return result;
 225:   }
 226: 
 227:   /**
 228:    * Fills the designated byte array with random data.
 229:    *
 230:    * @param buffer the byte array to fill with random data.
 231:    */
 232:   private void nextRandomBytes(byte[] buffer)
 233:   {
 234:     if (rnd != null)
 235:       rnd.nextBytes(buffer);
 236:     else
 237:       getDefaultPRNG().nextBytes(buffer);
 238:   }
 239: 
 240:   private PRNG getDefaultPRNG()
 241:   {
 242:     if (prng == null)
 243:       prng = PRNG.getInstance();
 244: 
 245:     return prng;
 246:   }
 247: }