我想使用硬件安全模块的 PKCS#11 API 的 PKCS#11 Java 包装器通过 DSA 签署 SHA-256 哈希。为此,我选择了 CKM_DSA 机制,从 token 加载相应的 DSA key 并对数据(以字节数组形式读取)进行签名。我用于测试的 key 长度为 1024 位。
一切似乎都工作正常: key 已加载,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 和另一个软件 key 生成 r 和 s ,它就可以工作。此外,openssl确实接受输入格式,但验证有时会失败并出现错误,有时只是“验证失败”。
因此,我认为 PKCS#11 签名到 r 和 s 的转换是错误的。有人可以帮忙找出错误吗?
最佳答案
您可能必须转换 r
和s
值为 BigInteger
上课之前。原因是 ASN.1 使用有符号值编码,而 DH 结果为无符号值编码。因此,您很有可能在 ASN.1 中获得负值,这将导致错误。
要执行转换,请使用 new BigInteger(1, r)
和new BigInteger(1, s)
并将结果放入 ASN1Integer
实例。这里1表示需要将值转换为正值(即输入为无符号正数)。
关于encryption - 使用 OpenSSL 验证 PKCS#11 生成的 DSA 签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42587642/