pkcs#11 - 在 BouncyCastle 中导入 PKCS11 公钥
问题描述
我正在使用 HSM 使用 PKCS11 命令生成一对公钥/私钥,但我需要在 BouncyCastle 中使用公钥。
我可以读取 DER 格式的属性 EC_POINT,但我不知道如何在 BouncyCastle 中导入它。
这是我的 EC_POINT 属性:CKA_EC_POINT: 04-39-04-ED-48-AE-D9-F8-02-CA-80-E1-1C-F2-3D-C9-C4-7D-B4-C5-9E- D2-53-A6-FE-27-D7-12-EF-C3-7F-2D-FC-D2-D0-31-62-8F-AF-60-19-E4-33-0F-63-A7- E4-95-33-0C-0D-D5-94-6C-92-B9-44-D8-2B
这是我的工作解决方案(感谢戴夫)
public ECPublicKeyParameters GetPubKeyFromParms(string curve, string pub) {
var pc = ToByteArray(pub);
var x9ecpar = ECNamedCurveTable.GetByName(curve);
var ecdp = new ECDomainParameters(x9ecpar.Curve, x9ecpar.G, x9ecpar.N);
var basePoint = lsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey(null, ecdp, pc));
var subinfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(basePoint);
var publicKey = (ECPublicKeyParameters)PublicKeyFactory.CreateKey(subinfo);
return publicKey;
}
签名从 PKCS11 格式到 BouncyCastle 格式的转换:
var r = signature.Take(signature.Length / 2).ToArray();
var s = signature.Skip(signature.Length / 2).ToArray();
var dersignature = new Org.BouncyCastle.Asn1.DerSequence(
new Org.BouncyCastle.Asn1.DerInteger(new Org.BouncyCastle.Math.BigInteger(1, r)),
new Org.BouncyCastle.Asn1.DerInteger(new Org.BouncyCastle.Math.BigInteger(1, s))
).GetDerEncoded();
解决方案
好的,这确实似乎是一个(DER)八位字节字符串(标签 04,长度 39 十六进制),其中包含一个标准(X9.62/SEC1 et seq)未压缩格式(第一个八位字节 04)的点,用于 28 个八位字节(224-位)曲线。这对我来说似乎很愚蠢,因为我知道的所有标准 ASN.1 结构都在 BIT STRING 中放置了一个(点或其他)公钥并包含元数据,但显然这是已知的或至少听说过,因为asn1.x9.X9ECPoint
它有一个构造函数。org.bouncycastle
(为了简洁和格式化,我省略了适用于所有内容的外包装。)
要使用一个点,您需要指定它在哪条曲线上;我知道由 SECG 和 X9 标准化的两条 224 位曲线(更准确地说是参数集)secp224{k1,r1},后者(仅)在 FIPS 186-2+ 中被 NIST 采用为 P-224,两个由 TeleTrusT 标准化为 Brainpool224{r1,t1}。asn1.x9.ECNamedCurveTable
这些可从或内置jce.ECNamedCurveTable
。可能有任意数量的非标准曲线,您必须自己创建 AFAICT。如果你不知道你的未压缩点在哪条曲线上,你可以通过测试它是否满足候选曲线的方程来准确地猜测它;我懒得为你做这件事。(压缩点更难猜测,也许是不可能的。)
给定曲线(来自参数集或直接)将 DER 转换为ASN1OctetString
withASN1OctetString.getInstance(ASN1Primitive.fromByteArray(byte[]))
或只是ASN1OctetString.getInstance(Object/*byte[]*/)
为您执行前者,然后您可以直接构造一个 X9ECPoint并根据您想要使用它的 API,调用.getPoint
转换为math.ec.ECPoint
或构造相关的(更复杂的)结构,如crypto.params.ECPublicKeyParameters
.
添加以回应评论,因为这很长,而且在某种程度上改变了我的答案:
我没有意识到您正在使用 dotNET;我的经验是使用 Java 版本的 Bouncy,这两个版本对您的代码甚至可以编译的跟踪都非常接近,这给我留下了深刻的印象。FWIW 修改后的代码的 Java 等价物,增加了一个BCECPublicKey
从参数显式构建的 a,因为 JCESignature
只接受一个Key
类型而不是 aParameters
类型,确实适用于我的测试密钥对和数据。您可以尝试类似的练习——在软件中创建密钥对和签名(用于已知数据)并确认您的代码是否适用于该案例,然后尝试隔离硬件签名案例中的差异。尽管 PKCS11 和其他硬件设备通常旨在防止设备生成的密钥被导出到软件中,在这些软件中它们可能面临更大的泄露风险,但它们在导入时并不总是那么严格——你也许可以使用你的软件——在硬件中生成密钥以进行比较。
除了任何签名无法验证的一般原因(错误的数据,错误的哈希 - 您显然默认,错误的密钥)之外,另一种可能性可能是签名格式。ECDSA(和 DSA)签名有两种常用的格式(或编码):要么是 DER 中的整数的 ASN.1 序列,要么只是两个固定二进制格式的整数,没有任何元数据连接。“标准”Java(更准确地说,是带有 Sun/Oracle 提供程序的 Java)仅使用 ASN.1 格式;带有 Bouncy 的 Java 默认为 ASN.1,但如果您将算法名称更改为,则支持 ECDSA 的固定格式[hash]WITH{PLAIN,CVC}-ECDSA
,所以我希望 dotNET 中的 Bouncy(假设您的代码正在使用它,尽管没有明确说明)可能会做同样的事情。我了解(但没有个人经验)PKCS11 使用固定格式。如果我给 Java Bouncy 提供了错误的格式,它会抛出异常而不是返回 verify=false,但我不知道 dotNET 在这里是否会有所不同。详细查看您的签名值以检查它是哪种格式。
推荐阅读
- amazon-web-services - 无法从 AWS 网络外部访问我在 ECS 上的网站
- python - 执行我用 pyinstaller 制作的 .exe 时出错
- python - 以 PDF 格式导出 Excel 文件,文件路径中带有括号
- sql - 用于比较具有多列的 2 个表的 SQL
- python - 使用 concurrent.futures 模块中的 ThreadPoolExecutor 终止执行程序
- php - 将 int 类型转换为数组
- postgresql - Yocto:没有提供'nativesdk-postgresql'
- embedded - VSCode OpenOCD 调试 - 检查一个非常大的数组?
- python - 用beautifulsoup爬取时如何在一个dataframe中获取多次迭代的结果?
- github-actions - 当分支为主且存在匹配标签时运行的 Github 操作工作流