c# JWT load ES256 PEM file into CngKey (jose-jwt)

2019-07-03 10:50发布

问题:

As a quick overview I am attempting to generate a ES256 algorithm -JWT token via C# using the https://github.com/dvsekhvalnov/jose-jwt library.

As the directions state:

ES256, ES384, ES256 ECDSA signatures requires CngKey (usually private) elliptic curve key of corresponding length. Normally existing CngKey loaded via CngKey.Open(..) method from Key Storage Provider. But if you want to use raw key material (x,y) and d, jose-jwt provides convenient helper EccKey.New(x,y,d).

The CngKey.Open() states it opens an existing key, but by the sounds of it I should be using the CngKey.Import() instead? When I attempt to call the CngKey.Import() it returns the following error:

The parameter is incorrect.

Basically what I am asking is what is the simplest way to convert an existing PEM file into the CngKey object which is required for the Jose.JWT.Encode() function? Any help would be highly appreciated. Thanks!

Below is my code(for security purposed that is not the real private key):

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:

I had the same problem with jose-jwt and got it working using my own implementation of GetECDsaPrivateKey(). Note that your project should target .NET 4.6.1. Please follow the steps below:

1.Generate a p12 X509Certificate2 using openssl

> 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.Generate a JWT by reading private key from above generated certificate:

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);
        }
}


标签: c# jwt