首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Android Java Spongycastle ECDSA签名到subtle.crypto Javascript

Android Java Spongycastle ECDSA签名到subtle.crypto Javascript
EN

Stack Overflow用户
提问于 2018-08-22 23:19:41
回答 2查看 493关注 0票数 0

我正在从我的网站导入一组值,这些值是用subtle.crypto用Javascript编写的,用于签名消息。在QR Code中,我将来自Javascript的键的X、Y和D值放入其中,这是我用来复制键的代码:

代码语言:javascript
复制
public static KeyPair GenerateExistingKeyPair(String d, String x, String y) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
    Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
    Log.d(TAG, "GenerateExistingKeyPair: PrivateKey D: " + d);
    Log.d(TAG, "GenerateExistingKeyPair: PublicKey X: " + x);
    Log.d(TAG, "GenerateExistingKeyPair: PublicKey Y: " + y);

    BigInteger privateD = decode(d);
    BigInteger publicX = decode(x);
    BigInteger publicY = decode(y);

    KeyFactory keyFactory = KeyFactory.getInstance("ECDSA", BouncyCastleProvider.PROVIDER_NAME);;
    ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("P-256");
    ECPoint Q = ecSpec.getG().multiply(privateD);

    ECPrivateKeySpec privSpec = new ECPrivateKeySpec(privateD, ecSpec);
    ECPublicKeySpec pubSpec = new ECPublicKeySpec(Q, ecSpec);

    PrivateKey privKey = keyFactory.generatePrivate(privSpec);
    PublicKey pubKey = keyFactory.generatePublic(pubSpec);

    KeyPair keyPair = new KeyPair(pubKey, privKey);
    Log.d(TAG, "GenerateExistingKeyPair: KeyPair: " + keyPair.getPrivate().toString());
    Log.d(TAG, "GenerateExistingKeyPair: " + Hex.toHexString(privKey.getEncoded()));
    return keyPair;

}

我使用“解码”是因为这些值存储在Javascript中的Base64中。

代码语言:javascript
复制
public static BigInteger decode(String value) {
    byte[] decoded = android.util.Base64.decode(value, android.util.Base64.URL_SAFE);
    BigInteger bigInteger = new BigInteger(Hex.toHexString(decoded), 16);
    return bigInteger;
}

现在这是它的输出。

代码语言:javascript
复制
    D/ECDSA:: GenerateExistingKeyPair: PrivateKey D: m-lI_bV8YoNgAgNGpccXPdNtRJ4I6k0hdMdKD7NDYlI
          GenerateExistingKeyPair: PublicKey X: BadCycqeFycXoL4ONkATL7vu1ZxlF66JmrSgbE2A4eY
          GenerateExistingKeyPair: PublicKey Y: obTA6W6xluIdXcqRjnvq0Nh-_IfiWKV4FWziJFxXHUo
D/ECDSA:: GenerateExistingKeyPair: KeyPair: EC Private Key [ed:66:72:8b:8c:1d:97:b9:82:0b:11:c8:1f:6e:db:aa:0e:bd:67:43]
                      X: 5a742c9ca9e172717a0be0e3640132fbbeed59c6517ae899ab4a06c4d80e1e6
                      Y: a1b4c0e96eb196e21d5dca918e7bead0d87efc87e258a578156ce2245c571d4a

据我所知,X和Y是正确的,使用Base64将它们转换回来,得到的值与我收到的值完全相同。现在,我将使用WebRTC通过JSON来散列消息并发送事务。

代码语言:javascript
复制
public static byte[] signTransaction(Wallet wallet, byte[] msgHash) throws Exception {

    Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
    Signature ecdsaSign = Signature.getInstance("SHA256withECDSA", BouncyCastleProvider.PROVIDER_NAME);
    ecdsaSign.initSign(wallet.getKeyPair().getPrivate());
    ecdsaSign.update(msgHash);
    byte[] signature = ecdsaSign.sign();
    Log.d(TAG, "signTransaction: " + new BigInteger(1, signature).toString(16));

    return signature;

}

这是我收到的签名:

代码语言:javascript
复制
3045022026728f6d621689955126e52ca04e5ad7d3f5633111c32ca79979022fc48f7155022100ed94989a8f9fb6bb804ee041cb2923b6ecc17876fbc55c559c93ab9becac415f

经过一些研究,我发现Java中的ECDSA签名是ANS1 DER编码的,而javascript中的签名使用P1363格式,它们只是签名的R和S。

因此,经过一些研究,我发现如何从签名中提取这些值。

代码语言:javascript
复制
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));
}

这为我提供了以下值:

代码语言:javascript
复制
26728f6d621689955126e52ca04e5ad7d3f5633111c32ca79979022fc48f7155
ed94989a8f9fb6bb804ee041cb2923b6ecc17876fbc55c559c93ab9becac415f

在最后一次尝试中,我尝试将这两个字符串放在一起并将它们发送到Javascript端,但无法验证,这两个值并排显示的字符大小与Javascript中生成的签名的大小相同,但该方法

代码语言:javascript
复制
await window.crypto.subtle.verify({name: "ECDSA", hash: {name: "SHA-256"},}, publicKey, signature, data)

在javascript中仍然返回false。

我的问题是,如何使签名在Java和Javascript之间兼容?我能在Javascript中把它从ASN1 DER转换成P1363吗?或者我可以在Java中以相反的方式进行转换?

任何帮助都将不胜感激。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-08-25 00:45:06

回答我自己的问题只是为了结束它,我找到了我没有被JS端验证的原因。我创建和复制公钥/私钥的所有方法都有效。我将ASN1 DER签名转换为R||S值的代码也工作正常。

我在Hash字符串的顶部签署事务,就像我认为浏览器所做的那样。原来浏览器没有对原始散列字符串进行签名,但他在签名之前对散列字符串进行了签名:

代码语言:javascript
复制
async sign(msg) {
    const encoder = new TextEncoder('utf-8');
    const msgBuffer = encoder.encode(msg.toString());
    const signedBuffer = await ECDSA.sign(this.keys.privateKey, msgBuffer);
    const signedArray = Array.from(new Uint8Array(signedBuffer));
    return Encryption.byteToHexString(signedArray);
}

请注意以下几行:

事实证明,浏览器将散列字符串编码为UTF-8,并对大小为64的字节数组进行签名,而不是对具有20个左右字节的字符串进行签名。所以之前当浏览器尝试验证我的签名时,它实际上对我的哈希串做了同样的事情,将其转换为UTF-8,这就是为什么我的签名失败了,因为我没有在浏览器试图验证的消息上签名。

如果我更仔细地潜入JS海湾,就可以节省大约2天。

感谢Maarten Bodewes试图帮助我,你实际上指出了我代码中的一些缺陷,并为我向你展示的JS端代码的缺失而道歉,你可能会发现这个问题,并在两天前帮助我。

票数 0
EN

Stack Overflow用户

发布于 2018-08-23 10:13:49

在执行以下操作时,您将执行两次散列:

代码语言:javascript
复制
ecdsaSign.update(msgHash);

ecdsaSign对象已经执行了SHA-256散列。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51970086

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档