我正在尝试使用SECP-256K1(比特币的ECC曲线)对某些数据执行SHA256withECDSA签名,并使用下面发布的代码。这些代码并不总是正常工作,有时会变得相当不稳定和不稳定。
我想帮助稳定下面的代码。
在Signer端生成ECC密钥对的代码:
AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC");
parameters.init(new ECGenParameterSpec("secp256k1"));
ECParameterSpec ecParameterSpec = parameters.getParameterSpec(ECParameterSpec.class);
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
keyGen.initialize(ecParameterSpec);
KeyPair ecKeyPair = keyGen.generateKeyPair();
ECPublicKey deviceEcPub = (ECPublicKey) ecKeyPair.getPublic();
ECPrivateKey deviceEcPriv = (ECPrivateKey) ecKeyPair.getPrivate();
根据公钥的比特币规范将ECC公钥转换为04<Public X Axis><Public Y Axis>
格式:
int cursor = 0;
byte[] devicePubKey = new byte[65];
devicePubKey[cursor] = (byte) 0x04;
cursor++;
System.arraycopy(deviceEcPub.getW().getAffineX().toByteArray(), 0, devicePubKey, cursor, 32);
cursor += 32;
System.arraycopy(deviceEcPub.getW().getAffineY().toByteArray(), 0, devicePubKey, cursor, 32);
在签名者的最后签署一些数据:
Signature sig = Signature.getInstance("SHA256withECDSA");
sig.initSign(deviceEcPriv);
sig.update(data, 0, data.length);
byte[] signature = sig.sign();
Verifier的结尾接收04<Public X Axis><Public Y Axis>
编码的公钥,并将X,Y坐标转换为实际的ECC公钥以供使用:
byte[] x = new byte[32];
byte[] y = new byte[32];
// Skips the first byte containing the 0x04 byte and copies the rest
System.arraycopy(devicePubKey, 1, x, 0, 32);
System.arraycopy(devicePubKey, 33, y, 0, 32);
Verifier使用X和Y坐标并重建ECPublicKey:
AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC");
parameters.init(new ECGenParameterSpec("secp256k1"));
ECParameterSpec ecParameterSpec = parameters.getParameterSpec(ECParameterSpec.class);
ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(x), new BigInteger(y)), ecParameterSpec);
ECPublicKey publicKey = (ECPublicKey) KeyFactory.getInstance("EC").generatePublic(ecPublicKeySpec);
验证者对提供的数据和签名执行验证:
Signature sig = Signature.getInstance("SHA256withECDSA");
sig.initVerify(publicKey);
sig.update(data, offset, length);
return sig.verify(signature);
验证结果有时为True
,有时False
会出现看似随意的事件。
我想就上面的代码提出建议,以确保它没有不稳定的行为。
答案 0 :(得分:1)
好的,我注意到如果第一个字节以0x00开头,则X或Y长度为33个字节,因此我没有正确复制X或Y字节。
为了缓解上面的情况,可以有0x00作为X和Y中的一个或两个的第一个字节,我会读取X和Y的第一个字节,如果第一个字节是0x00,我将跳过复制第一个字节和读取尽可能多的字节,直到每个X和Y的32个字节被完全复制。
还有一些情况,X和/或Y可能不足32个字节(即只有31个字节)
另一种方法是测量长度。如果长度大于32,则跳过字节直到最后32个字节。如果长度小于32,则复制字节并将0x00字节放到字节数组的前面。
我重新实现提取公共X和Y值的方法如下所示。
int cursor = 0;
int copy = 0;
// Get X
byte[] X = deviceEcPub.getW().getAffineX().toByteArray();
// Evaluate length
if (X.length > 32) {
copy = 1;
System.arraycopy(X, copy, devicePubKey, cursor, 32);
} else if (X.length < 32) {
System.arraycopy(X, copy, devicePubKey, cursor + (32 - X.length), X.length);
} else {
System.arraycopy(X, copy, devicePubKey, cursor, 32);
}
// Increment cursor
cursor += 32;
// Reset copy offset status
copy = 0;
// Get Y
byte[] Y = deviceEcPub.getW().getAffineY().toByteArray();
// Evaluate length
if (Y.length > 32) {
copy = 1;
System.arraycopy(Y, copy, devicePubKey, cursor, 32);
} else if (Y.length < 32) {
System.arraycopy(Y, copy, devicePubKey, cursor + (32 - Y.length), Y.length);
} else {
System.arraycopy(Y, copy, devicePubKey, cursor, 32);
}
结果是签名验证变得稳定。