blob: 26480b65a4d4b6ad53960dce1e76e13f5d7674d1 [file] [log] [blame]
/**
* @license
* Copyright 2016 Google Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.security.wycheproof;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import junit.framework.TestCase;
/**
* Tests RSA keys. Signatures and encryption are tested in different tests.
*
* @author bleichen@google.com (Daniel Bleichenbacher)
*/
// TODO(bleichen):
// - Add checks for bad random numbers
// - expect keys with e=1 to be rejected
// - expect keys with e=0 to be rejected
// - document stuff
// - Maybe also check encodings of private keys.
// - Test multi prime RSA
// - Tests for alternative representations:
// many libraries sort the primes as: p > q (but not all)
// some libraries compute d mod lambda(n)
// paramaters p,q,... are not really required
// - checks for bad random number generation
public class RsaKeyTest extends TestCase {
public static final String ENCODED_PUBLIC_KEY =
"30819f300d06092a864886f70d010101050003818d0030818902818100ab9014"
+ "dc47d44b6d260fc1fef9ab022042fd9566e9d7b60c54100cb6e1d4edc9859046"
+ "7d0502c17fce69d00ac5efb40b2cb167d8a44ab93d73c4d0f109fb5a26c2f882"
+ "3236ff517cf84412e173679cfae42e043b6fec81f9d984b562517e6febe1f722"
+ "95dbc3fdfc19d3240aa75515563f31dad83563f3a315acf9a0b351a23f020301"
+ "0001";
private void checkPrivateCrtKey(RSAPrivateCrtKey key, int expectedKeySize) throws Exception {
BigInteger p = key.getPrimeP();
BigInteger q = key.getPrimeQ();
BigInteger n = key.getModulus();
BigInteger e = key.getPublicExponent();
BigInteger d = key.getPrivateExponent();
BigInteger dp = key.getPrimeExponentP();
BigInteger dq = key.getPrimeExponentQ();
BigInteger crtCoeff = key.getCrtCoefficient();
// Simple test that (n,d,e) is a valid RSA key.
assertEquals(n, p.multiply(q));
assertEquals(expectedKeySize, n.bitLength());
int certainty = 80;
assertTrue(p.isProbablePrime(certainty));
assertTrue(q.isProbablePrime(certainty));
// Very simple checks for weak random number generators.
RandomUtil.checkPrime(p);
RandomUtil.checkPrime(q);
assertTrue(d.bitLength() > expectedKeySize / 2);
// TODO(bleichen): Keys that are very imbalanced can be broken with elliptic curve factoring.
// Add other checks. E.g. for the size of dp and dq
assertTrue(p.bitLength() > 256);
assertTrue(q.bitLength() > 256);
BigInteger p1 = p.subtract(BigInteger.ONE);
BigInteger q1 = q.subtract(BigInteger.ONE);
BigInteger phi = p1.multiply(q1);
BigInteger order = phi.divide(p1.gcd(q1)); // maximal order of elements
assertEquals(BigInteger.ONE, d.multiply(e).mod(order));
assertEquals(d.mod(p1), dp.mod(p1));
assertEquals(d.mod(q1), dq.mod(q1));
assertEquals(q.multiply(crtCoeff).mod(p), BigInteger.ONE);
}
private void checkPublicKey(RSAPublicKey pub, RSAPrivateKey priv) {
assertEquals(pub.getModulus(), priv.getModulus());
BigInteger e = pub.getPublicExponent();
// Checks that e > 1. [CVE-1999-1444]
assertEquals(e.compareTo(BigInteger.ONE), 1);
}
private void checkKeyPair(KeyPair keypair, int keySizeInBits) throws Exception {
RSAPublicKey pub = (RSAPublicKey) keypair.getPublic();
RSAPrivateKey priv = (RSAPrivateKey) keypair.getPrivate();
if (priv instanceof RSAPrivateCrtKey) {
checkPrivateCrtKey((RSAPrivateCrtKey) priv, keySizeInBits);
} else {
// Using a CRT key leads to 6-7 times better performance than not using the CRT.
// Such a perfomance loss makes a library almost useless. Thus we consider this
// a bug.
fail("Expecting an RSAPrivateCrtKey instead of " + priv.getClass().getName());
}
checkPublicKey(pub, priv);
}
public void testKeyGenerationSize(int keySizeInBits) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(keySizeInBits);
KeyPair keypair = keyGen.genKeyPair();
checkKeyPair(keypair, keySizeInBits);
}
public void testKeyGeneration() throws Exception {
testKeyGenerationSize(1024);
testKeyGenerationSize(2048);
}
/**
* Checks whether decoding and again encoding an RSA public key results
* in the same encoding.
* This is a regression test. Failing this test implies that the encoding has changed.
* Such a failure does not need to be a bug, since several encoding for the same key are
* possible.
*/
public void testEncodeDecodePublic() throws Exception {
KeyFactory kf = KeyFactory.getInstance("RSA");
byte[] encoded = TestUtil.hexToBytes(ENCODED_PUBLIC_KEY);
X509EncodedKeySpec spec = new X509EncodedKeySpec(encoded);
RSAPublicKey pub = (RSAPublicKey) kf.generatePublic(spec);
assertEquals("The test assumes that the public key is in X.509 format",
"X.509", pub.getFormat());
assertEquals(ENCODED_PUBLIC_KEY, TestUtil.bytesToHex(pub.getEncoded()));
}
}