Skip to content

Commit

Permalink
Check the RSA keysize in Signature
Browse files Browse the repository at this point in the history
At least 2048 bits of RSA key can be
used for Sign in Signature. However,
current openjceplusfips provider can
accept a RSA key which size is smaller
than 1024.

Add a check in the engineInitSign()
function to filter the keysize.

Signed-off-by: JinhangZhang <[email protected]>
  • Loading branch information
JinhangZhang authored and jasonkatonica committed Jun 10, 2024
1 parent 0d9bdaa commit fd04630
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 3 deletions.
39 changes: 39 additions & 0 deletions src/main/java/com/ibm/crypto/plus/provider/RSAKeyFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.List;

import com.ibm.crypto.plus.provider.RSAUtil.KeyType;

@SuppressWarnings({"removal", "deprecation"})
Expand All @@ -34,6 +37,8 @@ class RSAKeyFactory extends KeyFactorySpi {
public final static int MIN_MODLEN_FIPS = 2048;
public final static int MIN_MODLEN_FIPS_PUB = 1024; //FIPS currently allows signature verification on this size key
public final static int MAX_MODLEN = 16384;
public final static List<Integer> ALLOWABLE_MODLEN_FIPS_SIGN = Arrays.asList(2048, 3072, 4096);
public final static List<Integer> ALLOWABLE_MODLEN_FIPS_VERIFY = Arrays.asList(1024, 2048, 3072, 4096);

/*
* If the modulus length is above this value, restrict the size of the
Expand Down Expand Up @@ -124,6 +129,40 @@ static void checkKeyLengths(int modulusLen, BigInteger exponent, int minModulusL
}
}

/**
* Check the length of an RSA key modulus/exponent to make sure it is not
* too short or long. Some impls have their own min and max key sizes that
* may or may not match with a system defined value.
*
* @param modulusLen
* the bit length of the RSA modulus.
* @param exponent
* the RSA exponent
* @param minModulusLen
* if > 0, check to see if modulusLen is at least this long,
* otherwise unused.
* @param maxModulusLen
* caller will allow this max number of bits. Allow the smaller
* of the system-defined maximum and this param.
* @param specificModulesLen
* specific module length for sign/verify.
*
* @throws InvalidKeyException
* if any of the values are unacceptable.
*/
static void checkKeyLengths(int modulusLen, BigInteger exponent, int minModulusLen,
int maxModulusLen, List<Integer> specificModulesLen, String flag) throws InvalidKeyException {

checkKeyLengths(modulusLen, exponent, minModulusLen, maxModulusLen);
if ((specificModulesLen != null) && (!specificModulesLen.contains(modulusLen))) {
if (flag.equals("verify")) {
throw new InvalidKeyException("In FIPS mode, only 1024, 2048, 3072, or 4096 size of RSA key is accepted.");
} else if (flag.equals("sign")){
throw new InvalidKeyException("In FIPS mode, only 2048, 3072, or 4096 size of RSA key is accepted.");
}
}
}

/**
* For compatibility, we round up to the nearest byte here: some Key impls
* might pass in a value within a byte of the real value.
Expand Down
30 changes: 29 additions & 1 deletion src/main/java/com/ibm/crypto/plus/provider/RSASignature.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright IBM Corp. 2023
* Copyright IBM Corp. 2024
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
Expand All @@ -17,6 +17,9 @@
import java.security.SignatureException;
import java.security.SignatureSpi;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
import java.util.List;

import com.ibm.crypto.plus.provider.RSAUtil.KeyType;
import com.ibm.crypto.plus.provider.ock.Signature;

Expand Down Expand Up @@ -47,6 +50,18 @@ protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException
throw new InvalidKeyException("Key is not an RSAPublicKey");
}

List<Integer> specificModulesLen = null;
int minModulusLen = RSAKeyFactory.MIN_MODLEN_NONFIPS;
if (provider.isFIPS()) {
minModulusLen = RSAKeyFactory.MIN_MODLEN_FIPS_PUB;
specificModulesLen = RSAKeyFactory.ALLOWABLE_MODLEN_FIPS_VERIFY;
}
RSAKeyFactory.checkKeyLengths(((java.security.interfaces.RSAPublicKey) publicKey).getModulus().bitLength(),
RSAKeyGenParameterSpec.F4,
minModulusLen,
64 * 1024,
specificModulesLen,
"verify");

RSAPublicKey rsaPublic = (RSAPublicKey) RSAKeyFactory.toRSAKey(provider, publicKey);
try {
Expand Down Expand Up @@ -84,6 +99,19 @@ protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException
throw new InvalidKeyException("Key is not an RSAPrivateKey");
}

List<Integer> specificModulesLen = null;
int minModulusLen = RSAKeyFactory.MIN_MODLEN_NONFIPS;
if (provider.isFIPS()) {
minModulusLen = RSAKeyFactory.MIN_MODLEN_FIPS;
specificModulesLen = RSAKeyFactory.ALLOWABLE_MODLEN_FIPS_SIGN;
}
RSAKeyFactory.checkKeyLengths(((java.security.interfaces.RSAPrivateKey) privateKey).getModulus().bitLength(),
RSAKeyGenParameterSpec.F4,
minModulusLen,
64 * 1024,
specificModulesLen,
"sign");

//RSAPrivateCrtKey rsaPrivate = (RSAPrivateCrtKey) RSAKeyFactory.toRSAKey(provider, privateKey);
PrivateKey rsaPrivate = (PrivateKey) RSAKeyFactory.toRSAKey(provider, privateKey);

Expand Down
4 changes: 2 additions & 2 deletions src/test/java/ibm/jceplus/junit/openjceplusfips/TestAll.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright IBM Corp. 2023
* Copyright IBM Corp. 2024
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
Expand Down Expand Up @@ -39,7 +39,7 @@
TestSHA384.class, TestSHA512.class, TestRSAPSSInterop2.class, TestRSAPSSInterop3.class,
TestSHA3_224.class, TestSHA3_256.class, TestSHA3_384.class, TestSHA3_512.class,
TestRSAKeyInterop.class, TestRSAKeyInteropBC.class, TestRSAPSS2.class,
TestFIPSVerifyOnlyTest.class})
TestFIPSVerifyOnlyTest.class, TestRSASignatureWithSpecificSize.class})

public class TestAll {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* Copyright IBM Corp. 2024
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution.
*/
package ibm.jceplus.junit.openjceplusfips;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.InvalidKeyException;

import junit.framework.Test;
import junit.framework.TestSuite;

public class TestRSASignatureWithSpecificSize extends ibm.jceplus.junit.base.BaseTest {

//--------------------------------------------------------------------------
//
//
static final byte[] origMsg = "this is the original message to be signed I changed to a very long message to make sure enough bytes are there for copying."
.getBytes();

static {
Utils.loadProviderTestSuite();
}

//--------------------------------------------------------------------------
//
//
public TestRSASignatureWithSpecificSize() {
super(Utils.TEST_SUITE_PROVIDER_NAME);
}

// RSA signature sign allows at least 2048 bits of RSA key to be used for sign a signature.
public byte[] doSign(String sigAlgo, byte[] message, PrivateKey privateKey) throws Exception {
Signature sign = Signature.getInstance(sigAlgo, Utils.TEST_SUITE_PROVIDER_NAME);
try {
sign.initSign(privateKey);
} catch (InvalidKeyException ike) {
if (((java.security.interfaces.RSAPrivateKey) privateKey).getModulus().bitLength() < 2048 ) {
if ("RSA keys must be at least 2048 bits long".equals(ike.getMessage())) {
System.out.println("Expected exception msg: <RSA keys must be at least 2048 bits long> is caught for sign.");
return null;
}
} else {
if ("In FIPS mode, only 2048, 3072, or 4096 size of RSA key is accepted.".equals(ike.getMessage())) {
System.out.println("Expected exception msg: <In FIPS mode, only 2048, 3072, or 4096 size of RSA key is accepted.> is caught for sign.");
return null;
}
}
throw ike;
}
sign.update(message);
byte[] signedBytes = sign.sign();
return signedBytes;
}

// RSA signature verify allows at least 2048 bits of RSA key to be used for sign a signature.
public void doVerify(String sigAlgo, byte[] message, PublicKey publicKey,
byte[] signedBytes) throws Exception {
Signature verify = Signature.getInstance(sigAlgo, Utils.TEST_SUITE_PROVIDER_NAME);
try {
verify.initVerify(publicKey);
} catch (InvalidKeyException ike) {
if (((java.security.interfaces.RSAPublicKey) publicKey).getModulus().bitLength() < 1024 ) {
if ("RSA keys must be at least 1024 bits long".equals(ike.getMessage())) {
System.out.println("Expected exception msg: <RSA keys must be at least 1024 bits long> is caught for verify.");
return;
}
} else {
if ("In FIPS mode, only 1024, 2048, 3072, or 4096 size of RSA key is accepted.".equals(ike.getMessage())) {
System.out.println("Expected exception msg: <In FIPS mode, only 1024, 2048, 3072, or 4096 size of RSA key is accepted.> is caught for verify.");
return;
}
}
throw ike;
}
verify.update(message);
if (signedBytes != null) {
assertTrue("Signature verification failed", verify.verify(signedBytes));
} else {
assertFalse("Signature verification failed", verify.verify(signedBytes));
}
}

// Use a non FIPS provider to get a 1024 bits of RSA key.
public KeyPair generateKeyPair(int keysize) throws Exception {
KeyPairGenerator rsaKeyPairGen = KeyPairGenerator.getInstance("RSA", Utils.PROVIDER_SunRsaSign);
rsaKeyPairGen.initialize(keysize);
return rsaKeyPairGen.generateKeyPair();
}

public void testSHA256withRSA_1024() throws Exception {
KeyPair keyPair = generateKeyPair(1024);
System.out.println("Keysize is 1024");
byte[] signedBytes = doSign("SHA256withRSA", origMsg, keyPair.getPrivate());
doVerify("SHA256withRSA", origMsg, keyPair.getPublic(), signedBytes);
}

public void testSHA256withRSA_2048() throws Exception {
KeyPair keyPair = generateKeyPair(2048);
System.out.println("Keysize is 2048");
byte[] signedBytes = doSign("SHA256withRSA", origMsg, keyPair.getPrivate());
doVerify("SHA256withRSA", origMsg, keyPair.getPublic(), signedBytes);
}

public void testSHA256withRSA_3072() throws Exception {
KeyPair keyPair = generateKeyPair(3072);
System.out.println("Keysize is 3072");
byte[] signedBytes = doSign("SHA256withRSA", origMsg, keyPair.getPrivate());
doVerify("SHA256withRSA", origMsg, keyPair.getPublic(), signedBytes);
}

public void testSHA256withRSA_4096() throws Exception {
KeyPair keyPair = generateKeyPair(4096);
System.out.println("Keysize is 4096");
byte[] signedBytes = doSign("SHA256withRSA", origMsg, keyPair.getPrivate());
doVerify("SHA256withRSA", origMsg, keyPair.getPublic(), signedBytes);
}

// check large size
public void testSHA256withRSA_5120() throws Exception {
KeyPair keyPair = generateKeyPair(5120);
System.out.println("Keysize is 5120");
byte[] signedBytes = doSign("SHA256withRSA", origMsg, keyPair.getPrivate());
doVerify("SHA256withRSA", origMsg, keyPair.getPublic(), signedBytes);
}

// check small size
public void testSHA256withRSA_512() throws Exception {
KeyPair keyPair = generateKeyPair(512);
System.out.println("Keysize is 512");
byte[] signedBytes = doSign("SHA256withRSA", origMsg, keyPair.getPrivate());
doVerify("SHA256withRSA", origMsg, keyPair.getPublic(), signedBytes);
}

// check size not in the list
public void testSHA256withRSA_1032() throws Exception {
KeyPair keyPair = generateKeyPair(1032);
System.out.println("Keysize is 1032");
byte[] signedBytes = doSign("SHA256withRSA", origMsg, keyPair.getPrivate());
doVerify("SHA256withRSA", origMsg, keyPair.getPublic(), signedBytes);
}

// check size not in the list
public void testSHA256withRSA_2056() throws Exception {
KeyPair keyPair = generateKeyPair(2056);
System.out.println("keysize is 2056");
byte[] signedBytes = doSign("SHA256withRSA", origMsg, keyPair.getPrivate());
doVerify("SHA256withRSA", origMsg, keyPair.getPublic(), signedBytes);
}

//--------------------------------------------------------------------------
//
//
public static void main(String[] args) throws Exception {
junit.textui.TestRunner.run(suite());
}

//--------------------------------------------------------------------------
//
//
public static Test suite() {
TestSuite suite = new TestSuite(TestRSASignatureWithSpecificSize.class);
return suite;
}
}

0 comments on commit fd04630

Please sign in to comment.