0

I been stuck with this matter for a while. I have followed How can I get a PublicKey object from EC public key bytes? method. It's working on his example but not on mine. Not sure where went wrong. The sample works on node.js but when using the publickey, msg and signature from ECC elliptic it just always returning false.

byte[] pubKey = Hex.decode("04f491ccd1913757e10c2fe965ac764dd6af2f8bbfa47f4dddff0fe7736601701464d9fa02e6eada870ac626825e28d99bdf2d3a2742ba6dc99a56c663e875852a");
byte[] message = Hex.decode("613839633666323863616238633538623533313466626365636364326363633166646537643937383663373863373134313432646230343361313530323937657c416c6920416873616e7c43562d4d3957515253544d7c4d616c657c4d59537c323032312d30352d30355431303a33303a32392b30383a30307c3235383530303030317c39343735362d347c313234303538313030303030303130347c4b75616c61204c756d70757220486f73706974616c7c3031");
byte[] signature = Hex.decode("304502202f70fe9a8173a13daef97e8d59c9cd9aec3851c0f0568e5b5d61ddb752d1cb9e022100d9eccdcdd4b4a448bd36f3a9dafde08b55b8176f6b21bb6e1cc7d35a929dc1bf");



private static boolean isValidSignature(byte[] pubKey, byte[] message,byte[] signature) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException, InvalidKeySpecException {
    Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA", new BouncyCastleProvider());
    ecdsaVerify.initVerify(getPublicKeyFromBytes(pubKey));
    ecdsaVerify.update(message);
    System.out.println(getPublicKeyFromBytes(pubKey));
    System.out.println(message);
    System.out.println(signature);
    return ecdsaVerify.verify(signature);
}

private static PublicKey getPublicKeyFromBytes(byte[] pubKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
    ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("P-256");
    KeyFactory kf = KeyFactory.getInstance("ECDSA", new BouncyCastleProvider());
    ECNamedCurveSpec params = new ECNamedCurveSpec("P-256", spec.getCurve(), spec.getG(), spec.getN());
    ECPoint point =  ECPointUtil.decodePoint(params.getCurve(), pubKey);
    ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
    ECPublicKey pk = (ECPublicKey) kf.generatePublic(pubKeySpec);

    return pk;
}

Appreciate if someone could help. warm regards.


Below is created on node:

console.log("Create D. Signature: ");
let elliptic = require('elliptic');
let sha3 = require('js-sha3');
let ec = new elliptic.ec('p256');
let key = ec.keyFromPrivate("our private key");
let msgHash = sha3.sha3_256('scv1|Ali Ahsan|CV-M9WQRSTM|Male|MYS|2021-05- 05T10:30:29+08:00|258500001|94756-4|1240581000000104|Kuala Lumpur Hospital|01'); 
console.log("msgHash: "+msgHash);
let signature = key.sign(msgHash); signature=signature.toDER(); signature=Buffer.from(signature).toString('hex'); console.log("signature: "+signature);
let pubPoint = key.getPublic('hex'); console.log('public key: '+pubPoint);

/*
console.log("Verify D. Signature:");
const qrContent = 'a89c6f28cab8c58b5314fbceccd2ccc1fde7d9786c78c714142db043a150297e|Ali Ahsan|CV-M9WQRSTM|Male|MYS|2021-05-05T10:30:29+08:00|258500001|94756-4|1240581000000104|Kuala Lumpur Hospital|01|304502202f70fe9a8173a13daef97e8d59c9cd9aec3851c0f0568e5b5d61ddb752d1cb9e022100d9eccdcdd4b4a448bd36f3a9dafde08b55b8176f6b21bb6e1cc7d35a929dc1bf'
const pub = '4f491ccd1913757e10c2fe965ac764dd6af2f8bbfa47f4dddff0fe7736601701464d9fa02e6eada870ac626825e28d99bdf2d3a2742ba6dc99a56c663e875852a'
const EC = require('elliptic').ec;
let ec = new EC('p256');
const key = ec.keyFromPublic(pub,'hex');
const sigIndex = qrContent.lastIndexOf('|');
const data = qrContent.slice(0, sigIndex);
const sig = qrContent.slice(sigIndex + 1, qrContent.length); 
console.log(data);
console.log(sig);
console.log(key.verify(data, sig);*/
  • Your posted code is correct (for the stated case i.e. sha256-ecdsa-p256) and works correctly if I give it correct signature data. The signature data you show is wrong and by returning false your code correctly reports it is wrong. If this 'works' on nodejs either you're doing something wrong on nodejs or you converted/copied it to Java wrong. I notice you've tagged sha-3, but your code is NOT sha-3; should it be? – dave_thompson_085 Jul 16 '21 at 10:53
  • hi @dave_thompson_085. appreciate your response. i'm also lost on how i can match on android on how it's created on node. i updated the sample code as above. Really hope you could gv me some insight on this. – syahril idris Sep 22 '21 at 04:22
  • See answer (or at least pseudo-answer) – dave_thompson_085 Sep 23 '21 at 12:19

1 Answers1

0

Based on the nodejs code you added you are doing SHA-3-256 (not SHA-256 which is a SHA-2 algorithm), so your Java code needs to match that. However, your posted nodejs signing code uses a different value for the data than used by either the nodejs or java verify code, which will never work. Even fixing those, your posted values still don't verify for me. However, the following slightly simplified and corrected versions of your code (with a different, arbitrary key) DO work as expected:

$ cat SO68392857.js
// SO68392857.js
let msg = 'a89c6f28cab8c58b5314fbceccd2ccc1fde7d9786c78c714142db043a150297e|Ali Ahsan|CV-M9WQRSTM|Male|MYS|2021-05-05T10:30:29+08:00|258500001|94756-4|1240581000000104|Kuala Lumpur Hospital|01|';
// msg corrected(?) in first field and timestamp
let elliptic = require('elliptic');
let sha3 = require('js-sha3');
let ec = new elliptic.ec('p256');
let key = ec.keyFromPrivate('000102030405060707090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');
let msgHash = sha3.sha3_256(msg); console.log("msgHash: "+msgHash);
let signature = Buffer.from(key.sign(msgHash).toDER()).toString('hex'); console.log("signature: "+signature);
let pubPoint = key.getPublic('hex'); console.log('public key: '+pubPoint);

const k2 = ec.keyFromPublic(pubPoint,'hex');
console.log("verify="+k2.verify(msgHash,signature)); // verify needs hash not msg
$ nodejs SO68392857.js
msgHash: 3b1254cd6bc46bab86d36b9c45fad246f8e9cd71b48dd36f19831b15bbdf94a2
signature: 3046022100c901843a6d63f04d168cc961748dbd3e86fd9352e87a4e8d3bd2126ee78414620221008438128c52eadb04f80671e935acfc1b36605eccc1652818afd05ab53d6781b0
public key: 0462a5b3db60f01fbaab42721a46b07061b0509162fbfb1291098e20d804be4970e9ff7954b1b2b33e7a9812d6833052a56b2f8c8c699ce20b80d17af68ce8fa8c
verify=true
$ cat SO68392857.java
// SO68392857.java
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
import java.util.*;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.ECPointUtil;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class SO68392857 {
  public static void main (String[] args) throws Exception {
    byte[] msg = "a89c6f28cab8c58b5314fbceccd2ccc1fde7d9786c78c714142db043a150297e|Ali Ahsan|CV-M9WQRSTM|Male|MYS|2021-05-05T10:30:29+08:00|258500001|94756-4|1240581000000104|Kuala Lumpur Hospital|01|".getBytes(); // ASCII safe
    byte[] sig = fromhex("3046022100c901843a6d63f04d168cc961748dbd3e86fd9352e87a4e8d3bd2126ee78414620221008438128c52eadb04f80671e935acfc1b36605eccc1652818afd05ab53d6781b0");
    byte[] pub = fromhex("0462a5b3db60f01fbaab42721a46b07061b0509162fbfb1291098e20d804be4970e9ff7954b1b2b33e7a9812d6833052a56b2f8c8c699ce20b80d17af68ce8fa8c");
    Security.addProvider(new BouncyCastleProvider());
    // getPublicKeyFromBytes
    ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("P-256");
    KeyFactory kf = KeyFactory.getInstance("ECDSA", "BC");
    ECNamedCurveSpec params = new ECNamedCurveSpec("P-256", spec.getCurve(), spec.getG(), spec.getN());
    ECPoint point =  ECPointUtil.decodePoint(params.getCurve(), pub);
    ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
    ECPublicKey pk = (ECPublicKey) kf.generatePublic(pubKeySpec);
    // isValidSignature
    Signature ecdsaVerify = Signature.getInstance("SHA3-256withECDSA", "BC");
    ecdsaVerify.initVerify(pk);
    ecdsaVerify.update(msg);
    System.out.println("verify="+ecdsaVerify.verify(sig));

    // check that altering either data or signature is caught
    byte[] badmsg = Arrays.copyOf(msg,msg.length); badmsg[badmsg.length-1] ^= 1;
    ecdsaVerify.initVerify(pk);
    ecdsaVerify.update(badmsg);
    System.out.println("badmsg="+ecdsaVerify.verify(sig));
    byte[] badsig = Arrays.copyOf(sig,sig.length); badsig[badsig.length-1] ^= 1;
    ecdsaVerify.initVerify(pk);
    ecdsaVerify.update(msg);
    System.out.println("badsig="+ecdsaVerify.verify(badsig));
  }
  static byte[] fromhex(String x){
    byte[] t = new byte[x.length()/2];
    for(int i = 0; i<t.length; i++) t[i] = (byte) Integer.parseInt(x.substring(i*2,i*2+2),16);
    return t;
  }
}
$ java -cp [path_to_bouncy_jar] SO68392857.java # java can now run single-class without precompiling
verify=true
badmsg=false
badsig=false

so you must still have something wrong that you aren't showing in your post. Compare what I did that works with what you did that doesn't.

I notice you now mention Android. I don't have Android myself but to my understanding it has a port of bouncycastle (spongycastle) not the real thing. It is conceivable that's the problem, although I would expect something so widely used not to have problems like this. But you might check against desktop Java with real bouncycastle (at least 1.55).

dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70