在 BouncyCastle 中导入 PKCS11 public 密钥

Import PKCS11 public key in BouncyCastle

我正在使用 HSM 使用 PKCS11 命令生成一对 public/private 椭圆密钥,但我需要在 BouncyCastle 中使用 public 密钥。

我可以读取 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

这是我的工作解决方案(感谢 Dave)

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) OCTET STRING(标签 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},后者(仅)被 NIST 在 FIPS 186-2+ 中采用为 P-224,两个被 TeleTrusT 标准化为 brainpool224{r1,t1}。这些可以从 asn1.x9.ECNamedCurveTablejce.ECNamedCurveTable 内置。可能有任意数量的非标准曲线,AFAICT 您必须自己创建。如果你不知道你的未压缩点在什么曲线上,你可以通过测试它是否满足候选曲线的方程来非常准确地猜测;我懒得为你做这个了。 (压缩点更难猜测,也许是不可能的。)

给定曲线(从参数集或直接)将 DER 转换为 ASN1OctetStringASN1OctetString.getInstance(ASN1Primitive.fromByteArray(byte[])) 或只是 ASN1OctetString.getInstance(Object/*byte[]*/) 为你做前者,然后你可以 directly construct an X9ECPoint 并根据您要使用的 API,调用 .getPoint 转换为 math.ec.ECPoint 或构造相关(更复杂)的结构,如 crypto.params.ECPublicKeyParameters.


作为对评论的回应而添加,因为这相当长,而且在一定程度上改变了我的回答:

我不知道你在使用 dotNET;我的经验是使用 Bouncy 的 Java 版本,我印象深刻的是这两个版本足够紧密地跟踪您的代码甚至可以编译。 FWIW 修改后代码的 Java 等价物,增加了从参数显式构建 BCECPublicKey 因为 JCE Signature 只采用 Key 类型而不是 Parameters 类型,使用测试密钥对和数据对我有用。您可以尝试类似的练习——在软件中创建密钥对和签名(针对已知数据)并确认您的代码是否适用于该情况,然后尝试隔离硬件签名情况下的差异。尽管 PKCS11 和其他硬件设备通常设计为防止将设备生成的密钥导出到软件,在这些软件中它们可能面临更大的危害风险,但它们在导入时并不总是那么严格——您也许可以使用您的软件-在硬件中生成用于比较的密钥。

除了任何签名未验证的一般原因(错误的数据、错误的散列——您显然默认的、错误的密钥)另一种可能性 可能 是签名格式. ECDSA(和 DSA)签名有两种常用的格式(或编码):DER 中整数的 ASN.1 序列,或者只是固定二进制格式的两个整数连接而没有任何元数据。 'Standard' Java(更确切地说,Java 与 Sun/Oracle 提供商)仅使用 ASN.1 格式; Java Bouncy 默认为 ASN.1,但如果您将算法名称更改为 [hash]WITH{PLAIN,CVC}-ECDSA,则支持 ECDSA 的固定格式,因此我希望 dotNET 中的 Bouncy(假设您的代码正在使用它,尽管没有明确说明)陈述)可能会做同样的事情。我了解(但没有个人经验)PKCS11 使用固定格式。如果我向 Java Bouncy 提供错误的格式,它会抛出异常而不是返回 verify=false,但我不知道 dotNET 在这里是否会有所不同。详细看你的签名值是什么格式。