I'm trying to generate a keypair for the Secp192r1 curve using Javacard 2.2.1 (I've also tried using Javacard 2.2.2) by calling the KeyPair.genKeyPair function for the javacard Keypair object.
However when during the generation of a keypair, the following error is sometimes thrown "java.lang.ArithmeticException: BigInteger not invertible". The error is not thrown every time, but rather occurs around 50% of the generations. If the key generation is successful, it seems however not to generate correct keypairs. This is since I´ve tried in those successful cases to use the generated public key to create a Bounty castle representation of the public key, which then fails and signals that the public key is an incorrect point.
I've tried to debug the code further, but as I lack the source files of the internals of javacard, I've not been able to able to track the error further than that it's thrown in the AsymmetricCipherKeyPairGenerator generateKeyPair function.
I'm completely confused as to what I've done incorrectly, and all my attempts to solve the issue have failed. If anyone can spot what I'm doing wrong I would be very thankful!
It should be noted that this I've only tested the code using jcardsim to simulate and debug the execution of the code, and not run the code on actual phsyical chips if that is of any importance. The jcardsim version I'm using is the following: https://github.com/licel/jcardsim/raw/master/jcardsim-2.2.1-all.jar
I´ve created some example source code below, which sporadically throws the error:
Secp192r1 curve code:
package example.applet;
import javacard.security.ECKey;
public class Secp192r1 {
public static final byte[] EC192_FP_P = new byte[]{
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
public static final byte[] EC192_FP_A = new byte[]{
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC};
public static final byte[] EC192_FP_B = new byte[]{
(byte) 0x64, (byte) 0x21, (byte) 0x05, (byte) 0x19,
(byte) 0xE5, (byte) 0x9C, (byte) 0x80, (byte) 0xE7,
(byte) 0x0F, (byte) 0xA7, (byte) 0xE9, (byte) 0xAB,
(byte) 0x72, (byte) 0x24, (byte) 0x30, (byte) 0x49,
(byte) 0xFE, (byte) 0xB8, (byte) 0xDE, (byte) 0xEC,
(byte) 0xC1, (byte) 0x46, (byte) 0xB9, (byte) 0xB1};
public static final byte[] EC192_FP_G_UNCOMPRESSED = new byte[]{
(byte) 0x04, // Uncompressed
(byte) 0x18, (byte) 0x8D, (byte) 0xA8, (byte) 0x0E,
(byte) 0xB0, (byte) 0x30, (byte) 0x90, (byte) 0xF6,
(byte) 0x7C, (byte) 0xBF, (byte) 0x20, (byte) 0xEB,
(byte) 0x43, (byte) 0xA1, (byte) 0x88, (byte) 0x00,
(byte) 0xF4, (byte) 0xFF, (byte) 0x0A, (byte) 0xFD,
(byte) 0x82, (byte) 0xFF, (byte) 0x10, (byte) 0x12,
(byte) 0x07, (byte) 0x19, (byte) 0x2B, (byte) 0x95,
(byte) 0xFF, (byte) 0xC8, (byte) 0xDA, (byte) 0x78,
(byte) 0x63, (byte) 0x10, (byte) 0x11, (byte) 0xED,
(byte) 0x6B, (byte) 0x24, (byte) 0xCD, (byte) 0xD5,
(byte) 0x73, (byte) 0xF9, (byte) 0x77, (byte) 0xA1,
(byte) 0x1E, (byte) 0x79, (byte) 0x48, (byte) 0x11};
public static final byte[] EC192_FP_R = new byte[]{
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0x99, (byte) 0xDE, (byte) 0xF8, (byte) 0x36,
(byte) 0x14, (byte) 0x6B, (byte) 0xC9, (byte) 0xB1,
(byte) 0xB4, (byte) 0xD2, (byte) 0x28, (byte) 0x31};
public static final short EC192_FP_K = 1;
protected static boolean setCommonCurveParameters(ECKey key) {
try {
key.setFieldFP(EC192_FP_P, (short)0, (short)EC192_FP_P.length);
key.setA(EC192_FP_A, (short)0, (short)EC192_FP_A.length);
key.setB(EC192_FP_B, (short)0, (short)EC192_FP_B.length);
key.setG(EC192_FP_G_UNCOMPRESSED, (short)0, (short)EC192_FP_G_UNCOMPRESSED.length);
key.setR(EC192_FP_R, (short)0, (short)EC192_FP_R.length);
key.setK(EC192_FP_K);
return true;
}
catch(Exception e) {
return false;
}
}
}
Key generation code:
package example.applet;
import javacard.security.*;
public class ExampleKeyImpl {
private static KeyPair keyPair;
public ExampleKeyImpl() {
keyPair = new KeyPair(
(ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, KeyBuilder.LENGTH_EC_FP_192, false),
(ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_192, false));
Secp192r1.setCommonCurveParameters((ECKey) keyPair.getPrivate());
Secp192r1.setCommonCurveParameters((ECKey) keyPair.getPublic());
}
public void generateKeyPair() {
try {
keyPair.genKeyPair(); // THIS IS WHERE THE ERROR IS THROWN
}
catch (Exception e){
String errorMessage = e.getMessage(); // GIVES THE BigInteger ERROR MESSAGE
}
}
}
Applet code:
package example.applet;
import javacard.framework.*;
import javacard.security.Signature;
public class ExampleApplet extends Applet {
private static byte[] scratch;
private static ExampleKeyImpl keyImpl;
private static Signature localSignature;
public static void install(byte[] parameters, short startOffset, byte lengthToRead) throws ISOException {
new ExampleApplet();
}
public ExampleApplet() {
initializeResources();
generateKeyPair();
}
private void initializeResources() {
scratch = JCSystem.makeTransientByteArray((short) (200), JCSystem.CLEAR_ON_RESET);
localSignature = Signature.getInstance(Signature.ALG_ECDSA_SHA, false);
keyImpl = new ExampleKeyImpl();
}
private void generateKeyPair() {
keyImpl.generateKeyPair();
}
}
My best estimate is that I've somehow implemented the Secp192r1 curve incorrectly, so if you spot any error in my implementation, please tell me what I've done wrong. Thank you in advance!
I found that the issue is solved by changing the constructor in ExampleKeyImpl from:
public ExampleKeyImpl() {
keyPair = new KeyPair(
(ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, KeyBuilder.LENGTH_EC_FP_192, false),
(ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_192, false));
Secp192r1.setCommonCurveParameters((ECKey) keyPair.getPrivate());
Secp192r1.setCommonCurveParameters((ECKey) keyPair.getPublic());
}
To:
public ExampleKeyImpl() {
keyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_192);
Secp192r1.setCommonCurveParameters((ECKey) keyPair.getPrivate());
Secp192r1.setCommonCurveParameters((ECKey) keyPair.getPublic());
}
I'm not really sure why that makes a difference though.