1

I have generated an ECDSA signature in Java and I would like to get the R and S values from it in order to have the COSE encoded signature. It is my understanding that the signature I have generated is DER encoded (by default using bouncyCastle). I properly retrieve the R and S values when I used P-256 curve (SHA256withECDSA).

I have some issues parsing the signature from one encoding to another when I use other curves (P-521, P-384).

Here after is how I parse it :

public static byte[] extractRandSToCose(byte[] signature) {

    if (signature[0] == 0x30) {
        //parse R
        int rSize = signature[3];
        BigInteger rBigInt = new BigInteger(Arrays.copyOfRange(signature, 4, rSize+4));
        //strip out sign byte 0x00
        byte[] r = toByteArrayUnsigned(rBigInt);

        //parse S
        int sSize = signature[5 + rSize];
        int index = rSize + 6;
        BigInteger sBigInt = new BigInteger(Arrays.copyOfRange(signature, index, index+sSize));
        //strip out sign byte 0x00
        byte[] s = toByteArrayUnsigned(sBigInt);

        return Bytes.concat(r,s);
    }
    return null;
}


public static byte[] toByteArrayUnsigned(BigInteger bigInteger) {
    byte[] extractedBytes = bigInteger.toByteArray();
    int skipped = 0;
    boolean skip = true;
    for (byte b : extractedBytes) {
        boolean signByte = b == 0x00;

        if (skip && signByte) {
            skipped++;
            continue;
        } else if (skip){
            skip = false;
        }
    }
    extractedBytes = Arrays.copyOfRange(extractedBytes, skipped, extractedBytes.length);
    return extractedBytes;
}

I dont get what I am doing wrong.

PS: Those methods works from time to time. It works everytime for P-256 (whether the DER signature is 70 71 or 72 bytes long and properly gives me a 64 byte long Cose signature), P-384 (same for 102, 103 104 byte long and properly gives me a 96 byte long cose signature) but only works half the time for P-521 (138, 139, 140 byte long and even when it does the cose signature is only 31 byte long....)

Sox -
  • 592
  • 1
  • 9
  • 28
  • 1
    An alternative to converting the ASN.1/DER signature to an IEEE P1363 (r|s) signature is to generate the signature directly in IEEE P1363 (r|s) format. Newer BC versions support this format with the algorithm `SHA256withPlain-ECDSA` (or `SHA384withPlain-ECDSA` or `SHA512withPlain-ECDSA`). – Topaco Oct 12 '21 at 19:06
  • 1
    Regarding the conversion it might be more efficient to use an ASN.1 parser (e.g. from BouncyCastle), see e.g. [DER Decode ECDSA Signature in Java](https://stackoverflow.com/q/39385718/9014097). – Topaco Oct 12 '21 at 19:17
  • 2
    @Topaco+ Bouncy since 1.51 in 2014 -- or SunEC since 9 in 2017 with different (and clumsier) naming `{hash}withECDSAinP1363Format`. OP: your code, although kludgy, should _mostly_ work for any curve up to 488 bits including P-384; P-521/secp521r1 and brainpool512 are the only common ones above that, but some signatures which happen to have arithmetically small r and/or s will be wrong. – dave_thompson_085 Oct 12 '21 at 20:35
  • 1
    @dave_thompson_085 - Thx. I dimly remembered that support for r|s was added later, but not exactly when. Then I guess the BC version of the OP should work ;-) – Topaco Oct 12 '21 at 21:02
  • Thank you both for your answers, I ll take a look at it. Any reasons why it wont work with any curves after 488 bits ? – Sox - Oct 12 '21 at 22:17
  • how does above logic works if either of R and S value has 0x00 as first byte and 0x00 is part of 32 byte value itself not added to handle negative value. In this case you will be copying only 31 bytes of R or S value. – Harish Feb 27 '23 at 06:16

1 Answers1

0

{hash}withECDSAinP1363Format did the trick ! Thank you ! I didn't even needed to read all those specs to see how to parse it...

Sox -
  • 592
  • 1
  • 9
  • 28