/*
         * (non-Javadoc)
         *
         * @see org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator#generateKeyPair()
         */
        public IAsymmetricCipherKeyPair GenerateKeyPair()
        {
            int           strength  = param.Strength;
            ISecureRandom rand      = param.Random;
            int           certainty = param.Certainty;
            bool          debug     = param.IsDebug;

#if !NETFX_CORE
            if (debug)
            {
                Console.WriteLine("Fetching first " + param.CountSmallPrimes + " primes.");
            }
#endif

            IList smallPrimes = findFirstPrimes(param.CountSmallPrimes);

            smallPrimes = PermuteList(smallPrimes, rand);

            IBigInteger u = BigInteger.One;
            IBigInteger v = BigInteger.One;

            for (int i = 0; i < smallPrimes.Count / 2; i++)
            {
                u = u.Multiply((BigInteger)smallPrimes[i]);
            }
            for (int i = smallPrimes.Count / 2; i < smallPrimes.Count; i++)
            {
                v = v.Multiply((BigInteger)smallPrimes[i]);
            }

            IBigInteger sigma = u.Multiply(v);

            // n = (2 a u _p + 1 ) ( 2 b v _q + 1)
            // -> |n| = strength
            // |2| = 1 in bits
            // -> |a| * |b| = |n| - |u| - |v| - |_p| - |_q| - |2| -|2|
            // remainingStrength = strength - sigma.bitLength() - _p.bitLength() -
            // _q.bitLength() - 1 -1
            int         remainingStrength = strength - sigma.BitLength - 48;
            IBigInteger a = GeneratePrime(remainingStrength / 2 + 1, certainty, rand);
            IBigInteger b = GeneratePrime(remainingStrength / 2 + 1, certainty, rand);

            IBigInteger _p;
            IBigInteger _q;
            IBigInteger p;
            IBigInteger q;

            long tries = 0;
#if !NETFX_CORE
            if (debug)
            {
                Console.WriteLine("generating p and q");
            }
#endif

            IBigInteger _2au = a.Multiply(u).ShiftLeft(1);
            IBigInteger _2bv = b.Multiply(v).ShiftLeft(1);

            for (; ;)
            {
                tries++;

                _p = GeneratePrime(24, certainty, rand);

                p = _p.Multiply(_2au).Add(BigInteger.One);

                if (!p.IsProbablePrime(certainty))
                {
                    continue;
                }

                for (; ;)
                {
                    _q = GeneratePrime(24, certainty, rand);

                    if (_p.Equals(_q))
                    {
                        continue;
                    }

                    q = _q.Multiply(_2bv).Add(BigInteger.One);

                    if (q.IsProbablePrime(certainty))
                    {
                        break;
                    }
                }

                if (!sigma.Gcd(_p.Multiply(_q)).Equals(BigInteger.One))
                {
#if !NETFX_CORE
                    Console.WriteLine("sigma.gcd(_p.mult(_q)) != 1!\n _p: " + _p + "\n _q: " + _q);
#endif
                    continue;
                }

                if (p.Multiply(q).BitLength < strength)
                {
#if !NETFX_CORE
                    if (debug)
                    {
                        Console.WriteLine("key size too small. Should be " + strength + " but is actually "
                                          + p.Multiply(q).BitLength);
                    }
#endif
                    continue;
                }
                break;
            }

#if !NETFX_CORE
            if (debug)
            {
                Console.WriteLine("needed " + tries + " tries to generate p and q.");
            }
#endif

            IBigInteger n     = p.Multiply(q);
            IBigInteger phi_n = p.Subtract(BigInteger.One).Multiply(q.Subtract(BigInteger.One));
            IBigInteger g;
            tries = 0;
#if !NETFX_CORE
            if (debug)
            {
                Console.WriteLine("generating g");
            }
#endif
            for (; ;)
            {
                // TODO After the first loop, just regenerate one randomly-selected gPart each time?
                IList gParts = Platform.CreateArrayList();
                for (int ind = 0; ind != smallPrimes.Count; ind++)
                {
                    IBigInteger i = (BigInteger)smallPrimes[ind];
                    IBigInteger e = phi_n.Divide(i);

                    for (; ;)
                    {
                        tries++;

                        g = GeneratePrime(strength, certainty, rand);

                        if (!g.ModPow(e, n).Equals(BigInteger.One))
                        {
                            gParts.Add(g);
                            break;
                        }
                    }
                }
                g = BigInteger.One;
                for (int i = 0; i < smallPrimes.Count; i++)
                {
                    IBigInteger gPart      = (BigInteger)gParts[i];
                    IBigInteger smallPrime = (BigInteger)smallPrimes[i];
                    g = g.Multiply(gPart.ModPow(sigma.Divide(smallPrime), n)).Mod(n);
                }

                // make sure that g is not divisible by p_i or q_i
                bool divisible = false;
                for (int i = 0; i < smallPrimes.Count; i++)
                {
                    if (g.ModPow(phi_n.Divide((BigInteger)smallPrimes[i]), n).Equals(BigInteger.One))
                    {
#if !NETFX_CORE
                        if (debug)
                        {
                            Console.WriteLine("g has order phi(n)/" + smallPrimes[i] + "\n g: " + g);
                        }
#endif
                        divisible = true;
                        break;
                    }
                }

                if (divisible)
                {
                    continue;
                }

                // make sure that g has order > phi_n/4

                //if (g.ModPow(phi_n.Divide(BigInteger.ValueOf(4)), n).Equals(BigInteger.One))
                if (g.ModPow(phi_n.ShiftRight(2), n).Equals(BigInteger.One))
                {
#if !NETFX_CORE
                    if (debug)
                    {
                        Console.WriteLine("g has order phi(n)/4\n g:" + g);
                    }
#endif
                    continue;
                }

                if (g.ModPow(phi_n.Divide(_p), n).Equals(BigInteger.One))
                {
#if !NETFX_CORE
                    if (debug)
                    {
                        Console.WriteLine("g has order phi(n)/p'\n g: " + g);
                    }
#endif
                    continue;
                }
                if (g.ModPow(phi_n.Divide(_q), n).Equals(BigInteger.One))
                {
#if !NETFX_CORE
                    if (debug)
                    {
                        Console.WriteLine("g has order phi(n)/q'\n g: " + g);
                    }
#endif
                    continue;
                }
                if (g.ModPow(phi_n.Divide(a), n).Equals(BigInteger.One))
                {
#if !NETFX_CORE
                    if (debug)
                    {
                        Console.WriteLine("g has order phi(n)/a\n g: " + g);
                    }
#endif
                    continue;
                }
                if (g.ModPow(phi_n.Divide(b), n).Equals(BigInteger.One))
                {
#if !NETFX_CORE
                    if (debug)
                    {
                        Console.WriteLine("g has order phi(n)/b\n g: " + g);
                    }
#endif
                    continue;
                }
                break;
            }
#if !NETFX_CORE
            if (debug)
            {
                Console.WriteLine("needed " + tries + " tries to generate g");
                Console.WriteLine();
                Console.WriteLine("found new NaccacheStern cipher variables:");
                Console.WriteLine("smallPrimes: " + CollectionUtilities.ToString(smallPrimes));
                Console.WriteLine("sigma:...... " + sigma + " (" + sigma.BitLength + " bits)");
                Console.WriteLine("a:.......... " + a);
                Console.WriteLine("b:.......... " + b);
                Console.WriteLine("p':......... " + _p);
                Console.WriteLine("q':......... " + _q);
                Console.WriteLine("p:.......... " + p);
                Console.WriteLine("q:.......... " + q);
                Console.WriteLine("n:.......... " + n);
                Console.WriteLine("phi(n):..... " + phi_n);
                Console.WriteLine("g:.......... " + g);
                Console.WriteLine();
            }
#endif

            return(new AsymmetricCipherKeyPair(new NaccacheSternKeyParameters(false, g, n, sigma.BitLength),
                                               new NaccacheSternPrivateKeyParameters(g, n, sigma.BitLength, smallPrimes, phi_n)));
        }