首页 > 解决方案 > 在 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();

标签: pkcs#11

解决方案


好的,这确实似乎是一个(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 转换为ASN1OctetStringwithASN1OctetString.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 在这里是否会有所不同。详细查看您的签名值以检查它是哪种格式。


推荐阅读