dvsekhvalnov / jose-jwt

Ultimate Javascript Object Signing and Encryption (JOSE), JSON Web Token (JWT) and Json Web Keys (JWK) Implementation for .NET and .NET Core
MIT License
921 stars 183 forks source link

JWT.Encode works fine on local machine in .NET7.0 Windows 11 , on windows server 2012 throw exception: Unable to sign content., inner exception: The requested operation is not supported.| #227

Open RafalSzczerba opened 1 year ago

RafalSzczerba commented 1 year ago

I'm using this packange in my project in .NET7.0 and is wokring fine on my local machine, but after deployment this on the server problem occures while calling method Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.ES256, headers, options: new JwtOptions {DetachPayload = true, EncodePayload = false }); where:

payload: jsonBody, priavteKey: is made from .pem and creation object with fullfilled following properites: new Jwk (crv, , x, y, d: ); I've compared content of payload, header, private key , algorithm, options and there are exactly the same in local machine and on the server.

The exception pop up: CreateJWSSignature: ex: Unable to sign content., inner exception: The requested operation is not supported.

Could you tell me if this method need to have some extra access to the some resources which I have on local machine but not available on the server? Or maybe you know how to fix this in the code? I'm using windows 11 on local machine, server is windows server 2012

dvsekhvalnov commented 1 year ago

Hi @RafalSzczerba ,

can you post full stack trace? And if you have code that showing how do you load your privateKey, can be helpful as well.

RafalSzczerba commented 1 year ago

Hello @dvsekhvalnov Full stack trace:

Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: The requested operation is not supported. at System.Security.Cryptography.ECCng.ImportKeyBlob(String blobType, ReadOnlySpan1 keyBlob, String curveName, SafeNCryptProviderHandle provider) at System.Security.Cryptography.CngKeyLite.ImportKeyBlob(String blobType, Byte[] keyBlob, String curveName) at System.Security.Cryptography.ECDsaImplementation.ECDsaCng.ImportKeyBlob(Byte[] ecKeyBlob, String curveName, Boolean includePrivateParameters) at System.Security.Cryptography.ECDsaImplementation.ECDsaCng.ImportParameters(ECParameters parameters) at Jose.Jwk.ECDsaKey() at Jose.netstandard1_4.EcdsaUsingSha.Sign(Byte[] securedInput, Object key) --- End of inner exception stack trace --- at Jose.netstandard1_4.EcdsaUsingSha.Sign(Byte[] securedInput, Object key) at Jose.JWT.EncodeBytes(Byte[] payload, Object key, JwsAlgorithm algorithm, IDictionary2 extraHeaders, JwtSettings settings, JwtOptions options) at Jose.JWT.Encode(String payload, Object key, JwsAlgorithm algorithm, IDictionary`2 extraHeaders, JwtSettings settings, JwtOptions options)

PrivateKey creation:

            var sPrivKey = File.ReadAllText(privateKeyFile);   // here is a EC Priave key in .pem format
            var pemReaderPriv = new Org.BouncyCastle.OpenSsl.PemReader(new StringReader(sPrivKey));
            var pemPriv = pemReaderPriv.ReadObject();
            ECPrivateKeyParameters privKeyParams;
            var keyPair = pemPriv as Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair;
            if (keyPair == null)
            {
                privKeyParams = pemPriv as ECPrivateKeyParameters;
            }
            else
            {
                privKeyParams = keyPair.Private as ECPrivateKeyParameters;
            }
            var xCord = privKeyParams.Parameters.G.XCoord.ToBigInteger().ToByteArray();
            var yCord = privKeyParams.Parameters.G.YCoord.ToBigInteger().ToByteArray();    
            var dCord = privKeyParams.D.ToByteArray();               
            var privateKey = new Jwk(
                crv: "P-256",
                x: Base64Url.Encode(xCord),
                y: Base64Url.Encode(yCord),
                d: Base64Url.Encode(dCord[0] == 0
                    ? dCord.Skip(1).ToArray()
                    : dCord)
            );

                                var token = Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.ES256, headers, options: new JwtOptions {DetachPayload = true, EncodePayload = false });
RafalSzczerba commented 1 year ago

Additionally I've found place where exception is made. It is in JWK.cs during attempt to import parameters: ecdsaKey.ImportParameters(param);

dvsekhvalnov commented 1 year ago

Hm.. interesting, let's try simple thing first:

Jwk eccKey = new Jwk(
    crv: "P-256",
    x: "BHId3zoDv6pDgOUh8rKdloUZ0YumRTcaVDCppUPoYgk",
    y: "g3QIDhaWEksYtZ9OWjNHn9a6-i_P9o5_NrdISP0VWDU",
    d: "KpTnMOHEpskXvuXHFCfiRtGUHUZ9Dq5CCcZQ-19rYs4"
);

will your code work with key above ^^ ?

Also, on .NET 5+ you can use built-in functions to read PEM files, see here: https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.ecalgorithm.importfrompem?view=net-7.0#system-security-cryptography-ecalgorithm-importfrompem(system-readonlyspan((system-char)))

you can probably do something like (no explicit need to convert to Jwk, library can take different key formats directly):

var eccPem = File.ReadAllText("my-key.pem");

var key = ECDsa.Create();
key.ImportFromPem(eccPem);

Jose.JWT.Encode(payload, key, ....);