org.bouncycastle.crypto.generators.NaccacheSternKeyPairGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcprov-ext-debug-jdk18on Show documentation
Show all versions of bcprov-ext-debug-jdk18on Show documentation
The Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains JCE provider and lightweight API for the Bouncy Castle Cryptography APIs for Java 1.8 and later with debug enabled.
The newest version!
package org.bouncycastle.crypto.generators;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Vector;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
import org.bouncycastle.crypto.CryptoServicePurpose;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.KeyGenerationParameters;
import org.bouncycastle.crypto.constraints.ConstraintUtils;
import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
import org.bouncycastle.crypto.params.NaccacheSternKeyGenerationParameters;
import org.bouncycastle.crypto.params.NaccacheSternKeyParameters;
import org.bouncycastle.crypto.params.NaccacheSternPrivateKeyParameters;
import org.bouncycastle.util.BigIntegers;
/**
* Key generation parameters for NaccacheStern cipher. For details on this cipher, please see
*
* https://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
*/
public class NaccacheSternKeyPairGenerator
implements AsymmetricCipherKeyPairGenerator
{
private static int[] smallPrimes =
{
3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149,
151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233,
239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331,
337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431,
433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523,
541, 547, 557
};
private NaccacheSternKeyGenerationParameters param;
private static final BigInteger ONE = BigInteger.valueOf(1); // JDK 1.1 compatibility
/*
* (non-Javadoc)
*
* @see org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator#init(org.bouncycastle.crypto.KeyGenerationParameters)
*/
public void init(KeyGenerationParameters param)
{
this.param = (NaccacheSternKeyGenerationParameters)param;
CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(
"NaccacheStern KeyGen", ConstraintUtils.bitsOfSecurityForFF(param.getStrength()), param, CryptoServicePurpose.KEYGEN));
}
/*
* (non-Javadoc)
*
* @see org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator#generateKeyPair()
*/
public AsymmetricCipherKeyPair generateKeyPair()
{
int strength = param.getStrength();
SecureRandom rand = param.getRandom();
int certainty = param.getCertainty();
boolean debug = param.isDebug();
if (debug)
{
// -DM System.out.println
System.out.println("Fetching first " + param.getCntSmallPrimes() + " primes.");
}
Vector smallPrimes = findFirstPrimes(param.getCntSmallPrimes());
smallPrimes = permuteList(smallPrimes, rand);
BigInteger u = ONE;
BigInteger v = ONE;
for (int i = 0; i < smallPrimes.size() / 2; i++)
{
u = u.multiply((BigInteger)smallPrimes.elementAt(i));
}
for (int i = smallPrimes.size() / 2; i < smallPrimes.size(); i++)
{
v = v.multiply((BigInteger)smallPrimes.elementAt(i));
}
BigInteger 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;
BigInteger a = generatePrime(remainingStrength / 2 + 1, certainty, rand);
BigInteger b = generatePrime(remainingStrength / 2 + 1, certainty, rand);
BigInteger p_;
BigInteger q_;
BigInteger p;
BigInteger q;
long tries = 0;
if (debug)
{
// -DM System.out.println
System.out.println("generating p and q");
}
BigInteger _2au = a.multiply(u).shiftLeft(1);
BigInteger _2bv = b.multiply(v).shiftLeft(1);
for (;;)
{
tries++;
p_ = generatePrime(24, certainty, rand);
p = p_.multiply(_2au).add(ONE);
if (!p.isProbablePrime(certainty))
{
continue;
}
for (;;)
{
q_ = generatePrime(24, certainty, rand);
if (p_.equals(q_))
{
continue;
}
q = q_.multiply(_2bv).add(ONE);
if (q.isProbablePrime(certainty))
{
break;
}
}
if (!sigma.gcd(p_.multiply(q_)).equals(ONE))
{
// System.out.println("sigma.gcd(p_.mult(q_)) != 1!\n p_: " + p_
// +"\n q_: "+ q_ );
continue;
}
if (p.multiply(q).bitLength() < strength)
{
if (debug)
{
// -DM System.out.println
System.out.println("key size too small. Should be " + strength + " but is actually "
+ p.multiply(q).bitLength());
}
continue;
}
break;
}
if (debug)
{
// -DM System.out.println
System.out.println("needed " + tries + " tries to generate p and q.");
}
BigInteger n = p.multiply(q);
BigInteger phi_n = p.subtract(ONE).multiply(q.subtract(ONE));
BigInteger g;
tries = 0;
if (debug)
{
// -DM System.out.println
System.out.println("generating g");
}
for (;;)
{
Vector gParts = new Vector();
for (int ind = 0; ind != smallPrimes.size(); ind++)
{
BigInteger i = (BigInteger)smallPrimes.elementAt(ind);
BigInteger e = phi_n.divide(i);
for (;;)
{
tries++;
g = BigIntegers.createRandomPrime(strength, certainty, rand);
if (g.modPow(e, n).equals(ONE))
{
continue;
}
gParts.addElement(g);
break;
}
}
g = ONE;
for (int i = 0; i < smallPrimes.size(); i++)
{
g = g.multiply(((BigInteger)gParts.elementAt(i)).modPow(sigma.divide((BigInteger)smallPrimes.elementAt(i)), n)).mod(n);
}
// make sure that g is not divisible by p_i or q_i
boolean divisible = false;
for (int i = 0; i < smallPrimes.size(); i++)
{
if (g.modPow(phi_n.divide((BigInteger)smallPrimes.elementAt(i)), n).equals(ONE))
{
if (debug)
{
// -DM System.out.println
System.out.println("g has order phi(n)/" + smallPrimes.elementAt(i) + "\n g: " + g);
}
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(ONE))
{
if (debug)
{
// -DM System.out.println
System.out.println("g has order phi(n)/4\n g:" + g);
}
continue;
}
if (g.modPow(phi_n.divide(p_), n).equals(ONE))
{
if (debug)
{
// -DM System.out.print
System.out.println("g has order phi(n)/p'\n g: " + g);
}
continue;
}
if (g.modPow(phi_n.divide(q_), n).equals(ONE))
{
if (debug)
{
// -DM System.out.print
System.out.println("g has order phi(n)/q'\n g: " + g);
}
continue;
}
if (g.modPow(phi_n.divide(a), n).equals(ONE))
{
if (debug)
{
// -DM System.out.print
System.out.println("g has order phi(n)/a\n g: " + g);
}
continue;
}
if (g.modPow(phi_n.divide(b), n).equals(ONE))
{
if (debug)
{
// -DM System.out.print
System.out.println("g has order phi(n)/b\n g: " + g);
}
continue;
}
break;
}
// -DM 15 System.out.print
if (debug)
{
System.out.println("needed " + tries + " tries to generate g");
System.out.println();
System.out.println("found new NaccacheStern cipher variables:");
System.out.println("smallPrimes: " + smallPrimes);
System.out.println("sigma:...... " + sigma + " (" + sigma.bitLength() + " bits)");
System.out.println("a:.......... " + a);
System.out.println("b:.......... " + b);
System.out.println("p':......... " + p_);
System.out.println("q':......... " + q_);
System.out.println("p:.......... " + p);
System.out.println("q:.......... " + q);
System.out.println("n:.......... " + n);
System.out.println("phi(n):..... " + phi_n);
System.out.println("g:.......... " + g);
System.out.println();
}
return new AsymmetricCipherKeyPair(new NaccacheSternKeyParameters(false, g, n, sigma.bitLength()),
new NaccacheSternPrivateKeyParameters(g, n, sigma.bitLength(), smallPrimes, phi_n));
}
private static BigInteger generatePrime(
int bitLength,
int certainty,
SecureRandom rand)
{
BigInteger p_ = BigIntegers.createRandomPrime(bitLength, certainty, rand);
while (p_.bitLength() != bitLength)
{
p_ = BigIntegers.createRandomPrime(bitLength, certainty, rand);
}
return p_;
}
/**
* Generates a permuted ArrayList from the original one. The original List
* is not modified
*
* @param arr
* the ArrayList to be permuted
* @param rand
* the source of Randomness for permutation
* @return a new ArrayList with the permuted elements.
*/
private static Vector permuteList(
Vector arr,
SecureRandom rand)
{
Vector retval = new Vector();
Vector tmp = new Vector();
for (int i = 0; i < arr.size(); i++)
{
tmp.addElement(arr.elementAt(i));
}
retval.addElement(tmp.elementAt(0));
tmp.removeElementAt(0);
while (tmp.size() != 0)
{
retval.insertElementAt(tmp.elementAt(0), getInt(rand, retval.size() + 1));
tmp.removeElementAt(0);
}
return retval;
}
private static int getInt(
SecureRandom rand,
int n)
{
if ((n & -n) == n)
{
return (int)((n * (long)(rand.nextInt() & 0x7fffffff)) >> 31);
}
int bits, val;
do
{
bits = rand.nextInt() & 0x7fffffff;
val = bits % n;
}
while (bits - val + (n-1) < 0);
return val;
}
/**
* Finds the first 'count' primes starting with 3
*
* @param count
* the number of primes to find
* @return a vector containing the found primes as Integer
*/
private static Vector findFirstPrimes(
int count)
{
Vector primes = new Vector(count);
for (int i = 0; i != count; i++)
{
primes.addElement(BigInteger.valueOf(smallPrimes[i]));
}
return primes;
}
}