【问题标题】:Verifying DSA Signature generated by PKCS#11 with OpenSSL使用 OpenSSL 验证 PKCS#11 生成的 DSA 签名
【发布时间】:2017-07-24 01:56:22
【问题描述】:

我想使用 PKCS#11 Java Wrapper 为硬件安全模块的 PKCS#11 API 与 DSA 签署 SHA-256 哈希。为此,我选择了机制 CKM_DSA,从令牌中加载相应的 DSA 密钥并对数据(读取为字节数组)进行签名。我用于测试的密钥长度为 1024 位。

一切似乎都正常:密钥已加载,Session.sign() 产生一个长度为 40 的 byte[] 数组。这对应于 PKCS#11 规范,其中说:

"就该机制而言,DSA 签名是一个 40 字节的字符串, 对应于 DSA 值 r 和 s 的串联,每个都表示最高有效字节在前。"

现在我想使用 openSSL 验证此签名,即使用

openssl dgst -d -sha256 -verify ${PUBLIC_KEY} -signature signature.der <raw input file>

如果我可以这样做

a) 使用 OpenSSL 创建签名

b) 使用 bouncycastle 创建签名并将结果编码为 ASN1 编码的 DER 序列。

现在我想对 PKCS#11 签名做同样的事情。我的问题是:如何格式化这个 40 字节的数组?我尝试了以下方法:

        //sign data
        byte[] signedData = this.pkcs11Session.sign(dataToSign);
        //convert result
        byte[] r = new byte[20];
        byte[] s = new byte[20];
        System.arraycopy(signedData, 0, r, 0, 20);
        System.arraycopy(signedData, 19, s, 0, 20);

        //encode result
        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(new ASN1Integer(r));
        v.add(new ASN1Integer(s));
        return new DERSequence(v).getEncoded(ASN1Encoding.DER);

编码部分似乎是正确的,因为如果我直接使用 bouncycastle 和另一个软件密钥生成 r 和 s,它就可以工作。此外,openssl 确实接受输入格式,但验证有时会失败并出现错误,有时只是“验证失败”。

因此,我假设将 PKCS#11 签名转换为 r 和 s 是错误的。有人可以帮忙找出错误吗?

【问题讨论】:

  • 删除了 cmets,包括我要求提供完整堆栈跟踪的那个。请提供完整的堆栈跟踪,最好提供一些测试数据和密钥。
  • 第二个数组副本不应该从 pos 20 而不是 19 开始吗?好像你有两次 r 的最后一个字节,并且永远不会得到 s 的最后一个字节。

标签: encryption dsa encoding pkcs#11


【解决方案1】:

您可能必须先将rs 值转换为BigInteger 类。原因是 ASN.1 使用有符号值编码,而 DH 导致无符号值编码。因此,您很有可能在 ASN.1 中获得负值,这将导致错误。

要执行转换,请使用new BigInteger(1, r)new BigInteger(1, s) 并将结果放入ASN1Integer 实例中。这里1表示需要将值转换为正值(即输入为无符号正数)。

【讨论】:

  • LuxOnion 写道:“感谢您的建议。我尝试将 r 和 s 转换为 BigInteger。两者都为 BigInteger(r) 或 - 正如您所建议的 - BigInteger(1,r) (或,s ,分别)。我观察到,在使用 OpenSSL 解析 ASN1Sequence 时,值始终为正(我同意,根据 DSA 规范,我希望 s 不能为负)。但是,验证仍然失败。”
猜你喜欢
  • 1970-01-01
  • 2015-11-09
  • 1970-01-01
  • 2011-06-24
  • 2011-08-17
  • 2017-05-16
  • 2011-03-28
  • 1970-01-01
  • 2014-02-24
相关资源
最近更新 更多