如何在 C# 上读取 .Key 文件?

Kow*_*own 1 c# file file-read pem der

我阅读了 .der 文件如下。

byte[] byteKey = File.ReadAllBytes(openFileDialog1.FileName);
X509Certificate2 cert = new X509Certificate2(byteKey);
Run Code Online (Sandbox Code Playgroud)

但它没有私钥。它只有公钥。

cert.HasPrivateKey 返回 false。

当我搜索它时,我发现“.der 文件没有私钥,私钥在 .key 文件中”。

我使用记事本++在与.der文件相同的路径中打开一个.key文件,将打印损坏的文本。

第一个问题,如何从 C# 上的 .key 文件中读取私钥?

其次,如何在 C# 上将 .key 文件转换为 .pem 文件?它只是使用openssl吗?

我会很感激你的教导。

bar*_*njs 7

当前版本的 .NET 在这里没有很好的故事。.NET Core 3.0 有更好的故事。如果您愿意使用名称中带有“Experimental”的 NuGet 包,那就更好了。

注意:在整个答案中,我不会考虑像 BouncyCastle 这样的第三方库。它可能完全符合您的要求,但这不是我的专业领域。当我知道 NuGet 包时,我将考虑由同一组人开发的 NuGet 包作为 .NET 收件箱库的工作人员。

解释 1:“我调用哪个方法来从文件加载私钥?”

当前版本

没有解决方案。

.NET 核心 3.0

没有唯一的答案,您需要知道您拥有什么样的文件(或者只是尝试所有答案)。

  • RSA
    • 导入RSA私钥
      • 当数据采用 PKCS#1 RSAPrivateKey 格式时(PEM 开放标头:BEGIN RSA PRIVATE KEY)
    • ImportPkcs8PrivateKey
      • 当数据采用 PKCS#8 PrivateKeyInfo 格式时(PEM 开放标头:BEGIN PRIVATE KEY)
    • ImportEncryptedPkcs8PrivateKey
      • 当数据采用 PKCS#8 EncryptedPrivateKeyInfo 格式时(PEM 开放标头:BEGIN ENCRYPTED PRIVATE KEY)
  • ECDSA
    • 导入EC私钥
      • 当数据采用 RFC 5915 ECPrivateKey 格式时(PEM 开放标头:BEGIN EC PRIVATE KEY)
    • ImportPkcs8PrivateKey
    • ImportEncryptedPkcs8PrivateKey
  • ECDiffieHellman
    • 导入EC私钥
    • ImportPkcs8PrivateKey
    • ImportEncryptedPkcs8PrivateKey
  • 动态安全协议
    • ImportPkcs8PrivateKey
    • ImportEncryptedPkcs8PrivateKey

对这些方法的警告是它们只能理解 BER/DER 数据,而不是 PEM 数据。因此,如果您的文件是 PEM 格式(这样可以最容易地确定有效负载应该是什么),您首先需要将其转换为 BER/DER。

对于大多数 PEM 文件来说,这很简单:您只需找到 BEGIN 和 END 标记之间的内容,通过 Convert.FromBase64String 运行它,瞧。从技术上讲,PEM 支持属性,处理这些属性更难(并且超出了本答案的范围)。

所以,你最终可能会得到类似的东西

RSA rsa = RSA.Create();

try
{
    rsa.ImportRSAPrivateKey(data, out _);
    return rsa;
}
catch (CryptographicException)
{
}

try
{
    rsa.ImportPkcs8PrivateKey(data, out _);
    return rsa;
}
catch (CryptographicException)
{
}

try
{
    // prompt for password, then
    rsa.ImportEncryptedPkcs8PrivateKey(password, data, out _);
    return rsa;
}
catch (CryptographicException)
{
}

rsa.Dispose();
ECDsa ecdsa = ECDsa.Create();
...
Run Code Online (Sandbox Code Playgroud)

忽略的out值是输入字节中使用的字节数。它主要仅在从文件中间读取时才相关。

System.Security.Cryptography.Asn1.Experimental

没有解决方案,这个库比那个低得多。

解读二:“实践中如何理解这些文件?”

好的,这不是真正解释问题的方式,而是转场。

加密密钥文件总是(根据我的经验)DER-(虽然偶尔放松到 BER-)编码的 ASN.1 数据结构。要完全理解它们,您需要阅读并理解

  • ITU-T REC X.680:ASN.1 语言
  • ITU-T REC X.690 ASN.1 数据的基本编码规则 (BER) 字节布局(以及很少使用的限制规范编码规则 (CER) 和常用限制可分辨编码规则 (DER))。
  • 任何描述特定格式的事物,以及它们可能引用的事物。
    • RSAPrivateKey:公钥加密标准#1 (PKCS#1) 或RFC 8017
    • ECPrivateKey:RFC 5915
    • PKCS#8 私钥信息:PKCS#8 / RFC 5208
    • PKCS#8 EncryptedPrivateKeyInfo:PKCS#8 / RFC 5208(和 PKCS#5,至少,作为依赖项)

这些结构有时会使用隐私增强邮件 (PEM) 语法转换为文本表示,这本质上是

  • 5 个连字符减号
  • 全大写BEGIN后跟一个空格
  • 格式标识符,不以空格结尾
  • 5 个连字符减号
  • 换行符(CRLF 或 LF)
  • BER/DER 数据的 base64 编码版本,每行包含 64 个字符
  • base64 数据最后一部分末尾的换行符(CRLF 或 LF)
  • 5 个连字符减号
  • 全部大写 END后跟一个空格
  • BEGIN 中使用的相同格式标识符
  • 5 个连字符减号
  • (理想情况下是换行符或只是文件结尾)

请参阅RFC 7468更多信息,。

解读3:“如何在代码中读取这些文件的部分?”

当前版本

没有解决方案。

.NET 核心 3.0

没有解决方案。

System.Security.Cryptography.Asn1.Experimental

这个 NuGet 包是 .NET Core 2.1 / 3.0 中公开的 ASN.1 阅读器(其想法是在一些可用性反馈后从 .NET Core 公开)。

要读取 RSAPrivateKey,例如:

// PKCS#1 doesn't say that this structure is always DER encoded, so read it as BER
AsnReader reader = new AsnReader(data, AsnEncodingRules.BER);

// RSAPrivateKey ::= SEQUENCE {
AsnReader contents = reader.ReadSequence();

// version Version (0 for two-prime RSA)
if (!contents.TryReadInt32(out int version) || version != 0)
{
    throw new CryptographicException();
}

// modulus INTEGER,
BigInteger modulus = contents.ReadInteger();
// publicExponent INTEGER,
BigInteger publicExponent = contents.ReadInteger();
// privateExponent INTEGER,
BigInteger privateExponent = contents.ReadInteger();
// prime1 INTEGER,
BigInteger prime1 = contents.ReadInteger();
// prime2 INTEGER,
BigInteger prime2 = contents.ReadInteger();
// exponent1 INTEGER,
BigInteger exponent1 = contents.ReadInteger();
// exponent2 INTEGER,
BigInteger exponent2 = contents.ReadInteger();
// coefficient INTEGER,
BigInteger coefficient = contents.ReadInteger();
// otherPrimeInfos OtherPrimeInfos OPTIONAL,
// we don't support this, we limited to version 0.
// good thing the next token is:
// }
contents.ThrowIfNotEmpty();
// All done.
// If you expected no trailing data:
reader.ThrowIfNotEmpty();
Run Code Online (Sandbox Code Playgroud)

其他格式类似。