我正在用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));
结果应为:

相反,我得到:

我知道长度差异是由于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;
            }
        }
    }
}

ew好的,现在您有了一个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编码,这将为您提供所需的机器人字节。

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

10-09 07:08