gpt4 book ai didi

java - 使用 prime256v1 和 SHA256withECDSA 算法的 BouncyCaSTLe ECDSA 签名验证失败

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:13:47 27 4
gpt4 key购买 nike

我需要使用 BouncyCaSTLe 加密提供程序通过 Java 验证 ECDSA 签名。到目前为止,BouncyCaSTLe 无法验证签名。

签名是在Atmel AT88CK590 Crypto Authentication模块中创建的,公钥可以从模块中获取。这是 C/C++ 格式的公钥,长度为 64 个八位字节:

uint8_t pubKey[] = {
// X coordinate of the elliptic curve.
0xc1, 0x71, 0xCB, 0xED, 0x65, 0x71, 0x82, 0x2E, 0x8F, 0x8A, 0x43, 0x8D, 0x72, 0x56, 0xD1, 0xC8,
0x86, 0x3C, 0xD0, 0xBC, 0x7F, 0xCC, 0xE3, 0x6D, 0xE7, 0xB7, 0x17, 0xED, 0x29, 0xC8, 0x38, 0xCB,

// Y coordinate of the elliptic curve.
0x80, 0xCD, 0xBE, 0x0F, 0x1D, 0x5C, 0xC5, 0x46, 0x99, 0x24, 0x8F, 0x6E, 0x0A, 0xEA, 0x1F, 0x7A,
0x43, 0xBA, 0x2B, 0x03, 0x80, 0x90, 0xE9, 0x25, 0xB2, 0xD0, 0xE6, 0x48, 0x93, 0x91, 0x64, 0x83
};

Base64编码的原始消息、签名和公钥:

// Raw message to sign
private static final String tokenStr = "12345678901234567890123456789012";
// Base64 encoded
private static final String pubKeyStr = "wXHL7WVxgi6PikONclbRyIY80Lx/zONt57cX7SnIOMuAzb4PHVzFRpkkj24K6h96
Q7orA4CQ6SWy0OZIk5Fkgw==";
// Base64 encoded
private static final String signatureStr = "XF2WossFTA82ndYFGEH0FPqAldkFQLGd/Bv/Qh8UYip7sXUvCUnFgi1YXjN3WxLn
IwSo3OaHLCOzGAtIis0b3A==";

要转换公钥,我使用以下内容:

private static PublicKey getPublicKeyFromBytes(byte[] pubKey, String ecSpec, String provider)
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec(ecSpec);
KeyFactory kf = KeyFactory.getInstance(ECDSA_CRYPTO, provider);
ECNamedCurveSpec params = new ECNamedCurveSpec(ecSpec, spec.getCurve(), spec.getG(), spec.getN());
ECPoint pubPoint = ECPointUtil.decodePoint(params.getCurve(), pubKey);
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(pubPoint, params);
PublicKey publicKey = kf.generatePublic(pubKeySpec);

return publicKey;
}

要将签名转换为 DER 格式,我使用以下内容:

private static byte[] toDERSignature(byte[] tokenSignature) throws IOException {
byte[] r = Arrays.copyOfRange(tokenSignature, 0, tokenSignature.length / 2);
byte[] s = Arrays.copyOfRange(tokenSignature, tokenSignature.length / 2, tokenSignature.length);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DEROutputStream derOutputStream = new DEROutputStream(byteArrayOutputStream);
ASN1EncodableVector v = new ASN1EncodableVector();

v.add(new ASN1Integer(new BigInteger(1, r)));
v.add(new ASN1Integer(new BigInteger(1, s)));
derOutputStream.writeObject(new DERSequence(v));

byte[] derSignature = byteArrayOutputStream.toByteArray();

return derSignature;
}

验证签名的代码如下:

Security.addProvider(new BouncyCastleProvider());

byte[] tokenBytes = tokenStr.getBytes("UTF-8");
String urlDecodePubKeyStr = pubKeyStr.replace(newlineHtml, "");
byte[] pubKeyBytes = DatatypeConverter.parseBase64Binary(urlDecodePubKeyStr);
String urlDecodeSignatureStr = signatureStr.replace(newlineHtml, "");
byte[] signBytes = DatatypeConverter.parseBase64Binary(urlDecodeSignatureStr);
byte[] derSignature = toDERSignature(signBytes);
ByteBuffer bb = ByteBuffer.allocate(pubKeyBytes.length + 1);

bb.put((byte)4);
bb.put(pubKeyBytes);

PublicKey ecPublicKey = getPublicKeyFromBytes(bb.array(), "prime256v1", "BC");

System.out.println("\nSignature: " + Hex.toHexString(signBytes).toUpperCase());
System.out.println("DER Signature: " + Hex.toHexString(derSignature).toUpperCase());
System.out.println(ecPublicKey.toString());

Signature signature = Signature.getInstance("SHA256withECDSA", "BC");

signature.initVerify(ecPublicKey);
signature.update(tokenBytes);

boolean result = signature.verify(derSignature);

System.out.println("BC Signature Valid: " + result);

输出:

Signature: 5C5D96A2CB054C0F369DD6051841F414FA8095D90540B19DFC1BFF421F14622A7BB1752F0949C5822D585E33775B12E72304A8DCE6872C23B3180B488ACD1BDC
DER Signature: 304402205C5D96A2CB054C0F369DD6051841F414FA8095D90540B19DFC1BFF421F14622A02207BB1752F0949C5822D585E33775B12E72304A8DCE6872C23B3180B488ACD1BDC
EC Public Key
X: c171cbed6571822e8f8a438d7256d1c8863cd0bc7fcce36de7b717ed29c838cb
Y: 80cdbe0f1d5cc54699248f6e0aea1f7a43ba2b038090e925b2d0e64893916483

BC Signature Valid: false

有没有人遇到过同样的问题?我在这里错过了什么?

最佳答案

该签名值不是像标准和通常那样在散列上计算,而是直接在数据上计算。要么:

  • Atmel 不进行哈希处理,因此您必须先进行哈希处理并提供要签名的哈希输出

  • Atmel 应该散列,但您没有设置一些必要的选项或其他内容

  • 您实际上想要一个没有散列的非常规“原始”签名。请注意,这将数据限制为 32 或 31 个字节。如果要签名的值可能会受到对手的影响并且用于签名的随机生成器很弱,​​我认为这会导致恢复您的私钥的攻击(尽管我还没有完成)。即使有 if,我也会避免这种风险。

无论如何,您拥有的值可以使用方案 NoneWithECDSA 进行验证。

此外,在对此进行测试时,我发现您的代码中有一些您可能想要或可能不想要的改进。首先,你不需要准编码然后解码点,假设你有固定格式的 X,Y,你可以直接使用它们。其次,您确实需要对签名进行 DER 编码,但这可以更简单地完成。这是我的版本,为了清楚起见,在一个线性 block 中进行了两项更改:

public static void main (String[] args) throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// test data; real data would come from outside
byte[] dataBytes = "12345678901234567890123456789012".getBytes("UTF-8");
String sigString = "XF2WossFTA82ndYFGEH0FPqAldkFQLGd/Bv/Qh8UYip7sXUvCUnFgi1YXjN3WxLn
IwSo3OaHLCOzGAtIis0b3A==";
String pubkeyString = "wXHL7WVxgi6PikONclbRyIY80Lx/zONt57cX7SnIOMuAzb4PHVzFRpkkj24K6h96
Q7orA4CQ6SWy0OZIk5Fkgw==";
String ecSpec="prime256v1"; int size=32; // bytes for x,y in pubkey also r,s in sig

byte[] pubkeyBytes = DatatypeConverter.parseBase64Binary(pubkeyString.replaceAll("
","") );
KeyFactory kf = KeyFactory.getInstance ("ECDSA", "BC");
ECNamedCurveParameterSpec cspec = ECNamedCurveTable.getParameterSpec(ecSpec);
BigInteger x = new BigInteger(1, Arrays.copyOfRange(pubkeyBytes,0,size));
BigInteger y = new BigInteger(1, Arrays.copyOfRange(pubkeyBytes,size,size*2));
ECPublicKeySpec kspec = new ECPublicKeySpec (cspec.getCurve().createPoint(x, y), cspec);
PublicKey k = kf.generatePublic(kspec);

byte[] sigBytes = DatatypeConverter.parseBase64Binary(sigString.replaceAll("
","") );
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(/*r*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(sigBytes,0,size))));
v.add(/*s*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(sigBytes,size,size*2))));
byte[] sigDer = new DERSequence(v).getEncoded();

Signature sig = Signature.getInstance("NoneWithECDSA", "BC"); // NOTE None instead of a hash
sig.initVerify (k); sig.update (dataBytes);
System.out.println ("verify="+sig.verify(sigDer));
}

PS:您将公钥的一半注释为 椭圆曲线的 X [Y] 坐标。它们是公钥曲线上点的坐标;曲线本身没有坐标。

关于java - 使用 prime256v1 和 SHA256withECDSA 算法的 BouncyCaSTLe ECDSA 签名验证失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39714906/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com