【问题标题】:Android Studio Problem verifying elliptic p256 signature with public key. ECDSAAndroid Studio 使用公钥验证椭圆 p256 签名时出现问题。 ECDSA
【发布时间】:2021-07-15 11:19:24
【问题描述】:

我被这件事困扰了一段时间。 我遵循How can I get a PublicKey object from EC public key bytes? 方法。它适用于他的示例,但不适用于我的示例。不知道哪里出错了。该示例适用于 node.js,但是当使用来自 ECC 椭圆的公钥、消息和签名时,它总是返回 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;
}

如果有人可以提供帮助,我们将不胜感激。热烈的问候。


下面是在节点上创建的:

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);*/

【问题讨论】:

  • 您发布的代码是正确的(对于所述情况,即 sha256-ecdsa-p256),如果我提供正确的签名数据,它可以正常工作。您显示的签名数据是错误的,通过返回 false 您的代码正确报告它是错误的。如果这在 nodejs 上“有效”,要么你在 nodejs 上做错了,要么你将它转换/复制到 Java 错误。我注意到您已标记 sha-3,但您的代码不是 sha-3;应该是吗?
  • 嗨@dave_thompson_085。感谢您的回复。我也迷失了如何在 android 上匹配它是如何在节点上创建的。我更新了上面的示例代码。真的希望你能给我一些见解。
  • 查看答案(或至少是伪答案)

标签: android-studio bouncycastle ecdsa sha-3


【解决方案1】:

根据您添加的 nodejs 代码,您正在执行 SHA-3-256(不是 SHA-256,它是一种 SHA-2 算法),因此您的 Java 代码需要与之匹配。但是,您发布的 nodejs 签名代码使用的数据值与 nodejs java 验证代码使用的值不同,这将永远无法工作。即使修复了这些,您发布的值仍然无法为我验证。但是,以下稍微简化和更正的代码版本(使用不同的任意键)确实可以按预期工作:

$ 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

所以你肯定还有一些你没有在你的帖子中显示的错误。比较我所做的有效与您所做的无效。

我注意到您现在提到了 Android。我自己没有 Android,但据我了解,它有一个 port 的 bouncycastle (spongycastle) 不是真实的东西。 可以想象这就是问题所在,尽管我希望如此广泛使用的东西不会出现这样的问题。但是您可以使用真正的 bouncycastle(至少 1.55)检查桌面 Java。

【讨论】:

  • 谢谢@dave_thompson_085 这是版本号问题。一旦更新到 1.55 就可以完美运行。
猜你喜欢
  • 2023-03-19
  • 2019-09-06
  • 1970-01-01
  • 1970-01-01
  • 2018-11-14
  • 1970-01-01
  • 2020-03-19
  • 2020-03-23
  • 1970-01-01
相关资源
最近更新 更多