在 .NET 中加载椭圆曲线 (EC) 密钥

在 .NET 中加载椭圆曲线 (EC) 密钥

本文将讨论在.NET 中解析和加载EC 密钥的不同方式。在本文中,您将了解 EC 密钥的构成,然后了解如何以四种不同的方式使用这些知识ECDsa在 .NET 中加载或创建对象。到本文结束时,您应该能够加载 EC 密钥,无论格式如何。

本文针对 .NET Core 3.1 及更高版本。

椭圆曲线 (EC) 密钥的参数

加载 EC 密钥时,您将需要了解三件事:

1.私钥(可选)2.公钥3.您需要使用的曲线

私钥d是一个随机整数,其长度取决于您使用的曲线。如果你有私钥,你就可以对数据进行签名。

公钥是椭圆曲线上的一个点,使用坐标x&y。因此,公钥特定于该曲线,并使用私钥乘以曲线上的生成点G生成。使用公钥,您将能够验证签名。

这表明您需要了解正在使用的椭圆曲线(例如,NIST 的 P-256[1]或secp256k1[2])才能使用椭圆曲线加密 (ECC)。如果不知道曲线,您将不知道私钥应该有多长、提供的安全级别或公钥是否有效(如果它们是曲线上的点)。

如果您想了解有关椭圆曲线密码学工作原理的更多信息,我发现开发人员实用密码学[3]是一本有用的读物。

.NET 的ECDsa对象

.NET 对 ECDSA 的抽象是ECDsaSystem.Security.Cryptography. 这继承AsymmetricAlgorithm并遵循RSA您之前可能使用过的类的类似模式。 ECDsa是一个抽象类,具有使用不同实现的各种平台(例如,Windows 上的 CNG 和 Linux 上的 OpenSSL)。

此类的主要用途是调用SignDataVerifyData方法。否则,您可能会将其传递到SecurityKey 实现[4]或 SignedXml。

本文的其余部分将重点介绍如何创建ECDsa实现。

使用ECDsa和命名曲线生成 EC 密钥

创建新 EC 密钥的最简单方法是使用 .NETCreate上的方法让 .NET 为您完成ECDsa。这将在您选择的曲线上创建一个私钥。您通常会使用已知的命名曲线,例如 NIST 的 P-256,又名 secp256r1。您可以在ECCurve.NamedCurve课程中找到 .NET 支持的开箱即用曲线。

ECDsa key = ECDsa.Create(ECCurve.NamedCurves.nistP256);

这种方法非常适合在测试期间即时生成密钥,但是一旦对象被处置,您就会丢失密钥。幸运的是,您可以使用各种导出方法ECDsa;否则,您可以使用该ExportParameters方法直接访问 EC 参数。这些导出方法使您可以访问ECParameters包含关键点曲线、D 和 Q ( x & y ) 点的对象,您将在接下来的操作中看到这些点。

创建ECDSA使用为ECParameters

假设您已经收到曲线的原始点,并且您需要以这种方式加载密钥。这在使用 JSON Web Keys (JWK) 和OpenID Connect[5]时很常见。

这是来自 RFC 7517 的 JWK 示例:

{
  "kty": "EC",
  "crv": "P-256",
  "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
  "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0",
  "kid": "my EC key"
}

要加载密钥,您需要知道密钥使用什么曲线以及公钥参数(x和y坐标)。也可以使用d参数将私钥包含在 JWK 中;然而,这种情况很少见。

您可以再次使用ECDsaCreate方法加载这些坐标,但这次您将传入一些ECParameters.

// parse curve from JOSE format
// https://www.iana.org/assignments/jose/jose.xhtml#web-key-elliptic-curve
var curve = crv switch
{
    "P-256" => ECCurve.NamedCurves.nistP256,
    "P-384" => ECCurve.NamedCurves.nistP384,
    "P-521" => ECCurve.NamedCurves.nistP521,
    _ => throw new NotSupportedException()
};


ECDsa key = ECDsa.Create(new ECParameters
{
    Curve = curve,
    D = Base64UrlEncoder.DecodeBytes(d), // optional
    Q = new ECPoint
    {
        X = Base64UrlEncoder.DecodeBytes(x),
        Y = Base64UrlEncoder.DecodeBytes(y)
    }
});

在这里,您正在解析 JOSE 名称中的曲线,并使用 中找到的编码器对其他参数进行 base64url 解码Microsoft.IdentityModel.Tokens

此解析特定于 JWK 使用的格式,但它演示了如何在知道其参数的情况下加载 EC 密钥。

有一种Validate方法ECParameters可以检查关键和坐标大小并检查曲线是否有效。但是,Createon的方法ECDsa会为您调用它。

如果您确实有 JWK 并打算使用它来验证 JWT,我建议将 JWK 直接传递到构造函数中Microsoft.IdentityModel.Tokens.JsonWebKey并跳过我在上面演示的手动解析。

从X509Certificate2加载ECDsa

从 an 加载 EC 密钥X509Certificate2是简单使用GetECDsaPrivateKeyGetECDsaPublicKey方法的一种情况。

ECDsa key = cert.HasPrivateKey ? cert.GetECDsaPrivateKey() : cert.GetECDsaPublicKey();

为了首先加载X509Certificate2,您可以从 PEM 文件加载它[6]

X509Certificate2.PrivateKey与GetRSAPrivateKey和GetECDsaPrivateKey

过去,您可能在 上使用了PrivateKeyPublicKey属性X509Certificate2。但是,如果您尝试在包含 EC 密钥的证书上使用这些属性,您将获得 null forPrivateKey和以下异常PublicKey

System.NotSupportedException: 不支持证书密钥算法。
        在 System.Security.Cryptography.X509Certificates.PublicKey.get_Key()

要使用来自 X.509 的 EC 密钥,您需要使用GetECDsaPrivateKeyGetECDsaPublicKey方法。

从 .NET 6 开始,PrivateKeyPublicKey属性被标记为过时。因此,您必须使用RSAECDsa特定方法来全面加载密钥。

从十六进制字符串加载ECDsa

有时,您会看到 EC 密钥作为十六进制字符串共享。这在加密社区中很常见,我的意思是密码学和加密货币。我不能说它在密码学中是否有很大用处。这些键看起来像这样:

Private: c711e5080f2b58260fe19741a7913e8301c1128ec8e80b8009406e5047e6e1ef
Public: 04e33993f0210a4973a94c26667007d1b56fe886e8b3c2afdd66aa9e4937478ad20acfbdc666e3cec3510ce85d40365fc2045e5adb7e675198cf57c6638efa1bdb

这些键起初可能看起来有点奇怪,但它们实际上映射回您已经使用的参数。私钥只是d参数的十六进制表示,公钥是连接在一起的x &y坐标(第一个字节是标签)。

因此,您可以再次使用ECParameters来创建您的ECDsa对象:

public static ECDsa LoadFromHex()
{
    var privateKeyBytes = FromHexString("c711e5080f2b58260fe19741a7913e8301c1128ec8e80b8009406e5047e6e1ef");
    var publicKeyBytes = FromHexString("04e33993f0210a4973a94c26667007d1b56fe886e8b3c2afdd66aa9e4937478ad20acfbdc666e3cec3510ce85d40365fc2045e5adb7e675198cf57c6638efa1bdb");


    return ECDsa.Create(new ECParameters
    {
        Curve = ECCurve.NamedCurves.nistP256, // you'd need to know the curve before hand
        D = privateKeyBytes,
        Q = new ECPoint
        {
            X = publicKeyBytes.Skip(1).Take(32).ToArray(),
            Y = publicKeyBytes.Skip(33).ToArray()
        }
    });
}


private static byte[] FromHexString(string hex) {
    var numberChars = hex.Length;
    var hexAsBytes = new byte[numberChars / 2];
    for (var i = 0; i < numberChars; i += 2)
        hexAsBytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);


    return hexAsBytes;
}

源代码

您可以在我的GitHub 示例存储库中[7]找到演示上述方法的可执行代码。此代码是多目标 .NET Core 3.1 及以上版本,可在 Windows 和 Linux 上运行。

查看原文:https://www.scottbrady91.com/c-sharp/ecdsa-key-loading

References

[1] NIST 的 P-256: https://www.scottbrady91.com/C-Sharp/JWT-Signing-using-ECDSA-in-dotnet-Core
[2] secp256k1: https://www.scottbrady91.com/C-Sharp/Supporting-Custom-JWT-Signing-Algorithms-in-dotnet-Core
[3] 开发人员实用密码学: https://cryptobook.nakov.com/asymmetric-key-ciphers/elliptic-curve-cryptography-ecc
[4] SecurityKey 实现: https://www.scottbrady91.com/C-Sharp/JWT-Signing-using-ECDSA-in-dotnet-Core
[5] OpenID Connect: https://www.scottbrady91.com/OpenID-Connect/OpenID-Connect-Overview
[6] 从 PEM 文件加载它: https://www.scottbrady91.com/C-Sharp/PEM-Loading-in-dotnet-core-and-dotnet
[7] GitHub 示例存储库中: https://github.com/scottbrady91/Blog-Example-Classes/tree/master/EcdsaKeyLoading

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值