public KeyPair generate() {
      BigInteger p, q, n, d;

      // 1. Generate a prime p in the interval [2**(M-1), 2**M - 1], where
      // M = CEILING(L/2), and such that GCD(p, e) = 1
      int M = (L+1)/2;
      BigInteger lower = TWO.pow(M-1);
      BigInteger upper = TWO.pow(M).subtract(ONE);
      byte[] kb = new byte[(M+7)/8]; // enough bytes to frame M bits
      step1: while (true) {
         nextRandomBytes(kb);
         p = new BigInteger(1, kb).setBit(0);
         if (     p.compareTo(lower) >= 0
               && p.compareTo(upper) <= 0
               && Prime.isProbablePrime(p)
               && p.gcd(e).equals(ONE)) {
            break step1;
         }
      }

      // 2. Generate a prime q such that the product of p and q is an L-bit
      // number, and such that GCD(q, e) = 1
      step2: while (true) {
         nextRandomBytes(kb);
         q = new BigInteger(1, kb).setBit(0);
         n = p.multiply(q);
         if (     n.bitLength() == L
               && Prime.isProbablePrime(q)
               && q.gcd(e).equals(ONE)) {
            break step2;
         }

         // TODO: test for p != q
      }

      // TODO: ensure p < q

      // 3. Put n = pq. The public key is (n, e).
      // 4. Compute the parameters necessary for the private key K (see
      // Section 2.2).
      BigInteger phi = p.subtract(ONE).multiply(q.subtract(ONE));
      d = e.modInverse(phi);

      // 5. Output the public key and the private key.
      PublicKey pubK = new GnuRSAPublicKey(n, e);
      PrivateKey secK = new GnuRSAPrivateKey(p, q, e, d);

      return new KeyPair(pubK, secK);
   }