C# JWT:如何将 ES256 PEM 文件加载到 CngKey 中(jose-jwt)

6
作为一个快速概述,我正在尝试使用 https://github.com/dvsekhvalnov/jose-jwt 库通过 C# 生成 ES256 算法 -JWT 令牌。
根据说明:
ES256、ES384、ES256 ECDSA 签名需要对应长度的 CngKey(通常为私有)椭圆曲线密钥。通常,通过 CngKey.Open(..) 方法从密钥存储提供程序加载现有的 CngKey。但是,如果您想使用原始密钥材料 (x,y) 和 d,则 jose-jwt 提供了方便的 EccKey.New(x,y,d) 帮助程序。
CngKey.Open() 表示它打开现有密钥,但听起来我应该使用 CngKey.Import()?当我尝试调用 CngKey.Import() 时,它返回以下错误:
参数不正确。
基本上,我想问的是将现有的 PEM 文件转换为 Jose.JWT.Encode() 函数所需的 CngKey 对象的最简单方法是什么?任何帮助都将不胜感激。谢谢!
以下是我的代码(出于安全考虑,这不是真正的私钥):
public string GenerateToken(int contactID, Database _db)
        {
            var contact = GetContact(contactID, _db);
            var payload = new Dictionary<string, object>()
            {
                {"broker", 1},
                {"contact_id", contact.id},
                {"name", contact.fname + " " + contact.lname + ""},
                {"iss", "www.somewhere.com"},
                {"iat", (DateTime.Now - UnixEpoch).TotalSeconds},
                {"nbf", (DateTime.Now - UnixEpoch).TotalSeconds},
                {"exp", (DateTime.Now.AddDays(30) - UnixEpoch).TotalSeconds}
            };    

            string privateKey =
            "MHcCAQEffEIIIHHHHHHHHHHHHHHHffHHHHHHHHHHHHHHHHHHHHHHHoGgCCqGSM49" +
            "AwEHhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhI+pRkAxAb13" +
            "77vz2Yjjjjjjjjjjjjjjjjjjjjw==";
            byte[] b = Convert.FromBase64String(privateKey);

            CngKey cng = CngKey.Import(b, CngKeyBlobFormat.EccPrivateBlob);
            string token = Jose.JWT.Encode(payload, cng, JwsAlgorithm.ES256);
            return token;
        }
1个回答

3

我曾经遇到过与 jose-jwt 相关的问题,通过自己实现 GetECDsaPrivateKey() 方法解决了这个问题。请注意,你的项目应该针对.NET 4.6.1进行。请按照以下步骤操作:

1.使用 openssl 生成一个 p12 X509Certificate2

> openssl ecparam -name prime256v1 -genkey > private-key.pem
> openssl ec -in private-key.pem -pubout -out public-key.pem
> openssl req -new -key private-key.pem -x509 -nodes -days 365 -out public.cer
> winpty openssl pkcs12 -export -in public.cer -inkey private-key.pem -out publiccert.p12

2. 通过读取上述生成的证书中的私钥生成一个JWT

var claims = new Dictionary<string, object>()
{
    { "sub", "mr.x@contoso.com" },
    { "exp", 1300819380 }
};

var certificate = new X509Certificate2("publiccert.p12", "passcode");
string token = SignJWTWithCert(certificate, claims);

private static string SignJWTWithCert(X509Certificate2 cert, object claims)
{
        var header = new { alg = "ES256", typ = "JWT" };
        byte[] headerBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header, Formatting.None));
        byte[] claimsBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(claims, Formatting.None));

        using (ECDsa ecdsa = cert.GetECDsaPrivateKey())
        {
            if (ecdsa == null)
                throw new ArgumentException("Cert must have an ECDSA private key", nameof(cert));

            var payload = Base64UrlEncode(headerBytes) + "." + Base64UrlEncode(claimsBytes);
            var signature = ecdsa.SignData(Encoding.UTF8.GetBytes(payload), HashAlgorithmName.SHA256);
            return payload + "." + Base64UrlEncode(signature);
        }
}

这似乎非常有帮助,假设它能正常工作,但是能否分享一下 Base64UrlEncode() 到底是做什么的?首先编码为 base64 然后进行 url 编码?还是相反的顺序? - user2173353
另外,指数(上面字典键“exp”)从哪里获取?有没有办法使用生成的证书找到它? - user2173353
1
exp 是过期时间(Unix 时间戳)。您可以使用自己选择的编程语言来生成它。至于 Base64UrlEncode(),可以查看 https://dev59.com/04rda4cB1Zd3GeqPR922#30246618。 - Shawinder Sekhon
一个巧妙的方法将 OpenSSL 生成的 PEM 密钥导入到 .NET 环境中——正是我所需要的。 - sun2sirius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接