当前位置: 首页 > 知识库问答 >
问题:

Android Studio使用公钥验证椭圆p256签名时出现问题。ECDSA

牟正真
2023-03-14

我被这件事困扰了一段时间。我已经了解了如何从EC公钥字节中获取公钥对象?方法这是他的例子,但不是我的。不知道哪里出了问题。该示例在节点上工作。但是当使用公钥、消息和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);*/

共有1个答案

冯峻
2023-03-14

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

 类似资料:
  • 问题内容: 我有一个外部服务,在定义的事件发生后会给我回电,并用其私钥签署他的请求。 我已经存储了如下公钥: 因此,我的工作是通过验证签名来检查请求的内容是否未更改。 这是我的算法: 但是目前,我的算法已停止在此消息的“ PublicKey publicKey = keyFactory.generatePublic(publicKeySpec)”步骤处: 那么,如何以java api接受的方式加载

  • 验证应用程序: 据我所知.pfx文件包含公钥和私钥信息,因此我不应该让任何人都可以使用它。据我所知,验证步骤只需要公钥。我如何使用rsa.verifydata或其他函数来验证签名而不暴露我的pfx文件?

  • 我有一个XML文档,需要在其中验证签名。SignedInfo元素具有指定算法“http://www.w3.org/2001/10/xml-exc-c14n#”的元素CanonicalizationMethod,还具有一个子元素InclusiveNamespaces,该子元素具有填充的PrefixList属性,如下所示: 我使用以下代码创建我的C14Transform对象: 其中来自属性。 我遇到的

  • 我需要用DSA公钥验证X509证书签名。我的证书文件x509.crt,我的DSA公钥在一个名为dsa_pub.key的文件中 我正试图为此使用openssl。我已经阅读了openssl验证文档,但在其中找不到任何有关DSA的参考。 当我运行以下命令时,我可以在证书中看到我的DSA公钥: 但是,在使用文件DSA_pub.key中的DSA公钥验证cert.crt时,我遇到了麻烦 如果您有任何建议,我将

  • 我的理解是公钥加密,私钥解密...有谁能帮我弄明白吗?

  • 另外,这里还有一个函数演示,它从mod和指数生成PEM(摘自http://stackoverflow.com/questions/18835132/xml-to-pem-in-node-js) 前面提到的jsonwebtoken库可以使用任何一种方法来验证JWT--但是为什么呢?如果这两种验证方法都可以验证一个JWT签名,为什么它们都存在呢?他们之间有什么权衡?一个比另一个更安全吗?我应该使用哪一