.NET Core 3无法加载ECC私钥

6
感谢在.NET中加载ECC私钥,我能够将ECC私钥加载到.NET Core 3中并使用它们执行签名任务。
然而,我遇到了一个密钥,无法被ECDSA.ImportPrivateKey加载。奇怪的是,用openssl查看它会改变密钥字节,让.NET Core 3能够理解。
导入失败的私钥代码(这是实际失败的密钥):
ecdsa = ECDsa.Create();
var pem = "MHYCAQEEH5t2Xlmsw5uqw3W9+/3nosFi6i3V901uW6ZzUpvVM0qgCgYIKoZIzj0DAQehRANCAASck2UuMxfyDYBdJC0mHNeToqMBhJuMZYSgkUNbK/xzD7e3cwr5okPx0pZdSMfDmyi1dBujtIIxFK9va1bdVAR9";
var derArray = Convert.FromBase64String(pem);
ecdsa.ImportECPrivateKey(derArray, out _);

使用ImportECPrivateKey调用时,在System.Security.Cryptography.EccKeyFormatHelper.FromECPrivateKey(ReadOnlyMemory`1 keyData, AlgorithmIdentifierAsn& algId, ECParameters& ret)内出现System.Security.Cryptography.CryptographicException : ASN1 corrupted data错误。

原始PEM文件如下:

$ cat private_key_cert_265.pem
-----BEGIN EC PRIVATE KEY-----
MHYCAQEEH5t2Xlmsw5uqw3W9+/3nosFi6i3V901uW6ZzUpvVM0qgCgYIKoZIzj0D
AQehRANCAASck2UuMxfyDYBdJC0mHNeToqMBhJuMZYSgkUNbK/xzD7e3cwr5okPx
0pZdSMfDmyi1dBujtIIxFK9va1bdVAR9
-----END EC PRIVATE KEY-----

OpenSSL将私钥转换为其他格式:

$ openssl ec -in private_key_cert_265.pem
read EC key
writing EC key
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIACbdl5ZrMObqsN1vfv956LBYuot1fdNblumc1Kb1TNKoAoGCCqGSM49
AwEHoUQDQgAEnJNlLjMX8g2AXSQtJhzXk6KjAYSbjGWEoJFDWyv8cw+3t3MK+aJD
8dKWXUjHw5sotXQbo7SCMRSvb2tW3VQEfQ==
-----END EC PRIVATE KEY-----

使用这种PEM文件格式,.NET Core 3可以导入私钥。
我的问题是:发生了什么事情?为什么openssl会将私钥更改为另一种格式(我如何知道哪种格式是哪种格式?),为什么.NET Core 3可以理解一种格式而不理解另一种格式?

并非所有的私钥都适用于每种加密算法。异常表示该密钥不适用于您正在使用的算法。发布的私钥看起来像是一个64位字符串,可能需要在使用之前解码为字节。 - jdweng
我将密钥以它们的Base64表示发布,以便于阅读。代码行var derArray = Convert.FromBase64String(pem); 将密钥转换为DER字节数组。 - RasmusW
密钥需要具有所需的长度和CRC。您的私钥未满足要求。因此,该密钥可能适用于不同的算法,或者您正在实现错误的算法。有很多选项。每次使用私钥时,每个消息都会使用不同的临时密钥,因此很难破解密钥。因此,我认为SSL密钥是从私钥生成的临时密钥(或公钥)。通常,临时密钥是从私钥和时间消息一起生成的。 - jdweng
2
@jdweng 您的评论完全没有抓住重点,这是发送方的编码错误。 - Maarten Bodewes
1个回答

3
初始密钥格式不正确。私有值S没有被正确地左填充。值S以ASN.1八位字节串中的无符号大端整数形式传输。根据定义,这个八位字节串(字节数组)必须与密钥大小相同。这意味着对于您使用的256位曲线(secp256r1,以不同的名称知名),它必须是32个字节。
因此,您的代码和.NET代码是正确的。OpenSSL似乎更加自由接受,但它按照应该进行的方式写出值S。
下面是初始私钥值,取自这里
SEQUENCE (4 elem)
  INTEGER 1
  OCTET STRING (31 byte) 9B765E59ACC39BAAC375BDFBFDE7A2C162EA2DD5F74D6E5BA673529BD5334A
  [0] (1 elem)
    OBJECT IDENTIFIER 1.2.840.10045.3.1.7 prime256v1 (ANSI X9.62 named elliptic curve)
  [1] (1 elem)
    BIT STRING (520 bit) 0000010010011100100100110110010100101110001100110001011111110010000011…

这是已经修正了 OpenSSL 的版本:

SEQUENCE (4 elem)
  INTEGER 1
  OCTET STRING (32 byte) 009B765E59ACC39BAAC375BDFBFDE7A2C162EA2DD5F74D6E5BA673529BD5334A
  [0] (1 elem)
    OBJECT IDENTIFIER 1.2.840.10045.3.1.7 prime256v1 (ANSI X9.62 named elliptic curve)
  [1] (1 elem)
    BIT STRING (520 bit) 0000010010011100100100110110010100101110001100110001011111110010000011…

请注意OCTET STRING值的大小和前导零。


当然,这个私钥现在已经被泄露了。请注意,两个00字节丢失的机会较小,或者是三个(等等)。


1
谢谢,就这些。RFC5915显示了如何计算字符串长度,并且有关此问题的一些讨论在openssl-dev邮件列表上。 给定p256曲线顺序的字符串长度此NIST文档第91页给出结果32(在Wolfram Alpha上计算)。 - RasmusW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接