Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JwtSecurityTokenHandler.WriteToken doesn't work with windows certificate store certificate when marked as non-exportable #2985

Open
suntsu42 opened this issue Nov 7, 2024 · 0 comments

Comments

@suntsu42
Copy link

suntsu42 commented Nov 7, 2024

Which version of Microsoft.IdentityModel are you using?
System.IdentityModel.Tokens.Jwt 8.2.0 (dotnet 8)

Where is the issue?

  • [*] S.IM.Tokens.Jwt

Is this a new or an existing app?
New application. Same worked in .net 4.8

Repro
The following github repository can be used for reproducing this isuse: https://github.com/suntsu42/JwtDemoCertIssue

The following code fails with an ArgumentException "The CNG key handle being opened was detected to be ephemeral, but the EphemeralKey open option was not specified"

string? createdToken = tokenHandler.WriteToken(token);

This only throws an exception when the certificate is stored in the windows certificate store and is marked as non-exportable (Which means the private key cannot be exported).
Without this non-exportable constraint, the tokenHandler.WriteToken works and can create a signature.

The code did work in a previous version (Based on .net 4.8)

using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.Tokens;

internal class Program
{
    public static void Main(string[] args)
    {
        const string certName = "CN=CertificateName";
        using (var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))
        {
            certStore.Open(OpenFlags.ReadOnly);
            var jwtCerts = certStore.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, certName, false);
            if (jwtCerts.Count == 0)
                throw new Exception($"Certificate {certName} not found");
            if (jwtCerts.Count > 1)
                throw new Exception($"More than one Certificate with Subject {certName} found");

            X509Certificate2 jwtCert = jwtCerts[0];

            DateTime now = DateTime.Now;
            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
            var token = new JwtSecurityToken
            (
                $"MyIssuerName",
                "MyAudienceName",
                null,
                // valid for a timeframe of 20 minute (-10min/+10min)
                now.AddMinutes(-10),
                now.AddMinutes(+10),
                // the certificate to sign the token
                new MySigningCredentials(jwtCert)
            );

            // Create the token. 
            // Results in an exception when the certificate in the windows certificate store is marked as non-exportable
            // Exception message: The CNG key handle being opened was detected to be ephemeral, but the EphemeralKey open option was not specified
            string? createdToken = tokenHandler.WriteToken(token);
        }
    }

    /// <summary>
    /// Custom implementation of <see cref="SigningCredentials"/> to use an <see cref="X509Certificate2"/> for signing.
    /// Required since the constructor of <see cref="SigningCredentials"/> is protected.
    /// </summary>
    private sealed class MySigningCredentials : SigningCredentials
    {
        internal MySigningCredentials(X509Certificate2 certificate) : base(certificate)
        {
        }
    }
}

Expected behavior
tokenHandler.WriteToken should work with a certificate loaded from the windows certificate store even when the private key is marked as non-eportable

Actual behavior
tokenHandler.WriteToken fails with the error "The CNG key handle being opened was detected to be ephemeral, but the EphemeralKey open option was not specified"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant