【问题标题】:Import PKCS11 public key in BouncyCastle在 BouncyCastle 中导入 PKCS11 公钥
【发布时间】:2019-03-12 13:38:42
【问题描述】:

我正在使用 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

这是我的工作解决方案(感谢 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 格式的 EC 点;你能以十六进制或其他可读形式展示一个例子吗? (使用编辑按钮添加到您的 Q 中,而不是在评论中,因为可以根据 StackExchange 政策删除 cmets 中的讨论。)
  • 谢谢戴夫,我编辑了我的评论。

标签: pkcs#11


【解决方案1】:

好的,这确实似乎是一个 (DER) OCTET STRING(标记 04,长度 39 十六进制),其中包含一个 28 字节的标准(X9.62/SEC1 et seq)未压缩格式(第一个八位字节 04)点(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.ECNamedCurveTablejce.ECNamedCurveTable 内置获得。可能有任意数量的非标准曲线,您必须自己创建 AFAICT。如果你不知道你的未压缩点在哪条曲线上,你可以通过测试它是否满足候选曲线的方程来准确地猜测它;我懒得为你做这件事。 (压缩点会更难猜,也许是不可能的。)

给定曲线(来自参数集或直接)将 DER 转换为 ASN1OctetString 使用 ASN1OctetString.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 序列,要么只是两个固定二进制格式的整数,没有任何元数据连接。 “标准”Java(更准确地说,是带有 Sun/Oracle 提供程序的 Java)仅使用 ASN.1 格式;带有 Bouncy 的 Java 默认为 ASN.1,但如果您将算法名称更改为 [hash]WITH{PLAIN,CVC}-ECDSA,则支持 ECDSA 的固定格式,所以我希望 dotNET 中的 Bouncy(假设您的代码正在使用它,尽管没有明确说明)可能会做同样的事情.我了解(但没有个人经验)PKCS11 使用固定格式。如果我给 Java Bouncy 提供了错误的格式,它会抛出异常而不是返回 verify=false,但我不知道 dotNET 在这里是否会有所不同。详细查看您的签名值以检查它是哪种格式。

【讨论】:

  • 我根据你的建议再次编辑了我的问题,但数据从未验证过,这段代码可以吗?
  • 抱歉耽搁了;这在我的“邮箱”上滚动显示。对于 cmets 来说太长了,所以请参阅编辑。
猜你喜欢
  • 2019-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-17
  • 1970-01-01
  • 1970-01-01
  • 2012-02-23
相关资源
最近更新 更多