首页 > 解决方案 > Android Studio 使用公钥验证椭圆 p256 签名时出现问题。ECDSA

问题描述

我被这个问题困扰了一段时间。我已关注如何从 EC 公钥字节中获取 PublicKey 对象?方法。它适用于他的示例,但不适用于我的示例。不知道哪里出错了。该示例适用于 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);*/

标签: android-studiobouncycastleecdsasha-3

解决方案


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

$ 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,但据我了解,它有一个bouncycastle (spongycastle)端口,而不是真实的东西。可以想象这就是问题所在,尽管我希望如此广泛使用的东西不会出现这样的问题。但是您可以使用真正的 bouncycastle(至少 1.55)检查桌面 Java。


推荐阅读