Verify javacard signature ALG_ECDSA_SHA on bouncy

2019-02-15 06:44发布

问题:

My problem looks like this: im generating a signature on javaCard (jcdk 2.2.2) and when when i want to verify it on the terminal using BouncyCastle the signature isnt always verified- 1 in 3,66 (average in 100 tries) signatures are verified, the rest in returning false. when i verify the signature on the card it always returns true, but on the terminal it usually returns false, but sometimes true. because the terminal sometimes gives a positive answer i think that the code is ok and the reason is some where else but i can be wrong.

on javacard im usign Signature.ALG_ECDSA_SHA, and on the terminal Signature.getInstance("SHA1withECDSA", "BC") i tried also SHA1withDetECDSA but i behaves similar.

please help.

回答1:

The problem is that JavaCard and BouncyCastle use different format of the resulting signature. For example for the Prime192v1 curve the resulting JavaCard signature is always 56 bytes long, but the Bouncy Castle signature is sometimes shorter, because it omits leading zeros in EC point coordinates.

The JavaCard signature (for Prime192v1, again) looks like this in the hexadecimal format:

30 36 02 19 [25 bytes of the first coord] 02 19 [25 bytes of the second coord]

(it is a DER encoded structure: SEQUENCE of two INTEGERs)

However, BouncyCastle does not expect leading these zeros in EC coords. So you have to remove them and fix the DER structure, for example

30 36 02 19 **00 00** [the rest 23 bytes of the first coord] 02 19 **00** [24 bytes of the second coord]

from JavaCard must be converted for Bouncy Castle to:

30 **33** 02 **17** [23 bytes of the first coord] 02 **18** [24 bytes of the second coord]

The reason why you sometimes verify the signature correctly is simple: sometimes there are no leading zeros in your JavaCard signature coordinates.

EDIT: inspired by TajnosAgentos' observation:

BouncyCastle encodes coords as signed integers, JavaCard always as unsigned integers. That is why BouncyCastle adds a special leading zero (although it trims other leading zeros) whenever the most significant bit of the first byte is 1, because the coord is always a positive number.



回答2:

I have a solution. I dont know why this works, byt it does... The signing function on javacard should look like this:

    byte[] buffer = apdu.getBuffer();

    signature.init(ecPrivateKey, Signature.MODE_SIGN);
    short sLen = signature.sign(helloWorld, (short) 0, (short)helloWorld.length, scratch, (short) 0);

    if((short)scratch[4] < 0 || (short)scratch[30] < 0)
    {
        sign(apdu);
    }
    else
    {
        Util.arrayCopyNonAtomic(scratch, (short)0, buffer, (short)0, (short) sLen);

        apdu.setOutgoingAndSend((short)0, (short)sLen);
    }

ist a "prototype" function. it workes so im sharing it first, later maybe i will improve it a little. scratch is a byte array declared somewhere else.

like VOJTA posted earlier the signature (in my example) looks something like this:

30 34 02 18 [24 bytes NO1] 02 18 [24 byte NO2] [SW - 90 00]

the SW is NOT needed. so the SHORT value of the FIRST byte of [24 bytes NO1] and the SHORT value of the first byte of the [24 bytes NO2] cannot be nagative. i dont know why this works. i printed the verified and not verified signatures and i was looking for any differences, and i found this. I made over 1000 tries and verification ALWAYS returned TRUE. the probability that this 2 bytes will be nagative is 1/4 ((-128,127), 0 goes to positive, half negative, half positive -> 1/2 * 1/2 = 1/4) so probably thats why earlier only 1 on 3,66 signatures vere verified. if someone knows why it worsk i will be happy to read his post

EDIT

now i know how it works. there are to solutions of this problem. one is the solution above- generate signatures until the SHORT value of 2 elements of the array are positive and the second solution:

the signature looks something like this:

30 34 02 18 [24 bytes NO1] 02 18 [24 byte NO2]

now the algorithm about which VOJTA wrote about (the bold values will be changed if needed) lex x=34 (SECOND BYTE OF THE SIGN- this may vary, in my signature it is 34). if first bites of [24 byte NO1] and [24 byte NO2] are positive then do nothing with the signature. if the SHORT value of the first byte of [24 bytes NO1] is negative then we have to add ONE to X (now x=35), add ONE to the 18 next to the first byte of [24 bytes NO1] and insert a value 00 next to the 18 (now 19) to the signature. if the SHORT value of the first byte of [24 bytes NO2] is negative then we have to add ONE to X (now x=36), add ONE to the 18 next to the first byte of [24 bytes NO2] and insert 00 next to 18 (now 19). if both first bytes vere negative then the signature should look something like this:

30 36 02 19 00 [24 bytes NO1] 02 19 00 [24 byte NO2]

if only first first byte of [24 bytes NO1] was negative, and the first byte of [24 bytes NO2] was positive the the signature should look somethung like this

30 35 02 19 00 [24 bytes NO1] 02 18 [24 byte NO2]

and the last case: if only first first byte of [24 bytes NO1] was positive, and the first byte of [24 bytes NO2] was negativethe the signature should look somethung like this

30 35 02 18 [24 bytes NO1] 02 19 00 [24 byte NO2]

hope that i will be useful