使用以下代码,我可以生成椭圆曲线数字签名算法(ECDSA)参数(即公钥和私钥)。
using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP384);
var publicKey = ecdsa.ExportParameters(false);
var privateKey = ecdsa.ExportParameters(true);
此方法每次都会生成新的密钥,这对于许多应用程序来说是正确的选择。但是,在我的应用程序中,我需要能够修复随机种子以获得可重现的密钥。
我有两个要求:
关于如何生成可复制密钥有什么建议吗?
原始 EC 私钥是随机字节序列,例如对于由 48 个字节组成的 P-384。与对称密钥的密钥派生类似,密钥派生函数可用于根据给定的密钥材料确定相同的原始私钥。
如果密钥材料是熵不足的密码,则应将基于密码的密钥派生函数(例如 PBKDF2)与(非秘密)随机盐结合使用。然后可以使用密码和盐重建原始私钥。
如果密钥材料具有足够的熵,则可以使用基于哈希的密钥派生函数 (HKDF)(请参阅RFC 5869)。 NET 从 .NET 5 开始为此提供了实现,请参阅
HKDF
。
公钥(或相应的
ECPoint
)是通过将原始私钥乘以相应曲线的生成点而获得的。但是,没有必要直接确定公钥,因为 .NET 在幕后隐式确定它(如果由于某种原因需要直接确定:据我所知,.NET 不公开模块化算术的公共方法,这意味着必须使用其他库(例如 BouncyCastle)来直接从原始私钥确定原始公钥)。
根据问题,由于密钥材料具有足够的熵,因此以下是 HKDF 的示例实现。这不需要任何额外的库并且可以跨平台工作:
private static ECDsa CreateKeyViaHKDF(byte[] keyMaterial, ECCurve ecCurve, int size)
{
ECParameters ecp = new ECParameters();
ecp.Curve = ecCurve;
ecp.D = HKDF.DeriveKey(HashAlgorithmName.SHA512, keyMaterial, size, null, null);
return ECDsa.Create(ecp);
}
测试:
using System;
using System.Security.Cryptography;
...
byte[] keyMaterial = Convert.FromHexString("d25a4ea14eb6ad223393fd84ab59a62ea9740057f1249ebd65074866e2192af77ca6f78bae94c329b8adaf5d344f1c55c6d20f04abe7de53b08d93e89d8820ae"); // keyMaterial with sufficient entropy
ECDsa key = CreateKeyViaHKDF(keyMaterial, ECCurve.NamedCurves.nistP384, 48);
// Export raw private/public key
ECParameters privateKey = key.ExportParameters(true);
ECParameters publicKey = key.ExportParameters(false);
Console.WriteLine("Private key (raw, Base64): " + Convert.ToBase64String(privateKey.D));
Console.WriteLine("Public key, X (raw, Base64): " + Convert.ToBase64String(publicKey.Q.X));
Console.WriteLine("Public key, Y (raw, Base64): " + Convert.ToBase64String(publicKey.Q.Y));
// Export keys in PKCS#8 (private key) and SPKI (public key) format
Console.WriteLine("\nPrivate key (PKCS#8): " + key.ExportPkcs8PrivateKeyPem());
Console.WriteLine("\nPublic key (SPKI): " + key.ExportSubjectPublicKeyInfoPem());