【问题标题】:How to read ES512 private key in .NET Framework如何在 .NET Framework 中读取 ES512 私钥
【发布时间】: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
            }
        });
    }
}

【问题讨论】:

    标签: cryptography jwt .net-framework-version ecdsa


    【解决方案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)。

      【讨论】:

        猜你喜欢
        • 2010-09-19
        • 2013-07-13
        • 2022-06-20
        • 2016-12-07
        • 1970-01-01
        • 2018-09-30
        • 2012-08-01
        相关资源
        最近更新 更多