1

I am trying to generate a client assertion from a X509Certificate2 (here is the documentation). The fact is that my code is working perfectly when I get the certificate from a file as follows :

string signinCertPath = Path.Combine(Directory.GetCurrentDirectory(), "Cert", "anAwesomeCertificate.pfx");

X509Certificate2 certificate = new X509Certificate2(signinCertPath, "MySuperCertificatePassword", X509KeyStorageFlags.EphemeralKeySet);

string clientAssertion = GenerateClientAssertion(certificate, tenantId, clientId); //clientAssertion handles the value I am looking for.

Here is the GenerateClientAssertion method, that I've copied from the MSAL documentation :

private string GenerateClientAssertion(X509Certificate2 certificate, string tenantId, string clientId)
{
    // Get the RSA with the private key, used for signing.
    var rsa = certificate.GetRSAPrivateKey();

    //alg represents the desired signing algorithm, which is SHA-256 in this case
    //x5t represents the certificate thumbprint base64 url encoded
    var header = new Dictionary<string, string>()
    {
        { "alg", "RS256"},
        { "typ", "JWT" },
        { "x5t", Base64UrlEncode(certificate.GetCertHash()) }
    };

    var claims = GetClaims(tenantId, clientId);

    var headerBytes = JsonConvert.SerializeObject(header);
    var claimsBytes = JsonConvert.SerializeObject(claims);
    string token = Base64UrlEncode(Encoding.UTF8.GetBytes(Newtonsoft.Json.Linq.JObject.FromObject(header).ToString())) + "." + Base64UrlEncode(Encoding.UTF8.GetBytes(Newtonsoft.Json.Linq.JObject.FromObject(claims).ToString()));

    string signature = Base64UrlEncode(rsa.SignData(Encoding.UTF8.GetBytes(token), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
    string signedClientAssertion = string.Concat(token, ".", signature);
    return signedClientAssertion;
}

The problem is that I am not able to access the certificate file in real conditions, that is why I copied it in a byte[] and try to do the same thing by creating the certificate with the byte[], like that :

X509Certificate2 certificate = new X509Certificate2(variables.cert, "MySuperCertificatePassword", X509KeyStorageFlags.EphemeralKeySet); //variables.cert is the byte[]

string clientAssertion = GenerateClientAssertion(certificate, tenantId, clientId);

And here comes the issue : my code throw me an error as the rsa variable is not set to an instance of an object, meaning that I am not getting the certificate private key.

So does anybody know how I can get this private key?

Any contribution would be nice. Thanks by advance.

  • What version of Net was the documentation using? You may need to target another version of net to get RSA to work since it is obsolete. Here is a similar issue posted this morning : https://stackoverflow.com/questions/71233045/certificate-recreation-after-upgrading-to-net-framework-4-8?force_isolation=true#comment125915568_71233045 – jdweng Feb 23 '22 at 12:32
  • @jdweng The RSA class is certainly not obsolete. Neither is the GetRSAPrivateKey method. – bartonjs Feb 23 '22 at 16:25
  • Are you sure the certificate and associated private key are RSA? If the public key (and associated private key) are ECC, then GetRSAPrivateKey() will return null, and you want GetECDsaPrivateKey(). – bartonjs Feb 23 '22 at 16:26
  • @bartonjs : it is the type of RSA I meant. The X509Certificate and X509Certificate2 in Net are using different encryption algorithms and may not be compatible. Also see the RSA version in following. The ones mark no are no longer recommended since hackers found way to break : https://en.wikipedia.org/wiki/Transport_Layer_Security?force_isolation=true – jdweng Feb 23 '22 at 16:39

0 Answers0