Search code examples
javacryptographyjavacardapduelliptic-curve

Recovering an ECPublicKey from Java to JavaCard


This question is related to the one I asked yesterday : Recovering an ECPublicKey from JavaCard to Java

I have the same problem but in the opposite way : After sending the public key from my card to my computer (the point is represented as an octet string in uncompressed form as per ANSI X9.62) I can recover the corresponding key.

But I can't recover it on my card if I send it from my computer, I get a CryptoException (ILLEGAL_VALUE) which means that the form isn't correct or the point doesn't match with the curve parameter.

I verified, my parameters are well defined and I send the point in the right form...

To debug it, I try to send the public key from my card to my computer, and resend it to my card to try to recover it (so I'm sure that parameters are fine). Whatever, I still get the same error...

To illustrate my problem, I post the corresponding code:

On the card side - to send the public key :

pubKey = (ECPublicKey) KeyBuilder.buildKey(
            KeyBuilder.TYPE_EC_FP_PUBLIC, (short) 0x0100, false);
pubKey.setFieldFP(p, (short) 0x0001, (short) 0x0020);
pubKey.setA(a, (short) 0x0001, (short) 0x0020);
pubKey.setB(b, (short) 0x0000, (short) 0x0020);
pubKey.setR(r, (short) 0x0001, (short) 0x0020);
pubKey.setG(g, (short) 0x0000, (short) g.length);

privKey = (ECPrivateKey) KeyBuilder.buildKey(
        KeyBuilder.TYPE_EC_FP_PRIVATE, (short) 0x0100, false);

KeyPair keypair = new KeyPair(pubKey, privKey);
    keypair.genKeyPair();

short len = pubKey.getW(apduBuffer, (short) 0x0000);
setOutgoingAndSend((short) 0x0000, len);

The response sent is:

APDU <<<: 04C2B28FBE96C5EAC1E81750E9B288B0BD8357D3AED4EA39413197D734B145EBC92F3FD7489B9A7EF4C8A956427668851F0BB3A55D5C7B9033A533F21463E1A2139000

So the first coordinate is x = C2B28FBE96C5EAC1E81750E9B288B0BD8357D3AED4EA39413197D734B145EBC9 and the second one, y = 2F3FD7489B9A7EF4C8A956427668851F0BB3A55D5C7B9033A533F21463E1A213

On the computer side - to recover the key and resend it:

byte[] x = new byte[32];
byte[] y = new byte[32];
System.arraycopy(W, 1, x, 0, x.length);
System.arraycopy(W, 1 + x.length, y, 0, y.length);

ECPublicKeySpec pub = new ECPublicKeySpec(new ECPoint(new BigInteger(1,x), new BigInteger(1,y)), ecParamSpec);

ECPublicKey ecPubKey = (ECPublicKey) kf.generatePublic(pub);

byte[] k_x = ecPubKey.getEncoded();

byte[] tmp = new byte[70];
tmp[0] = (byte) 0x80;
tmp[1] = (byte) 0x20;
tmp[2] = (byte) 0x00;
tmp[3] = (byte) 0x00;
tmp[4] = (byte) (65);
System.arraycopy(k_x, k_x.length - 65, tmp, 5,65);

sendApdu(cardChan, tmp);

The APDU sent is:

APDU >>>: 802000004104C2B28FBE96C5EAC1E81750E9B288B0BD8357D3AED4EA39413197D734B145EBC92F3FD7489B9A7EF4C8A956427668851F0BB3A55D5C7B9033A533F21463E1A213

So we can easily see that the data which is sent is exactly the same that the one received before.

And, finally, on the card side, I have the following code to recover the key:

pubKey.setW(apduBuffer, ISO7816.OFFSET_CDATA,
            ISO7816.OFFSET_LC);

len = pubKey.getW(apduBuffer, (short) 0x0000);
setOutgoingAndSend((short) 0x0000, len);

But I get APDU >>>: 0401

Which means CryptoException.ILLEGAL_VALUE that is raised at the instruction

pubKey.setW(apduBuffer, ISO7816.OFFSET_CDATA,
                ISO7816.OFFSET_LC);

How can it raises an Exception knowing that I send the same thing that I get by pubKey.getW()?


Solution

  • The problem is likely:

    pubKey.setW(apduBuffer, ISO7816.OFFSET_CDATA, ISO7816.OFFSET_LC);
    

    ISO7816.OFFSET_CDATA has value 5. It has been replaced with APDU.getOffsetCData() for the location of the command data. This is compatible with extended length APDU's, where the location may be larger than the value 5.

    ISO7816.OFFSET_LC has value 4. It should normally not be used either. In old code you could use apduBuffer[ISO7816.OFFSET_LC] to get the size of the command data Nc. It is however recommended to always use APDU.setIncomingAndReceive() to get the Nc value. Lc is the encoding of this Nc value and OFFSET_LC is the location where Lc is present, but again this is not compatible with extended length APDU's.

    Currently your code is failing as you use which is the location of the size of the command data instead the size itself. Your public point is bigger than 4 bytes, which is why the code fails.