gpt4 book ai didi

java - 根据 RFC 测试 vector 计算 Java 中的 ECDSA 签名

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:50:50 26 4
gpt4 key购买 nike

我正在用 java 为与 ikev2 协议(protocol)相关的程序编写测试工具。作为其中的一部分,我需要能够计算 ECDSA 签名(特别是使用 NIST P-256 曲线)。

RFC 4754描述了 IKEv2 中 ECDSA 的使用,并提供了一组测试 vector (包括我需要的 p256 曲线)。

我正在尝试使用以下代码通过 java 的 ECDSA 签名实现来运行 ECDSA-256 测试 vector 值(RFC 中的 Section 8.1):

//"abc" for the input
byte[] input = { 0x61, 0x62, 0x63 };

//Ugly way of getting the ECParameterSpec for the P-256 curve by name as opposed to specifying all the parameters manually.
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec kpgparams = new ECGenParameterSpec("secp256r1");
kpg.initialize(kpgparams);
ECParameterSpec params = ((ECPublicKey) kpg.generateKeyPair().getPublic()).getParams();

//Create the static private key W from the Test Vector
ECPrivateKeySpec static_privates = new ECPrivateKeySpec(new BigInteger("DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F", 16), params);
KeyFactory kf = KeyFactory.getInstance("EC");
ECPrivateKey spriv = (ECPrivateKey) kf.generatePrivate(static_privates);

//Perfrom ECDSA signature of the data with SHA-256 as the hash algorithm
Signature dsa = Signature.getInstance("SHA256withECDSA");
dsa.initSign(spriv);
dsa.update(input);

byte[] output = dsa.sign();
System.out.println("Result: " + new BigInteger(1, output).toString(16));

结果应该是:

CB28E099 9B9C7715 FD0A80D8 E47A7707 9716CBBF 917DD72E97566EA1 C066957C 86FA3BB4 E26CAD5B F90B7F81 899256CE 7594BB1E A0C89212748BFF3B 3D5B0315

相反,我得到:

30460221 00dd9131 edeb5efd c5e718df c8a7ab2d 5532b85b 7d4c012a e5a4e90c 3b824ab5 d7022100 9a8a2b12 9e10a2ff 7066ff79 89aa73d5 ba37c868 5ec36517 216e2e43 ffa876d7

我知道长度差异是由于 Java ASN.1 对签名进行了编码。然而,剩下的部分是完全错误的,我不知道为什么。

如有任何帮助或建议,我们将不胜感激!

P.S 我不是 ECDSA 或 Java 加密专家,所以这可能是我犯的一个愚蠢的错误

最佳答案

我猜你每次运行你的程序时,你都会为相同的明文(待签名)输入获得不同的签名值。

ECDSA 指定为每个签名生成一个随机的临时 ECDSA 私钥。为此,Signature.getInstance("SHA256withECDSA") 不允许您指定临时 key (这是一件好事,可以防止许多人搬起石头砸自己的脚!)。相反,它获得自己的 SecureRandom 实例,这将使您的输出不确定。

这可能意味着您不能使用 JCE (Signature.getInstance()) 进行测试 vector 验证。

您可以做的是以返回确定性数据的方式扩展 SecureRandom。显然,您不应该在实际部署中使用它:

public class FixedSecureRandom extends SecureRandom {
private static boolean debug = false;
private static final long serialVersionUID = 1L;
public FixedSecureRandom() { }
private int nextBytesIndex = 0;

private byte[] nextBytesValues = null;

public void setBytes(byte[] values) {
this.nextBytesValues = values;
}

public void nextBytes(byte[] b) {
if (nextBytesValues==null) {
super.nextBytes(b);
} else if (nextBytesValues.length==0) {
super.nextBytes(b);
} else {
for (int i=0; i<b.length; i++) {
b[i] = nextBytesValues[nextBytesIndex];
nextBytesIndex = (nextBytesIndex + 1) % nextBytesValues.length;
}
}
}
}

呸。好的,现在你有一个 SecureRandom 类,它返回一些已知字节数,然后回退到真正的 SecureRandom。我会再说一遍(请原谅大喊大叫)——不要在生产中使用它!

接下来,您将需要使用 ECDSA 实现,让您指定自己的 SecureRandom。为此,您可以使用 BouncyCaSTLe 的 ECDSASigner。除了在这里您要给它您自己的盗版 FixedSecureRandom,这样当它调用 secureRandom.getBytes() 时,它会获得您想要的字节。这使您可以控制临时 key 以匹配测试 vector 中指定的 key 。您可能需要修改实际字节(例如,添加零预填充)以匹配 ECDSASigner 将要请求的内容。

ECPrivateKeyParameters ecPriv = ...; // this is the user's EC private key (not ephemeral)

FixedSecureRandom fsr_k = new FixedSecureRandom();
fsr_k.setBytes(tempKeyK);

ECDSASigner signer = new ECDSASigner();
ParametersWithRandom ecdsaprivrand = new ParametersWithRandom(ecPriv, fsr_k);
signer.init(true, ecdsaprivrand);

请注意,BC 的 ECDSASigner 只实现了 EC 签名部分,而不是哈希。您仍然需要进行自己的哈希处理(假设您的输入数据在 data 中):

Digest md = new SHA256Digest()
md.reset();
md.update(data, 0, data.length);
byte[] hash = new byte[md.getDigestSize()];
md.doFinal(hash, 0);

在创建 ECDSA 签名之前:

BigInteger[] sig = signer.generateSignature(hash);

最后,这个 BigInteger[](应该是 length==2)是 (r,s) 值。您需要对其进行 ASN.1 DER 编码,这应该会为您提供所需的 droids 字节。

关于java - 根据 RFC 测试 vector 计算 Java 中的 ECDSA 签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19904548/

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