如何在 .NET Framework 中读取 ES512 私钥
Posted
技术标签:
【中文标题】如何在 .NET Framework 中读取 ES512 私钥【英文标题】:How to read ES512 private key in .NET Framework 【发布时间】:2020-11-15 12:07:35 【问题描述】:我正在尝试读取 ES512 私钥以在 .NET Framework 4.7.2 中创建 JWT 令牌,但在创建 ECDsa 对象时它会引发验证异常。
System.Security.Cryptography.CryptographicException: 'The specified key parameters are not valid. Q.X and Q.Y are required fields. Q.X, Q.Y must be the same length. If D is specified it must be the same length as Q.X and Q.Y for named curves or the same length as Order for explicit curves.'
这里有什么问题?我检查了互联网,找不到任何解决方案。在 NET Core 3 中,它使用 ImportECPrivateKey 方法,但我不知道如何在我需要的 .NET Framework 中执行此操作。
class Program
static string privateKey = @"MIHcAgEBBEIBiyAa7aRHFDCh2qga9sTUGINE5jHAFnmM8xWeT/uni5I4tNqhV5Xx0pDrmCV9mbroFtfEa0XVfKuMAxxfZ6LM/yKgBwYFK4EEACOhgYkDgYYABAGBzgdnP798FsLuWYTDDQA7c0r3BVk8NnRUSexpQUsRilPNv3SchO0lRw9Ru86x1khnVDx+duq4BiDFcvlSAcyjLACJvjvoyTLJiA+TQFdmrearjMiZNE25pT2yWP1NUndJxPcvVtfBW48kPOmvkY4WlqP5bAwCXwbsKrCgk6xbsp12ew==";
static void Main(string[] args)
var derArray = Convert.FromBase64String(privateKey);
LoadPrivateKey(derArray);
private static ECDsa LoadPrivateKey(byte[] key)
var privKeyInt = new Org.BouncyCastle.Math.BigInteger(+1, key);
var parameters = SecNamedCurves.GetByName("secp521r1");
var ecPoint = parameters.G.Multiply(privKeyInt);
var privKeyX = ecPoint.Normalize().XCoord.ToBigInteger().ToByteArrayUnsigned();
var privKeyY = ecPoint.Normalize().YCoord.ToBigInteger().ToByteArrayUnsigned();
return ECDsa.Create(new ECParameters
Curve = ECCurve.NamedCurves.nistP521,
D = privKeyInt.ToByteArrayUnsigned(),
Q = new ECPoint
X = privKeyX,
Y = privKeyY
);
【问题讨论】:
【参考方案1】:虽然 .NET Core 直接支持导入 PKCS#1、PKCS#8、SEC1 和 X.509/SPKI 等密钥格式,但 .NET Framework 并非如此。这是使用BouncyCastle 的一种方法。例如。对于已发布的SEC1 键:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
...
string privateKey = @"-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIBiyAa7aRHFDCh2qga9sTUGINE5jHAFnmM8xWeT/uni5I4tNqhV5Xx
0pDrmCV9mbroFtfEa0XVfKuMAxxfZ6LM/yKgBwYFK4EEACOhgYkDgYYABAGBzgdn
P798FsLuWYTDDQA7c0r3BVk8NnRUSexpQUsRilPNv3SchO0lRw9Ru86x1khnVDx+
duq4BiDFcvlSAcyjLACJvjvoyTLJiA+TQFdmrearjMiZNE25pT2yWP1NUndJxPcv
VtfBW48kPOmvkY4WlqP5bAwCXwbsKrCgk6xbsp12ew==
-----END EC PRIVATE KEY-----";
PemReader pemReader = new PemReader(new StringReader(privateKey));
AsymmetricCipherKeyPair ecKeyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
ECPrivateKeyParameters ecPrivateKeyParams = (ECPrivateKeyParameters)ecKeyPair.Private;
ECPublicKeyParameters ecPublicKeyParams = (ECPublicKeyParameters)ecKeyPair.Public;
BouncyCastle 的SignerUtilities
类支持签名/验证,例如:
byte[] message = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
ISigner signer = SignerUtilities.GetSigner("SHA-512withECDSA");
signer.Init(true, ecPrivateKeyParams);
signer.BlockUpdate(message, 0, message.Length);
byte[] signature = signer.GenerateSignature();
生成的签名以 ASN.1 格式返回。较新的 BC 版本还支持 SHA-512withPLAIN-ECDSA
,它以 r|s 格式返回签名。这在 JWT 的上下文中很有趣,因为 JWT 使用 r|s 格式。
或者,ECDsa
实例可以应用于签名/验证(正如问题中发布的代码中所意图的那样):
ECDsa ecdsa = ECDsa.Create(ecParams);
Create()
方法需要一个 System.Security.Cryptography.ECParameters
实例。后者可以派生自ECPrivateKeyParameters
/ECPublicKeyParameters
实例,在this SO answer中有详细介绍。
【讨论】:
【参考方案2】:这是一个 PKCS#8 / DER 编码的私钥。 DER 编码不仅仅包含私有元素。在这种情况下,它还有一个版本号、曲线的名称以及可能是 EC 公共点。您可以查看内容here on Lapo.it。重新计算公钥相对较快,因此您可以不用可选的公钥。
要对其进行解码,您需要专门的类,例如 Pkcs8PrivateKeyInfo
。之后你可以extract the private key bytes from the class。请注意,它是一个 big endian 编码的数字,因此您的部分代码需要保持原样。
实际上,将私钥作为字节,我认为整个 LoadPrivateKey
仍然是正确的(除了名称,当我在名称中看到 load
时,我希望有某种 IO)。
【讨论】:
以上是关于如何在 .NET Framework 中读取 ES512 私钥的主要内容,如果未能解决你的问题,请参考以下文章