3

I am trying to sign content using a java client and then verifying the same on a server (nodejs). My client signature function uses ECDSA and returns a byte[]. I have access to the x and y coordinate values comprising the publicKey on the server.

public static byte[] sign(String plainText, PrivateKey privateKey) throws Exception {
    java.security.Signature dsa = java.security.Signature.getInstance("SHA1withECDSA");
    dsa.initSign(privateKey);
    dsa.update(plainText.getBytes(UTF_8));
    return dsa.sign();
}

Is it possible to find the r and s values that comprise the signature? How do I convert the byte[] obtained from above into a (r,s) pair or a DER-encoded signature as hex? On the node server side I am using elliptic for the signature verification.

Edit:

Thank you Dave for the comments, I am using the methods indicated in this SO answer:

public static BigInteger extractR(byte[] signature) throws Exception {
    int startR = (signature[1] & 0x80) != 0 ? 3 : 2;
    int lengthR = signature[startR + 1];
    return new BigInteger(Arrays.copyOfRange(signature, startR + 2, startR + 2 + lengthR));
}

public static BigInteger extractS(byte[] signature) throws Exception {
    int startR = (signature[1] & 0x80) != 0 ? 3 : 2;
    int lengthR = signature[startR + 1];
    int startS = startR + 2 + lengthR;
    int lengthS = signature[startS + 1];
    return new BigInteger(Arrays.copyOfRange(signature, startS + 2, startS + 2 + lengthS));
}

Knowing the x,y, r and s values, I am trying to verify the message this is a test string on the node server.

Message : this is a test string
Curve Parameters: secp256k1
Public Key:
   X : 52552626316292256179275635993655485173638967401615704770864787021340356427096
   Y : 115577290317206876914379725139810202736866562857077399175416156471449711434272
Signature details:
   R : [0, -63, -80, -50, -87, -56, 93, 19, 82, 46, 51, 14, -75, 103, 115, 126, 21, 94, 43, 102, -21, -86, -29, -5, 25, 14, -6, -116, 120, -54, -66, 2, -78]
   S : [0, -40, -119, 77, -14, 113, -105, -117, 93, 70, -107, -3, 63, 12, 77, -48, 59, -47, -7, -126, -60, -109, 95, -6, -66, -120, -8, -103, 122, 40, 24, -31, 89]

For verification using the elliptic module I have the following

var EC_Instance = new EC();
var signature = {
    r : new Buffer([0, -63, ..., 2, -78]),
    s : new Buffer([0, -40, ..., -31, 89])
};
var x = "52552626316292256179275635993655485173638967401615704770864787021340356427096";
var y = "115577290317206876914379725139810202736866562857077399175416156471449711434272";
EC_Instance.importPublicKey(x, y); // calls ec.keyFromPublic(pub, 'hex')
var verification_true = EC_Instance.verify("this is a test string", signature);

And EC_Instance is an object of the class containing the following:

constructor() {
    // Require the elliptic library for curve cryptography
    var EC = require('elliptic').ec;
    var ec = new EC('secp256k1');
    this.ec = ec;
}

importPublicKey(x, y) {
    var pub = { x: x.toString('hex'), y: y.toString('hex') };
    var key = this.ec.keyFromPublic(pub, 'hex');
    this.key = key;
    return key;
}

verify(message, signature) {
    return this.key.verify(message, signature);
}
Sudheesh Singanamalla
  • 2,283
  • 3
  • 19
  • 36
  • ECDSA signature from Java crypto _is_ (ASN.1) DER, see https://stackoverflow.com/questions/49825455 and https://stackoverflow.com/questions/48177791 . To convert between them in Java see https://stackoverflow.com/questions/34063694 or https://stackoverflow.com/questions/39385718 already suggested at the right of this page by Stack's search. – dave_thompson_085 Apr 23 '18 at 08:17
  • Thank you very much @dave_thompson_085. That was very helpful. However, I still seem to be stuck with the validation of the signature on the node server side. I've edited the question with more details. Thank you for your help. – Sudheesh Singanamalla Apr 23 '18 at 09:00
  • The X,Y you posted are not the hex representation of coordinates of a point (thus publickey) on secp256k1. They are the _decimal_ representation of such coordinates. Using that point/key, your r,s _do_ validate in Java+Bouncy(LWAPI) against the SHA1 (not SHA256) of your string. Note Java treats `byte` as signed -128..+127 and `BigInteger.toByteArray` is two's-complement; I don't know if JS and that JS module do those the same way and the example at your link suggests not. Since it says it does take DER as hexstring or buffer, why don't you try that? – dave_thompson_085 Apr 24 '18 at 00:25

1 Answers1

1

It's probably the hash function. SHA-1 should not be used anymore for signature operations.

I presume that for the node.js code that the SHA-256 hash method is used, although it is next to impossible to verify this with the current documentation (hashing is hardly even mentioned).

Note that - with any signatures - the hash is a configuration parameter; it should (and for EC it cannot, at least without actual verification) be determined from the signature itself.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • Thanks @Maarten , I just took a deeper look at found that the elliptic library uses hash.js and for the [curve chosen](https://github.com/indutny/elliptic/blob/79e2f3769771cbd092c75c740ac45c73a59be688/lib/elliptic/curves.js#L175-L205) as secp256k1 it has defined the hash.sha256. I updated the relevant java client code but the verification still doesn't seem to work as expected. It looks like I am still missing something – Sudheesh Singanamalla Apr 23 '18 at 11:55