jwt-dotnet / jwt

Jwt.Net, a JWT (JSON Web Token) implementation for .NET
Other
2.14k stars 462 forks source link

How to create a certificate from a private key #447

Closed BabyWhite69 closed 1 year ago

BabyWhite69 commented 2 years ago

Overview of what I am trying to do:

I have a c# server that is trying to communicate with an API written with Node.js through a token with RSA SHA256.

In order for the API to receive the token to me, the c# server has to create a token with the private key using the RS256 algorithm.

Now the API will validate the token with the public key and give it access to the CRUD.

Key creation:

Create a pair of keys, one public and one private with the following command:

ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key

And replace the public key in PEM format with this command:

openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub

The private key I passed it to the Unity editor which I accessed with a script and passed it to String format which gave me an error cannot convert from 'string' to 'System.Security.Cryptography.RSA', I read this post on GitHub which had the same error, but now it is giving me an exception to access this key created following the steps in the post mentioned above.

using JWT.Algorithms;
using JWT.Builder;
using System;
using UnityEngine;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;

public class JWT_Test : MonoBehaviour
{
    public const string ServerRsaPublicKey2 = "MIIDfDCCAmSgAwIBAgIQQDCxkdjCQqmQsnSLtcHj3TANBgkqhkiG9w0BAQsFADA7MQswCQYDVQQGEwJ1czELMAkGA1UECBMCVVMxETAPBgNVBAoTCENlcnR0ZXN0MQwwCgYDVQQDEwNqd3QwHhcNMjAwMzIzMDI1NDAzWhcNMjMwMzIzMDMwNDAzWjA7MQswCQYDVQQGEwJ1czELMAkGA1UECBMCVVMxETAPBgNVBAoTCENlcnR0ZXN0MQwwCgYDVQQDEwNqd3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5hM+0cIjO0oLcxQPGdnSS0ZVJDSNVsPmtiXimSLhEIPczbZ35OSWa9PI+PRIztr/yjtwjTlCES4EjyEoJ8LYIQmGVLdYV5ULS/CyXVpgWpDdiSv6QOwB2qMv3mKiPcmaKxy+oo4zfihBqGkCC6QnspyvUFPZiWTx86Apw3u3WqBRE3HQ+PjMnjDSnWdPaAsb75ti61RU+9qYj3BwxDJR6xnAaYz1RSkxHOw4+Ty+/tNtObrZmTH7msVRpV7kMU1QgyD3Y2/JTTf3YUU0LCm1J+WJ0cMbVrILAvVlOQnRn3IlcI1LOL/e6XEyET5tVymv8S5EoJjGf2o8VnTsF3vttAgMBAAGjfDB6MA4GA1UdDwEB/wQEAwIFoDAJBgNVHRMEAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAfBgNVHSMEGDAWgBTTMvXgytSFWwQk58CpxCpZAr5G1jAdBgNVHQ4EFgQU0zL14MrUhVsEJOfAqcQqWQK+RtYwDQYJKoZIhvcNAQELBQADggEBAK5vSwzh0x0pJm6njJX29rsd53ktyph+L90Enh0xzLFN0Ku9p+tM8E9TmKR+9ppdPqIEe4G/AuR1fHvmWenEw44M85Y/pBIPZDM2QVQngjg6iRQ42yD5hb/P4+UnvP9a5uI4Xc3f4NlJi3n54qBmdD5Hg52tNYgr8FKRoNzAoUCHelLk5PW0llF8Nc6cjJf0JfrSA1lVua488Dd34sPt798xM3IoISof1dqKslTypHP4BCyZ55SSfQJ+GrY7T9J3ct23BTrPnhhq0sPDogN4j258RmDriBGZmRcnrlmuBD5v+lvjYk0fISYNMfkrCQg5zae4d6BJIZVLY3gITGbaNoA=";
    private const string ServerRsaPrivateKey2 = "<RSAKeyValue><Modulus>uYTPtHCIztKC3MUDxnZ0ktGVSQ0jVbD5rYl4pki4RCD3M22d+TklmvTyPj0SM7a/8o7cI05QhEuBI8hKCfC2CEJhlS3WFeVC0vwsl1aYFqQ3Ykr+kDsAdqjL95ioj3JmiscvqKOM34oQahpAgukJ7Kcr1BT2Ylk8fOgKcN7t1qgURNx0Pj4zJ4w0p1nT2gLG++bYutUVPvamI9wcMQyUesZwGmM9UUpMRzsOPk8vv7TbTm62Zkx+5rFUaVe5DFNUIMg92NvyU0392FFNCwptSflidHDG1ayCwL1ZTkJ0Z9yJXCNSzi/3ulxMhE+bVcpr/EuRKCYxn9qPFZ07Bd77bQ==</Modulus><Exponent>AQAB</Exponent><P>2Mrzzbb8Gh7aoW0YXdtdO7WEZ7+pOvbxdp4Qw8sp8dF5cF5vss3I2FoJ9kssy/DsUsreBUhD0HKrADBus7BHKXp7Q/9hhu1nAJxpng255cUfngVD9k1xQdfWEHCeWrr7XJHcplTkh4ysH4nWK+8S+RoCpiuphkJJqVxPzDaY1+M=</P><Q>2xHwflmaMbNs9dXi3wx10SyG5KQJeRIXlKkhlUYlAU+7598AdmTiUPHfhj4WDRCmcJGHjSWqdiuQuwmRYsBXRhtk7XjGAjcefloSpXSR9G+tpVFuIthBU337g2pK1o8z/29LKiWZvcytgxQLEWwGIyduj2I9BoDw1jgFmVd/IG8=</Q><DP>b9n+ghO37G4g1QqpeLtWVhkoEDNFyANiv5V8BtjKclZmdoBy1ujviBikbSuKGErcUzcR593KB0EyUu2qIBGCFbd447NeiTPxYdJRd9eTIyZaUrhawThhh9wpOOAyA5PXXoJvOm4wXnNI1xjRpGc7/cPavAto8rk+sh/LmAxPPYs=</DP><DQ>b2l2N6v2IWSw+22lje5WVOUiTVGnh61N1MsXS0V7OGmGlOvy3kN8XdJE7Y7RxB89pm480+neAW8ykgzRpblQKVVxRNxxR1sk5PmGFiNsvzW0yCjbrFjzEDU4HqOGIAyAU14UigDJaZ+YdttQrbGUhXheYAmEI7SbxzaCknPPMX0=</DQ><InverseQ>SpRpqI+Z4g3jMbb0iE0oD+FAUaBXGp00DjKVbeYH8WQl2rVGFkspFYeN69u3ZFUL3JJd4rCF6zbuLq6iyDJq/F+Jo4zSzXChepr/dSEH1TszaA6imdqFyj3pjOT/ZXNK4YPCRijRM3fy8GdNybZDQljL1djY8D1YK3CWEtKuogs=</InverseQ><D>ADJKztC1SseTfRmPgnZ+DLXAgbflpK6WS3+/9/UcKAsc5LOmA8bytwvkjpPqYNGkH5g7iKU8yP16rbrSXgy6NJ7VYAVENJIhYWKdxxJzAMfvVkeCc4A/sa1GFThwXUG5KBND7EExrsu3oe67LyhOBJXv7vHCvQhSwkZNbiDEtOh7y6bKOOb0aluzPir3eY3HyN7TP2uS5mEeokMvwk9yGOUvCeKoz8t9WJf8HoP2OsDqFsbs5qA66qC6DWaU9OZ0VrO3zgmceIDP2ZXFkWmz2cVJ/Yvfi5zCvc0+g670twnuG8P00Syr/3xNCVuhwwuZbDcILjNvc9uOu9iDbY5xZQ==</D></RSAKeyValue>";
    public static readonly X509Certificate2 CertificateWithPrivateKey = CreateCertificate();

    private void Start()
    {
        Test();
    }

    void Test()
    {
        var token = JwtBuilder.Create()
                        .WithAlgorithm(new RS256Algorithm(CertificateWithPrivateKey))
                        .AddClaim("exp", DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds())
                        .Encode(); 

        Debug.Log(token);
    }

    private static X509Certificate2 CreateCertificate()
    {
        var rsa = RSA.Create();
        rsa.FromXmlString(ServerRsaPrivateKey2);

        var certPub = new X509Certificate2(Convert.FromBase64String(ServerRsaPublicKey2));
        var certPriv = new X509Certificate2(certPub.CopyWithPrivateKey(rsa).Export(X509ContentType.Pfx));

        return certPriv;
    }
}

I am receiving the error from the line: public static readonly X509Certificate2 CertificateWithPrivateKey = CreateCertificate();

### Important note: In the Script written above the public and private key are provided in a different format than the one described for my needs which were obtained from the post mentioned above.

abatishchev commented 2 years ago

Please post the exception full stack trace. Also note that the code above only works on .NET Core (3.1 and higher, if I remember correctly).

BabyWhite69 commented 2 years ago

I'm using Unity 2021.3.11f1 which uses .netstandard 2.1

Edit: Sorry, I forgot to put this excerpt earlier

CryptographicException: Unable to decode certificate.
System.Security.Cryptography.X509Certificates.X509Certificate2ImplMono..ctor (System.Byte[] rawData, Microsoft.Win32.SafeHandles.SafePasswordHandle password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) (at <a971fed4bf844502b6501dbde9cced46>:0)
Mono.X509PalImpl.ImportFallback (System.Byte[] data) (at <a971fed4bf844502b6501dbde9cced46>:0)
Mono.X509PalImplMono.Import (System.Byte[] data) (at <a971fed4bf844502b6501dbde9cced46>:0)
Mono.SystemCertificateProvider.Import (System.Byte[] data, Mono.CertificateImportFlags importFlags) (at <a971fed4bf844502b6501dbde9cced46>:0)
System.Security.Cryptography.X509Certificates.X509Helper.Import (System.Byte[] rawData) (at <9aad1b3a47484d63ba2b3985692d80e9>:0)
System.Security.Cryptography.X509Certificates.X509Certificate..ctor (System.Byte[] data) (at <9aad1b3a47484d63ba2b3985692d80e9>:0)
System.Security.Cryptography.X509Certificates.X509Certificate2..ctor (System.Byte[] rawData) (at <a971fed4bf844502b6501dbde9cced46>:0)
JWT_Test.CreateCertificate () (at Assets/JWT_Test.cs:43)
JWT_Test..cctor () (at Assets/JWT_Test.cs:13)
Rethrow as TypeInitializationException: The type initializer for 'JWT_Test' threw an exception.
abatishchev commented 2 years ago

.NET Standard is a compatibility API layer. What matters is the actual runtime by the app itself. Looks like it's Mono in your case. And that might explain why it fails, because the underlying crypto API doesn't support the operation.

BabyWhite69 commented 2 years ago

Oh, I still don't understand much about apis and framework. and yes, unity uses mono or IL2CPP, will it work with IL2CPP?

abatishchev commented 2 years ago

I frankly don't know what's that (beyond a basic Google search) so can't say. But it might not as it depends on the crypto API availability on certain platform.

I'd suggest googling how to construct an X509Certificate2 object specifically on Unity.

BabyWhite69 commented 2 years ago

I was reading this post and maybe this is the solution, I just need to know how you created the key pair and what format they are in, specifically the command to test it the same way, because if I create the key pair with the format I described it gives me another error, however in this one it is trying to decode the certificate and maybe it is due to the password...