当前位置: 首页 > 知识库问答 >
问题:

根据RFC测试向量计算Java中的ECDSA签名

赫连俊悟
2023-03-14

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

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

我正在尝试使用以下代码通过java的ECDSA签名实现运行ECDSA-256测试向量值(RFC中的第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 917DD72E 97566EA1 C066957C 86FA3BB4 E26CAD5BF90B7F81 899256CE 7594BB1E A0C89212 748BFF3B3D5B0315

相反,我得到:

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

我知道长度差异是由于JavaASN.1编码签名。然而,其余的部分是完全错误的,我不明白为什么。

任何帮助或建议将不胜感激!

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

共有2个答案

魏安然
2023-03-14

下面是我对tsechin使用BouncyCastle的解决方案的完整测试,但坚持使用好的旧JCA API:

    byte[] input = { 0x61, 0x62, 0x63 };

    //Create the static private key W from the Test Vector
    ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
    org.bouncycastle.jce.spec.ECPrivateKeySpec privateKeySpec = new org.bouncycastle.jce.spec.ECPrivateKeySpec(new BigInteger("DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F", 16), parameterSpec);
    KeyFactory kf = KeyFactory.getInstance("EC");
    ECPrivateKey spriv = (ECPrivateKey) kf.generatePrivate(privateKeySpec);

    //Perfrom ECDSA signature of the data with SHA-256 as the hash algorithm
    Signature dsa = Signature.getInstance("SHA256withECDSA", "BC");
    FixedSecureRandom random = new FixedSecureRandom();
    random.setBytes(Hex.decode("9E56F509196784D963D1C0A401510EE7ADA3DCC5DEE04B154BF61AF1D5A6DECE"));
    dsa.initSign(spriv, random);
    dsa.update(input);
    byte[] output = dsa.sign();

    // compare the signature with the expected reference values
    ASN1Sequence sequence = ASN1Sequence.getInstance(output);
    DERInteger r = (DERInteger) sequence.getObjectAt(0);
    DERInteger s = (DERInteger) sequence.getObjectAt(1);
    Assert.assertEquals(r.getValue(), new BigInteger("CB28E0999B9C7715FD0A80D8E47A77079716CBBF917DD72E97566EA1C066957C", 16));
    Assert.assertEquals(s.getValue(), new BigInteger("86FA3BB4E26CAD5BF90B7F81899256CE7594BB1EA0C89212748BFF3B3D5B0315", 16));
东方明亮
2023-03-14

我猜每次运行程序时,相同的纯文本(待签名)输入都会得到不同的签名值。

ECDSA指定为每个签名生成随机临时ECDSA私钥。为此,签名。getInstance(“SHA256withECDSA”)不允许您指定临时密钥(这是一件好事,可以防止许多人在脚上自拍!)。相反,它会得到自己的SecureRandom实例,这将使您的输出不确定性。

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

您可以做的是扩展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(),它获取您想要的字节。这使您可以控制临时键以匹配测试向量中指定的键。您可能需要调整实际字节(例如添加零预填充),以匹配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);

最后,这个Bigintger[](应该是长度==2)是(r, s)值。您需要对ASN.1进行DER编码,这将为您提供 <罢工> 机器人 你要找的字节。

 类似资料:
  • 如果我有一个相机的偏航、俯仰和向上向量(在我的情况下总是[0,1,0]),计算相应滚动的最佳方法是什么?

  • 问题内容: 我想使用GeoDjango或GeoPy根据方向和距离计算一个点。 例如,如果我的点是(-24680.1613,6708860.65389),我想使用Vincenty距离公式找出北1KM,东1KM,苏尔1KM和西1KM的点。 我能找到的最接近的东西是distance.py(https://code.google.com/p/geopy/source/browse/trunk/geopy/

  • 我需要计算和的中位数。但是,要计算每个中位数,我必须包括具有相同面和相同类别的所有行。例如,要计算第二行的中位数,我必须包括行 2 和 3,因为我在第 2 行和第 3 行中具有相同的面和 。我正在尝试使用循环函数,但我不知道如何包含此条件。 这就像一个条件中位数。 非常感谢您的关注。 这里,就是例子:

  • 我用Java生成了一个ECDSA签名,我想从中获得R和S值。我的理解是我生成的签名是DER编码的。有人能给我提供一些Java代码(也许用Bouncy Castle)来检索作为大整数的R和S值吗? 注意:如果有帮助,我通过JCE的Signature类使用内置提供程序生成签名,并且我的P_256EC密钥对的签名长度通常徘徊在70到72字节之间。

  • 我正在尝试计算多个列的中值,但是我的数据有点奇怪。它看起来像下面的示例。 在表中到列表示该值的出现次数。我想计算中位数的出现次数。 例如对于ID = 1 是我想要创建的计算。 对于ID=2 我尝试过使用<code>rep()或<code>rep(10,2)),这就是我所期望的。我只是努力创建一个列表或向量,每个列都有重复。

  • 问题内容: 大家都说,由于性能的原因,应该使用vector(因为Vector在每次操作和所有操作之后都会同步)。我写了一个简单的测试: 结果如下: 基于此,似乎在遍历和阅读方面的表现要好一些。也许这是一个愚蠢的任务,或者我做出了错误的假设-有人可以解释一下吗? 问题答案: 您已经编写了一个幼稚的微基准测试。在JVM上进行微基准测试是一项非常棘手的事情,要列举所有的陷阱甚至不容易,但是这里有一些经典