Coder Social home page Coder Social logo

dvsekhvalnov / jose-jwt Goto Github PK

View Code? Open in Web Editor NEW
905.0 42.0 180.0 30.38 MB

Ultimate Javascript Object Signing and Encryption (JOSE), JSON Web Token (JWT) and Json Web Keys (JWK) Implementation for .NET and .NET Core

License: MIT License

C# 99.98% PowerShell 0.01% Batchfile 0.01%
jose jwt json jwa jwe jws fips jwt-token jwt-authentication encryption signature security openid oauth2 openidconnect federation netcore oidc jwk

jose-jwt's Introduction

Ultimate Javascript Object Signing and Encryption (JOSE), JSON Web Token (JWT), JSON Web Encryption (JWE) and JSON Web Keys (JWK) Implementation for .NET and .NET Core

Minimallistic zero-dependency library for generating, decoding and encryption JSON Web Tokens. Supports full suite of JSON Web Algorithms and Json Web Keys. JSON parsing agnostic, can plug any desired JSON processing library. Extensively tested for compatibility with jose.4.j, Nimbus-JOSE-JWT and json-jwt libraries. JWE JSON Serialization cross-tested with JWCrypto.

FIPS compliance

Library is fully FIPS compliant since v2.1

Which version?

  • v5.0 brings Linux, OSX and FreeBSD compatibility for ECDH encryption as long as managed ECDsa keys support. Fixes cross compatibility issues with encryption over NIST P-384, P-521 curves. And introduces new security fixes and controls.

  • v4.1 added additional capabilities to manage runtime avaliable alg suite, see Customizing library for security. And also introduced default max limits for PBKDF2 (PBES2-*) max iterations according to OWASP PBKDF2 Recomendations.

  • v4.0 introduced Json Web Key (JWK), RFC 7517 support. Latest stable. All new features will most likely appear based on given version.

  • v3.2 dropped Newtonsoft.Json support in favor of System.Text.Json on netstandard2.1

  • v3.1 introduced JWE JSON Serialization defined in RFC 7516

  • v3.0 and above additionally targets netstandard2.1 to leverage better .net crypto support on *nix systems and enable more supported algorithms.

  • v2.1 and above added extra features support for .NET461+ and coming with 3 version of binaries (NET4, NET461 and netstandard1.4).

  • v2.0 and above is .NET Core compatible and aimed to support both .NET framework (NET40) and .NET Core (netstandard1.4) runtimes.

  • v1.9 is built against .NET framework only and should be compatible with NET40 and above. The version is not actively maintained anymore except critical bug fixes.

  • WinRT compatible version (Windows 8.1 and Windows Phone 8.1) is avaliable as standalone project here: jose-rt.

  • PCLCrypto based experimental project living up here: jose-pcl.

Important upgrade notes

⚠️ v4 -> v5:

  • JWK EC keys now bridges to ECDsa by default instead of CngKey on .net 4.7.2+ and netstandard2.1+
  • Jwk.ToJson() / Jwk.FromJson() now defaults to JWT.DefaultSettings.JsonMapper if not provided explicitly.
  • Deflate decompression is limited to 250Kb by default. Check out customization section if need more.

⚠️ v3.0 -> v3.1 stricter argument validation extraHeaders argument

In 3.1 and above an attempt to override enc or alg header values in extraHeaders will throw ArgumentException.

⚠️ v2 -> v3 update public sdk changes

Moved:

  • Security.Cryptography.EccKey to Jose.keys.EccKey
  • Security.Cryptography.RsaKey to Jose.keys.RsaKey

OS cross compatibility

.Net version Windows Linux Mac OS FreeBSD v14
netcoreapp2.1
netcoreapp3.1
net 8.0
net 5.0
net 4.7
net 4.6
net 4.0

Foreword

Originally forked from https://github.com/johnsheehan/jwt . Almost re-written from scratch to support JWT encryption capabilities and unified interface for encoding/decoding/encryption and other features. Moved to separate project in February 2014.

AES Key Wrap implementation ideas and test data from http://www.cryptofreak.org/projects/rfc3394/ by Jay Miller

Supported JWA algorithms

CLR

Signing

  • HMAC signatures with HS256, HS384 and HS512.
  • ECDSA signatures with ES256, ES384 and ES512.
  • RSASSA-PKCS1-V1_5 signatures with RS256, RS384 and RS512.
  • RSASSA-PSS signatures (probabilistic signature scheme with appendix) with PS256, PS384 and PS512.
  • NONE (unprotected) plain text algorithm without integrity protection

Encryption

  • RSAES OAEP 256 (using SHA-256 and MGF1 with SHA-256) encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM
  • RSAES OAEP (using SHA-1 and MGF1 with SHA-1) encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM
  • RSAES-PKCS1-V1_5 encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM
  • Direct symmetric key encryption with pre-shared key A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM and A256GCM
  • A128KW, A192KW, A256KW encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM
  • A128GCMKW, A192GCMKW, A256GCMKW encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM
  • ECDH-ES* with A128CBC-HS256, A128GCM, A192GCM, A256GCM
  • ECDH-ES+A128KW*, ECDH-ES+A192KW*, ECDH-ES+A256KW* with A128CBC-HS256, A128GCM, A192GCM, A256GCM
  • PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM

Compression

  • DEFLATE compression

CORECLR

Signing

  • HMAC signatures with HS256, HS384 and HS512.
  • ECDSA signatures with ES256, ES384 and ES512.
  • RSASSA-PKCS1-V1_5 signatures with RS256, RS384 and RS512.
  • NONE (unprotected) plain text algorithm without integrity protection

Encryption

  • RSAES OAEP 256 (using SHA-256 and MGF1 with SHA-256) encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM
  • RSAES OAEP (using SHA-1 and MGF1 with SHA-1) encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM
  • RSAES-PKCS1-V1_5 encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM
  • Direct symmetric key encryption with pre-shared key A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM and A256GCM
  • A128KW, A192KW, A256KW encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM
  • A128GCMKW, A192GCMKW, A256GCMKW encryption with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM
  • PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW with A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM
  • ECDH-ES* with A128CBC-HS256, A128GCM, A192GCM, A256GCM
  • ECDH-ES+A128KW*, ECDH-ES+A192KW*, ECDH-ES+A256KW* with A128CBC-HS256, A128GCM, A192GCM, A256GCM

Compression

  • DEFLATE compression

Json Web Key (JWK)

  • RSA, EC, Oct keys
  • X509 Chains, SHA1 & SHA2 thumbprints
Notes:
  • Types returned by crytographic methods MAY be different on Windows and Linux. e.g. GetRSAPrivateKey() on X509Certificate2 on Windows returns RsaCng and OpenSslRsa on *nix.
  • It appears that Microsoft CNG implementation of BCryptSecretAgreement/NCryptSecretAgreement contains a bug for calculating Elliptic Curve Diffie-Hellman secret agreement on keys higher than 256 bit (P-384 and P-521 NIST curves correspondingly). At least produced secret agreements do not match any other implementation in different languages. Starting version 5 we not recommending usage of CngKey keys with ECDH-ES family due to cross compatibility with other libraries. Please switch to use ECDsa, ECDiffieHellman or JWK instead, which are cross compatible on all curves and operating systems.

Installation

NuGet

https://www.nuget.org/packages/jose-jwt/

Install-Package jose-jwt

Manual

Grab source and compile yourself:

  1. dotnet restore
  2. dotnet pack -c Release

Usage

Creating Plaintext (unprotected) Tokens

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

string token = Jose.JWT.Encode(payload, null, JwsAlgorithm.none);

Warning: When using a class as the data structure of the payload, always use nullable data types for its properties. details

Creating signed Tokens

HS-* family

HS256, HS384, HS512 signatures require byte[] array key or Jwk key of type oct of corresponding length

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var secretKey = new byte[]{164,60,194,0,161,189,41,38,130,89,141,164,45,170,159,209,69,137,243,216,191,131,47,250,32,107,231,117,37,158,225,234};

string token=Jose.JWT.Encode(payload, secretKey, JwsAlgorithm.HS256);
var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

Jwk key = new Jwk(new byte[]{164,60,194,0,161,189,41,38,130,89,141,164,45,170,159,209,69,137,243,216,191,131,47,250,32,107,231,117,37,158,225,234});

string token=Jose.JWT.Encode(payload, key, JwsAlgorithm.HS256);

RS-* and PS-* family

NET40-NET45:

RS256, RS384, RS512 and PS256, PS384, PS512 signatures require RSACryptoServiceProvider (usually private) key of corresponding length. CSP need to be forced to use Microsoft Enhanced RSA and AES Cryptographic Provider. Which usually can be done be re-importing RSAParameters. See http://clrsecurity.codeplex.com/discussions/243156 for details.

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var privateKey=new X509Certificate2("my-key.p12", "password", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet).PrivateKey as RSACryptoServiceProvider;

string token=Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.RS256);

NETCORE: RS256, RS384, RS512 and PS256, PS384, PS512 signatures require RSA (usually private) or Jwk key of type RSA of corresponding length.

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var privateKey=new X509Certificate2("my-key.p12", "password").GetRSAPrivateKey();

string token=Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.RS256);
var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

Jwk privateKey = new Jwk(
    e: "AQAB",
    n: "qFZv0pea_jn5Mo4qEUmStuhlulso8n1inXbEotd_zTrQp9K0RK0hf7t0K4BjKVhaiqIam4tVVQvkmYeBeYr1MmnO_0N97dMBz_7fmvyv0hgHaBdQ5mR5u3LTlHo8tjRE7-GzZmGs6jMcyj7HbXobDPQJZpqNy6JjliDVXxW8nWJDetxGBlqmTj1E1fr2RCsZLreDOPSDIedG1upz9RraShsIDzeefOcKibcAaKeeVI3rkAU8_mOauLSXv37hlk0h6sStJb3qZQXyOUkVkjXIkhvNu_ve0v7LiLT4G_OxYGzpOQcCnimKdojzNP6GtVDaMPh-QkSJE32UCos9R3wI2Q",
    p: "0qaOkT174vRG3E_67gU3lgOgoT6L3pVHuu7wfrIEoxycPa5_mZVG54SgvQUofGUYEGjR0lavUAjClw9tOzcODHX8RAxkuDntAFntBxgRM-IzAy8QzeRl_cbhgVjBTAhBcxg-3VySv5GdxFyrQaIo8Oy_PPI1L4EFKZHmicBd3ts",
    q: "zJPqCDKqaJH9TAGfzt6b4aNt9fpirEcdpAF1bCedFfQmUZM0LG3rMtOAIhjEXgADt5GB8ZNK3BQl8BJyMmKs57oKmbVcODERCtPqjECXXsxH-az9nzxatPvcb7imFW8OlWslwr4IIRKdEjzEYs4syQJz7k2ktqOpYI5_UfYnw1s",
    d: "lJhwb0pKlB2ivyDFO6thajotClrMA3nxIiSkIUbvVr-TToFtha36gyF6w6e6YNXQXs4HhMRy1_b-nRQDk8G4_f5urd_q-pOn5u4KfmqN3Xw-lYD3ddi9qF0NLeTVUNVFASeP0FFqbPYfdNwD-LyvwjhtT_ggMOAw3mYvU5cBfz6-3uPdhl3CwQFCTgwOud_BA9p2MPMUHG82wMK_sNO1I0TYpjm7TnwNBwiKbMf-i5CKnuohgoYrEDYLeMg3f32eBljlCFNYaoCtT-mr1Ze0OTJND04vbfLotV-BBKulIpbOOSeVpKG7gJxZHmv7in7PE5_WzaxKFVoHW3wR6v_GzQ",
    dp: "KTWmTGmf092AA1euOmRQ5IsfIIxQ5qGDn-FgsRh4acSOGE8L7WrTrTU4EOJyciuA0qz-50xIDbs4_j5pWx1BJVTrnhBin9vNLrVo9mtR6jmFS0ko226kOUpwEVLgtdQjobWLjtiuaMW-_Iw4gKWNptxZ6T1lBD8UWHaPiEFW2-M",
    dq: "Jn0lqMkvemENEMG1eUw0c601wPOMoPD4SKTlnKWPTlQS6YISbNF5UKSuFLwoJa9HA8BifDrD-Mfpo1M1HPmnoilEWUrfwMqqdCkOlbiJQhKY8AZ16QGH50kDXhmVVa8BRWdVQWBTUzWXS5kXMaeskVzextTgymPcOAhXN-ph7MU",
    qi: "sRAPigJpl8S_vsf1zhJTrHM97xRwuB26R6Tm-J8sKRPb7p5xxNlmOBBFvWmWxdto8dBElNlydSZan373yBLxzW-bZgVp-B2RKT1B3WhTYW_Vo5DLhWi84XMncJxH7avtxtF9yksaeKe0e2n3J6TTan53mDg4KF8U0OEO2ciqO9g"
);

string token=Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.RS256);

NET461 and above: Accepts RSACryptoServiceProvider, RSA or Jwk types of keys (see above).

ES-* family

NET40-NET45: ES256, ES384, ES512 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).

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

byte[] x = { 4, 114, 29, 223, 58, 3, 191, 170, 67, 128, 229, 33, 242, 178, 157, 150, 133, 25, 209, 139, 166, 69, 55, 26, 84, 48, 169, 165, 67, 232, 98, 9 };
byte[] y = { 131, 116, 8, 14, 22, 150, 18, 75, 24, 181, 159, 78, 90, 51, 71, 159, 214, 186, 250, 47, 207, 246, 142, 127, 54, 183, 72, 72, 253, 21, 88, 53 };
byte[] d = { 42, 148, 231, 48, 225, 196, 166, 201, 23, 190, 229, 199, 20, 39, 226, 70, 209, 148, 29, 70, 125, 14, 174, 66, 9, 198, 80, 251, 95, 107, 98, 206 };

var privateKey=EccKey.New(x, y, d);

string token=Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.ES256);

NETCORE: ES256, ES384, ES512 ECDSA signatures can accept either CngKey(see above), ECDsa (usually private) or Jwk of type EC elliptic curve key of corresponding length.

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var privateKey=new X509Certificate2("ecc-key.p12", "password").GetECDsaPrivateKey();

string token=Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.ES256);
var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

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

string token=Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.ES256);

NET461 and above: Accepts CngKey, ECDsa and Jwk types of keys (see above).

Creating encrypted Tokens

RSA-* key management family of algorithms

NET40-NET45:

RSA-OAEP-256, RSA-OAEP and RSA1_5 key management requires RSACryptoServiceProvider (usually public) key of corresponding length.

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var publicKey=new X509Certificate2("my-key.p12", "password").PublicKey.Key as RSACryptoServiceProvider;

string token = Jose.JWT.Encode(payload, publicKey, JweAlgorithm.RSA_OAEP, JweEncryption.A256GCM);

NETCORE: RSA-OAEP-256, RSA-OAEP and RSA1_5 key management requires RSA (usually public) or Jwk key of type RSA of corresponding length.

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var publicKey=new X509Certificate2("my-key.p12", "password").GetRSAPublicKey();

string token = Jose.JWT.Encode(payload, publicKey, JweAlgorithm.RSA_OAEP, JweEncryption.A256GCM);
var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

Jwk publicKey = new Jwk("AQAB", "qFZv0pea_jn5Mo4qEUmStuhlulso8n1inXbEotd_zTrQp9K0RK0hf7t0K4BjKVhaiqIam4tVVQvkmYeBeYr1MmnO_0N97dMBz_7fmvyv0hgHaBdQ5mR5u3LTlHo8tjRE7-GzZmGs6jMcyj7HbXobDPQJZpqNy6JjliDVXxW8nWJDetxGBlqmTj1E1fr2RCsZLreDOPSDIedG1upz9RraShsIDzeefOcKibcAaKeeVI3rkAU8_mOauLSXv37hlk0h6sStJb3qZQXyOUkVkjXIkhvNu_ve0v7LiLT4G_OxYGzpOQcCnimKdojzNP6GtVDaMPh-QkSJE32UCos9R3wI2Q");

string token = Jose.JWT.Encode(payload, publicKey, JweAlgorithm.RSA_OAEP, JweEncryption.A256GCM);

NET461: Accepts RSACryptoServiceProvider, RSA, Jwk (see above) and CngKey types of keys.

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

CngKey publicKey = CngKey.Open("connectionKeyId", CngProvider.MicrosoftSoftwareKeyStorageProvider, CngKeyOpenOptions.MachineKey));

string token = Jose.JWT.Encode(payload, publicKey, JweAlgorithm.RSA_OAEP, JweEncryption.A256GCM);

DIR direct pre-shared symmetric key family of algorithms

Direct key management with pre-shared symmetric keys requires byte[] array or Jwk of type oct key of corresponding length

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var secretKey = new byte[]{164,60,194,0,161,189,41,38,130,89,141,164,45,170,159,209,69,137,243,216,191,131,47,250,32,107,231,117,37,158,225,234};

string token = Jose.JWT.Encode(payload, secretKey, JweAlgorithm.DIR, JweEncryption.A128CBC_HS256);
var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var secretKey = new Jwk(new byte[]{164,60,194,0,161,189,41,38,130,89,141,164,45,170,159,209,69,137,243,216,191,131,47,250,32,107,231,117,37,158,225,234});

string token = Jose.JWT.Encode(payload, secretKey, JweAlgorithm.DIR, JweEncryption.A128CBC_HS256);

AES Key Wrap key management family of algorithms

AES128KW, AES192KW and AES256KW key management requires byte[] array or Jwk of type oct key of corresponding length

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var secretKey = new byte[]{164,60,194,0,161,189,41,38,130,89,141,164,45,170,159,209,69,137,243,216,191,131,47,250,32,107,231,117,37,158,225,234};

string token = Jose.JWT.Encode(payload, secretKey, JweAlgorithm.A256KW, JweEncryption.A256CBC_HS512);
var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var secretKey = new Jwk(new byte[]{164,60,194,0,161,189,41,38,130,89,141,164,45,170,159,209,69,137,243,216,191,131,47,250,32,107,231,117,37,158,225,234});

string token = Jose.JWT.Encode(payload, secretKey, JweAlgorithm.A256KW, JweEncryption.A256CBC_HS512);

AES GCM Key Wrap key management family of algorithms

AES128GCMKW, AES192GCMKW and AES256GCMKW key management requires byte[] array or Jwk of type oct key of corresponding length

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var secretKey = new byte[]{164,60,194,0,161,189,41,38,130,89,141,164,45,170,159,209,69,137,243,216,191,131,47,250,32,107,231,117,37,158,225,234};

string token = Jose.JWT.Encode(payload, secretKey, JweAlgorithm.A256GCMKW, JweEncryption.A256CBC_HS512);
var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var secretKey = new Jwk(new byte[]{164,60,194,0,161,189,41,38,130,89,141,164,45,170,159,209,69,137,243,216,191,131,47,250,32,107,231,117,37,158,225,234});

string token = Jose.JWT.Encode(payload, secretKey, JweAlgorithm.A256GCMKW, JweEncryption.A256CBC_HS512);

ECDH-ES and ECDH-ES with AES Key Wrap key management family of algorithms

NET40-NET46 (windows only): ECDH-ES and ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW key management requires CngKey (usually public) or Jwk of type EC elliptic curve key of corresponding length.

Normally existing CngKey can be 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,usage:CngKeyUsages.KeyAgreement) or use Jwk instead.

Jwk keys will use transparent bridging to CngKey under the hood.

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

byte[] x = { 4, 114, 29, 223, 58, 3, 191, 170, 67, 128, 229, 33, 242, 178, 157, 150, 133, 25, 209, 139, 166, 69, 55, 26, 84, 48, 169, 165, 67, 232, 98, 9 };
byte[] y = { 131, 116, 8, 14, 22, 150, 18, 75, 24, 181, 159, 78, 90, 51, 71, 159, 214, 186, 250, 47, 207, 246, 142, 127, 54, 183, 72, 72, 253, 21, 88, 53 };

var publicKey=EccKey.New(x, y, usage:CngKeyUsages.KeyAgreement);

string token = Jose.JWT.Encode(payload, publicKey, JweAlgorithm.ECDH_ES, JweEncryption.A256GCM);
var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var publicKey = new Jwk(
    crv: "P-256",
    x: "BHId3zoDv6pDgOUh8rKdloUZ0YumRTcaVDCppUPoYgk",
    y: "g3QIDhaWEksYtZ9OWjNHn9a6-i_P9o5_NrdISP0VWDU"
);

string token = Jose.JWT.Encode(payload, publicKey, JweAlgorithm.ECDH_ES, JweEncryption.A256GCM);

NET472 or NETCORE (all OS): Accepts either CngKey, Jwk of type EC (see above) or additionally ECDsa and ECDiffieHellman as a key.

Jwk keys will use transparent bridging to ECDiffieHellman under the hood.

jose-jwt provides convenient helper EcdhKey.New(x,y,usage:CngKeyUsages.KeyAgreement) if one want to to constuct ECDiffieHellman using raw key material (x,y) and d.

ECDsa keys usually loaded from files.

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

ECDsa publicKey = new X509Certificate2("ecc384.p12", "<password>").GetECDsaPublicKey();

string token = Jose.JWT.Encode(payload, publicKey, JweAlgorithm.ECDH_ES_A192KW, JweEncryption.A192GCM);
var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

byte[] x = { 4, 114, 29, 223, 58, 3, 191, 170, 67, 128, 229, 33, 242, 178, 157, 150, 133, 25, 209, 139, 166, 69, 55, 26, 84, 48, 169, 165, 67, 232, 98, 9 };
byte[] y = { 131, 116, 8, 14, 22, 150, 18, 75, 24, 181, 159, 78, 90, 51, 71, 159, 214, 186, 250, 47, 207, 246, 142, 127, 54, 183, 72, 72, 253, 21, 88, 53 };

ECDiffieHellman publicKey=EcdhKey.New(x, y, usage:CngKeyUsages.KeyAgreement);

string token = Jose.JWT.Encode(payload, publicKey, JweAlgorithm.ECDH_ES_A128KW, JweEncryption.A128GCM);

PBES2 using HMAC SHA with AES Key Wrap key management family of algorithms

PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW key management requires string passphrase to derive key from

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

string token = Jose.JWT.Encode(payload, "top secret", JweAlgorithm.PBES2_HS256_A128KW, JweEncryption.A256CBC_HS512);

Iteration counts can be controlled by setting p2c header value:

var headers = new Dictionary<string, object>
{
    { "p2c", 10000 }
};

string token = Jose.JWT.Encode(payload, "top secret", JweAlgorithm.PBES2_HS256_A128KW, JweEncryption.A256CBC_HS512, extraHeaders: headers);

Please see Adding extra headers for additional details.

Optional compressing payload before encrypting

Optional DEFLATE compression is supported

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var publicKey=new X509Certificate2("my-key.p12", "password").PublicKey.Key as RSACryptoServiceProvider;

string token = Jose.JWT.Encode(payload, publicKey, JweAlgorithm.RSA1_5, JweEncryption.A128CBC_HS256, JweCompression.DEF);

Verifying and Decoding Tokens

What methods to use?

Historically jose-jwt provided single family of Decode() methods that handles both signed and encrypted tokens with uniform interface, but as a number of confusion attacks on JWT libraries increased over last years, starting v5 library additionally provides dedicated methods Verify() and Encrypt() that are limited in scope to verifying signatures and decrypting tokens accordingly. See Strict Validation and Confusion Attacks sections for more information.

Decoding json web tokens is fully symmetric to creating signed or encrypted tokens:

HS256, HS384, HS512 signatures, A128KW, A192KW, A256KW, A128GCMKW, A192GCMKW, A256GCMKW and DIR key management algorithms expects byte[] array or Jwk of type oct key

string token = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..Fmz3PLVfv-ySl4IJ.LMZpXMDoBIll5yuEs81Bws2-iUUaBSpucJPL-GtDKXkPhFpJmES2T136Vd8xzvp-3JW-fvpRZtlhluqGHjywPctol71Zuz9uFQjuejIU4axA_XiAy-BadbRUm1-25FRT30WtrrxKltSkulmIS5N-Nsi_zmCz5xicB1ZnzneRXGaXY4B444_IHxGBIS_wdurPAN0OEGw4xIi2DAD1Ikc99a90L7rUZfbHNg_iTBr-OshZqDbR6C5KhmMgk5KqDJEN8Ik-Yw.Jbk8ZmO901fqECYVPKOAzg";

byte[] secretKey=new byte[]{164,60,194,0,161,189,41,38,130,89,141,164,45,170,159,209,69,137,243,216,191,131,47,250,32,107,231,117,37,158,225,234};

string json = Jose.JWT.Decode(token, secretKey);

// starting v5 can also
string json=Jose.JWT.Decrypt(token, secretKey);
string token = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..Fmz3PLVfv-ySl4IJ.LMZpXMDoBIll5yuEs81Bws2-iUUaBSpucJPL-GtDKXkPhFpJmES2T136Vd8xzvp-3JW-fvpRZtlhluqGHjywPctol71Zuz9uFQjuejIU4axA_XiAy-BadbRUm1-25FRT30WtrrxKltSkulmIS5N-Nsi_zmCz5xicB1ZnzneRXGaXY4B444_IHxGBIS_wdurPAN0OEGw4xIi2DAD1Ikc99a90L7rUZfbHNg_iTBr-OshZqDbR6C5KhmMgk5KqDJEN8Ik-Yw.Jbk8ZmO901fqECYVPKOAzg";

byte[] secretKey=new Jwk(new byte[]{164,60,194,0,161,189,41,38,130,89,141,164,45,170,159,209,69,137,243,216,191,131,47,250,32,107,231,117,37,158,225,234});

string json = Jose.JWT.Decode(token, secretKey);

// starting v5 can also
string json = Jose.JWT.Decrypt(token, secretKey);

RS256, RS384, RS512, PS256, PS384, PS512 signatures and RSA-OAEP-256, RSA-OAEP, RSA1_5 key management algorithms expects

NET40-NET45: RSACryptoServiceProvider as a key, public/private is asymmetric to encoding:

string token = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.bx_4TL7gh14IeM3EClP3iVfY9pbT81pflXd1lEZOVPJR6PaewRFXWmiJcaqH9fcU9IjGGQ19BS-UPtpErenL5kw7KORFgIBm4hObCYxLoAadMy8A-qQeOWyjnxbE0mbQIdoFI4nGK5qWTEQUWZCMwosvyeHLqEZDzr9CNLAAFTujvsZJJ7NLTkA0cTUzz64b57uSvMTaOK6j7Ap9ZaAgF2uaqBdZ1NzqofLeU4XYCG8pWc5Qd-Ri_1KsksjaDHk12ZU4vKIJWJ-puEnpXBLoHuko92BnN8_LXx4sfDdK7wRiXk0LU_iwoT5zb1ro7KaM0hcfidWoz95vfhPhACIsXQ.YcVAPLJ061gvPpVB-zMm4A.PveUBLejLzMjA4tViHTRXbYnxMHFu8W2ECwj9b6sF2u2azi0TbxxMhs65j-t3qm-8EKBJM7LKIlkAtQ1XBeZl4zuTeMFxsQ0VShQfwlN2r8dPFgUzb4f_MzBuFFYfP5hBs-jugm89l2ZTj8oAOOSpAlC7uTmwha3dNaDOzlJniqAl_729q5EvSjaYXMtaET9wSTNSDfMUVFcMERbB50VOhc134JDUVPTuriD0rd4tQm8Do8obFKtFeZ5l3jT73-f1tPZwZ6CmFVxUMh6gSdY5A.tR8bNx9WErquthpWZBeMaw";

var privateKey=new X509Certificate2("my-key.p12", "password", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet).PrivateKey as RSACryptoServiceProvider;

string json = Jose.JWT.Decode(token,privateKey);

// starting v5 can also
string json = Jose.JWT.Decrypt(token, secretKey);

NETCORE: RSA or Jwk of type RSA as a key, public/private is asymmetric to encoding:

string token = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.bx_4TL7gh14IeM3EClP3iVfY9pbT81pflXd1lEZOVPJR6PaewRFXWmiJcaqH9fcU9IjGGQ19BS-UPtpErenL5kw7KORFgIBm4hObCYxLoAadMy8A-qQeOWyjnxbE0mbQIdoFI4nGK5qWTEQUWZCMwosvyeHLqEZDzr9CNLAAFTujvsZJJ7NLTkA0cTUzz64b57uSvMTaOK6j7Ap9ZaAgF2uaqBdZ1NzqofLeU4XYCG8pWc5Qd-Ri_1KsksjaDHk12ZU4vKIJWJ-puEnpXBLoHuko92BnN8_LXx4sfDdK7wRiXk0LU_iwoT5zb1ro7KaM0hcfidWoz95vfhPhACIsXQ.YcVAPLJ061gvPpVB-zMm4A.PveUBLejLzMjA4tViHTRXbYnxMHFu8W2ECwj9b6sF2u2azi0TbxxMhs65j-t3qm-8EKBJM7LKIlkAtQ1XBeZl4zuTeMFxsQ0VShQfwlN2r8dPFgUzb4f_MzBuFFYfP5hBs-jugm89l2ZTj8oAOOSpAlC7uTmwha3dNaDOzlJniqAl_729q5EvSjaYXMtaET9wSTNSDfMUVFcMERbB50VOhc134JDUVPTuriD0rd4tQm8Do8obFKtFeZ5l3jT73-f1tPZwZ6CmFVxUMh6gSdY5A.tR8bNx9WErquthpWZBeMaw";

var privateKey=new X509Certificate2("my-key.p12", "password").GetRSAPrivateKey();

string json = Jose.JWT.Decode(token,privateKey);

// starting v5 can also
string json = Jose.JWT.Decrypt(token, secretKey);
string token = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.bx_4TL7gh14IeM3EClP3iVfY9pbT81pflXd1lEZOVPJR6PaewRFXWmiJcaqH9fcU9IjGGQ19BS-UPtpErenL5kw7KORFgIBm4hObCYxLoAadMy8A-qQeOWyjnxbE0mbQIdoFI4nGK5qWTEQUWZCMwosvyeHLqEZDzr9CNLAAFTujvsZJJ7NLTkA0cTUzz64b57uSvMTaOK6j7Ap9ZaAgF2uaqBdZ1NzqofLeU4XYCG8pWc5Qd-Ri_1KsksjaDHk12ZU4vKIJWJ-puEnpXBLoHuko92BnN8_LXx4sfDdK7wRiXk0LU_iwoT5zb1ro7KaM0hcfidWoz95vfhPhACIsXQ.YcVAPLJ061gvPpVB-zMm4A.PveUBLejLzMjA4tViHTRXbYnxMHFu8W2ECwj9b6sF2u2azi0TbxxMhs65j-t3qm-8EKBJM7LKIlkAtQ1XBeZl4zuTeMFxsQ0VShQfwlN2r8dPFgUzb4f_MzBuFFYfP5hBs-jugm89l2ZTj8oAOOSpAlC7uTmwha3dNaDOzlJniqAl_729q5EvSjaYXMtaET9wSTNSDfMUVFcMERbB50VOhc134JDUVPTuriD0rd4tQm8Do8obFKtFeZ5l3jT73-f1tPZwZ6CmFVxUMh6gSdY5A.tR8bNx9WErquthpWZBeMaw";

Jwk privateKey = new Jwk(
    e: "AQAB",
    n: "qFZv0pea_jn5Mo4qEUmStuhlulso8n1inXbEotd_zTrQp9K0RK0hf7t0K4BjKVhaiqIam4tVVQvkmYeBeYr1MmnO_0N97dMBz_7fmvyv0hgHaBdQ5mR5u3LTlHo8tjRE7-GzZmGs6jMcyj7HbXobDPQJZpqNy6JjliDVXxW8nWJDetxGBlqmTj1E1fr2RCsZLreDOPSDIedG1upz9RraShsIDzeefOcKibcAaKeeVI3rkAU8_mOauLSXv37hlk0h6sStJb3qZQXyOUkVkjXIkhvNu_ve0v7LiLT4G_OxYGzpOQcCnimKdojzNP6GtVDaMPh-QkSJE32UCos9R3wI2Q",
    p: "0qaOkT174vRG3E_67gU3lgOgoT6L3pVHuu7wfrIEoxycPa5_mZVG54SgvQUofGUYEGjR0lavUAjClw9tOzcODHX8RAxkuDntAFntBxgRM-IzAy8QzeRl_cbhgVjBTAhBcxg-3VySv5GdxFyrQaIo8Oy_PPI1L4EFKZHmicBd3ts",
    q: "zJPqCDKqaJH9TAGfzt6b4aNt9fpirEcdpAF1bCedFfQmUZM0LG3rMtOAIhjEXgADt5GB8ZNK3BQl8BJyMmKs57oKmbVcODERCtPqjECXXsxH-az9nzxatPvcb7imFW8OlWslwr4IIRKdEjzEYs4syQJz7k2ktqOpYI5_UfYnw1s",
    d: "lJhwb0pKlB2ivyDFO6thajotClrMA3nxIiSkIUbvVr-TToFtha36gyF6w6e6YNXQXs4HhMRy1_b-nRQDk8G4_f5urd_q-pOn5u4KfmqN3Xw-lYD3ddi9qF0NLeTVUNVFASeP0FFqbPYfdNwD-LyvwjhtT_ggMOAw3mYvU5cBfz6-3uPdhl3CwQFCTgwOud_BA9p2MPMUHG82wMK_sNO1I0TYpjm7TnwNBwiKbMf-i5CKnuohgoYrEDYLeMg3f32eBljlCFNYaoCtT-mr1Ze0OTJND04vbfLotV-BBKulIpbOOSeVpKG7gJxZHmv7in7PE5_WzaxKFVoHW3wR6v_GzQ",
    dp: "KTWmTGmf092AA1euOmRQ5IsfIIxQ5qGDn-FgsRh4acSOGE8L7WrTrTU4EOJyciuA0qz-50xIDbs4_j5pWx1BJVTrnhBin9vNLrVo9mtR6jmFS0ko226kOUpwEVLgtdQjobWLjtiuaMW-_Iw4gKWNptxZ6T1lBD8UWHaPiEFW2-M",
    dq: "Jn0lqMkvemENEMG1eUw0c601wPOMoPD4SKTlnKWPTlQS6YISbNF5UKSuFLwoJa9HA8BifDrD-Mfpo1M1HPmnoilEWUrfwMqqdCkOlbiJQhKY8AZ16QGH50kDXhmVVa8BRWdVQWBTUzWXS5kXMaeskVzextTgymPcOAhXN-ph7MU",
    qi: "sRAPigJpl8S_vsf1zhJTrHM97xRwuB26R6Tm-J8sKRPb7p5xxNlmOBBFvWmWxdto8dBElNlydSZan373yBLxzW-bZgVp-B2RKT1B3WhTYW_Vo5DLhWi84XMncJxH7avtxtF9yksaeKe0e2n3J6TTan53mDg4KF8U0OEO2ciqO9g"
);

string json = Jose.JWT.Decode(token,privateKey);

// starting v5 can also
string json = Jose.JWT.Decrypt(token, secretKey);

NET461: RSACryptoServiceProvider, RSA, Jwk of type RSA (see above) or CngKey types of keys, public/private is asymmetric to encoding.

string token = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.bx_4TL7gh14IeM3EClP3iVfY9pbT81pflXd1lEZOVPJR6PaewRFXWmiJcaqH9fcU9IjGGQ19BS-UPtpErenL5kw7KORFgIBm4hObCYxLoAadMy8A-qQeOWyjnxbE0mbQIdoFI4nGK5qWTEQUWZCMwosvyeHLqEZDzr9CNLAAFTujvsZJJ7NLTkA0cTUzz64b57uSvMTaOK6j7Ap9ZaAgF2uaqBdZ1NzqofLeU4XYCG8pWc5Qd-Ri_1KsksjaDHk12ZU4vKIJWJ-puEnpXBLoHuko92BnN8_LXx4sfDdK7wRiXk0LU_iwoT5zb1ro7KaM0hcfidWoz95vfhPhACIsXQ.YcVAPLJ061gvPpVB-zMm4A.PveUBLejLzMjA4tViHTRXbYnxMHFu8W2ECwj9b6sF2u2azi0TbxxMhs65j-t3qm-8EKBJM7LKIlkAtQ1XBeZl4zuTeMFxsQ0VShQfwlN2r8dPFgUzb4f_MzBuFFYfP5hBs-jugm89l2ZTj8oAOOSpAlC7uTmwha3dNaDOzlJniqAl_729q5EvSjaYXMtaET9wSTNSDfMUVFcMERbB50VOhc134JDUVPTuriD0rd4tQm8Do8obFKtFeZ5l3jT73-f1tPZwZ6CmFVxUMh6gSdY5A.tR8bNx9WErquthpWZBeMaw";

CngKey privateKey = CngKey.Open("decryptionKeyId", CngProvider.MicrosoftSoftwareKeyStorageProvider, CngKeyOpenOptions.MachineKey));

string json = Jose.JWT.Decode(token,privateKey);

// starting v5 can also
string json = Jose.JWT.Decrypt(token, secretKey);

ES256, ES284, ES512 signatures expects

NET40-NET45: CngKey as a key, public/private is asymmetric to encoding. If EccKey.New(...) wrapper is used, make sure correct usage: value is set. Should be CngKeyUsages.Signing for ES-* signatures (default value, can be ommited).

string token = "eyJhbGciOiJFUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.EVnmDMlz-oi05AQzts-R3aqWvaBlwVZddWkmaaHyMx5Phb2NSLgyI0kccpgjjAyo1S5KCB3LIMPfmxCX_obMKA";

byte[] x = { 4, 114, 29, 223, 58, 3, 191, 170, 67, 128, 229, 33, 242, 178, 157, 150, 133, 25, 209, 139, 166, 69, 55, 26, 84, 48, 169, 165, 67, 232, 98, 9 };
byte[] y = { 131, 116, 8, 14, 22, 150, 18, 75, 24, 181, 159, 78, 90, 51, 71, 159, 214, 186, 250, 47, 207, 246, 142, 127, 54, 183, 72, 72, 253, 21, 88, 53 };

var publicKey=EccKey.New(x, y);

string json = Jose.JWT.Decode(token, publicKey);

// starting v5 can also
string json = Jose.JWT.Verify(token, secretKey);

NETCORE: can accept either CngKey (see above), ECDsa or Jwk of type EC as a key, public/private is asymmetric to encoding.

string token = "eyJhbGciOiJFUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.EVnmDMlz-oi05AQzts-R3aqWvaBlwVZddWkmaaHyMx5Phb2NSLgyI0kccpgjjAyo1S5KCB3LIMPfmxCX_obMKA";

var publicKey=new X509Certificate2("ecc-key.p12", "password").GetECDsaPublicKey();

string token=Jose.JWT.Decode(token, publicKey, JwsAlgorithm.ES256);

// starting v5 can also
string json = Jose.JWT.Verify(token, secretKey);
string token = "eyJhbGciOiJFUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.EVnmDMlz-oi05AQzts-R3aqWvaBlwVZddWkmaaHyMx5Phb2NSLgyI0kccpgjjAyo1S5KCB3LIMPfmxCX_obMKA";

var publicKey = new Jwk(
    crv: "P-256",
    x: "BHId3zoDv6pDgOUh8rKdloUZ0YumRTcaVDCppUPoYgk",
    y: "g3QIDhaWEksYtZ9OWjNHn9a6-i_P9o5_NrdISP0VWDU"
);

string token=Jose.JWT.Decode(token, publicKey, JwsAlgorithm.ES256);

// starting v5 can also
string json = Jose.JWT.Verify(token, secretKey);

NET461: accepts CngKey, ECDsa or Jwk of type EC types of keys (see examples above), public/private is asymmetric to encoding.

ECDH-ES and ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW key management algorithms expects

NET40-NET46 (windows only): CngKey or Jwk of type EC as a key, public/private is asymmetric to encoding. If EccKey.New(...) wrapper is used, make sure correct usage: value is set. Should be CngKeyUsages.KeyAgreement for ECDH-ES.

Jwk keys will use transparent bridging to CngKey under the hood.

string token = "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJrdHkiOiJFQyIsIngiOiJPbDdqSWk4SDFpRTFrcnZRTmFQeGp5LXEtY3pQME40RVdPM1I3NTg0aEdVIiwieSI6Ik1kU2V1OVNudWtwOWxLZGU5clVuYmp4a3ozbV9kTWpqQXc5NFd3Q0xaa3MiLCJjcnYiOiJQLTI1NiJ9fQ..E4XwpWZ2kO-Vg0xb.lP5LWPlabtmzS-m2EPGhlPGgllLNhI5OF2nAbbV9tVvtCckKpt358IQNRk-W8-JNL9SsLdWmVUMplrw-GO-KA2qwxEeh_8-muYCw3qfdhVVhLnOF-kL4mW9a00Xls_6nIZponGrqpHCwRQM5aSr365kqTNpfOnXgJTKG2459nqv8n4oSfmwV2iRUBlXEgTO-1Tvrq9doDwZCCHj__JKvbuPfyRBp5T7d-QJio0XRF1TO4QY36GtKMXWR264lS7g-T1xxtA.vFevA9zsyOnNA5RZanKqHA";

byte[] x = { 4, 114, 29, 223, 58, 3, 191, 170, 67, 128, 229, 33, 242, 178, 157, 150, 133, 25, 209, 139, 166, 69, 55, 26, 84, 48, 169, 165, 67, 232, 98, 9 };
byte[] y = { 131, 116, 8, 14, 22, 150, 18, 75, 24, 181, 159, 78, 90, 51, 71, 159, 214, 186, 250, 47, 207, 246, 142, 127, 54, 183, 72, 72, 253, 21, 88, 53 };
byte[] d = { 42, 148, 231, 48, 225, 196, 166, 201, 23, 190, 229, 199, 20, 39, 226, 70, 209, 148, 29, 70, 125, 14, 174, 66, 9, 198, 80, 251, 95, 107, 98, 206 };

var privateKey=EccKey.New(x, y, d, CngKeyUsages.KeyAgreement);

string json = Jose.JWT.Decode(token, privateKey);

// starting v5 can also
string json = Jose.JWT.Decrypt(token, secretKey);
string token = "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJrdHkiOiJFQyIsIngiOiJPbDdqSWk4SDFpRTFrcnZRTmFQeGp5LXEtY3pQME40RVdPM1I3NTg0aEdVIiwieSI6Ik1kU2V1OVNudWtwOWxLZGU5clVuYmp4a3ozbV9kTWpqQXc5NFd3Q0xaa3MiLCJjcnYiOiJQLTI1NiJ9fQ..E4XwpWZ2kO-Vg0xb.lP5LWPlabtmzS-m2EPGhlPGgllLNhI5OF2nAbbV9tVvtCckKpt358IQNRk-W8-JNL9SsLdWmVUMplrw-GO-KA2qwxEeh_8-muYCw3qfdhVVhLnOF-kL4mW9a00Xls_6nIZponGrqpHCwRQM5aSr365kqTNpfOnXgJTKG2459nqv8n4oSfmwV2iRUBlXEgTO-1Tvrq9doDwZCCHj__JKvbuPfyRBp5T7d-QJio0XRF1TO4QY36GtKMXWR264lS7g-T1xxtA.vFevA9zsyOnNA5RZanKqHA";

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

string json = Jose.JWT.Decode(token, privateKey);

// starting v5 can also
string json = Jose.JWT.Decrypt(token, secretKey);

NET472 or NETCORE (all OS): Accepts either CngKey, Jwk of type EC (see above) or additionally ECDsa and ECDiffieHellman as a key.

Jwk keys will use transparent bridging to ECDiffieHellman under the hood.

string token = "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJrdHkiOiJFQyIsIngiOiJPbDdqSWk4SDFpRTFrcnZRTmFQeGp5LXEtY3pQME40RVdPM1I3NTg0aEdVIiwieSI6Ik1kU2V1OVNudWtwOWxLZGU5clVuYmp4a3ozbV9kTWpqQXc5NFd3Q0xaa3MiLCJjcnYiOiJQLTI1NiJ9fQ..E4XwpWZ2kO-Vg0xb.lP5LWPlabtmzS-m2EPGhlPGgllLNhI5OF2nAbbV9tVvtCckKpt358IQNRk-W8-JNL9SsLdWmVUMplrw-GO-KA2qwxEeh_8-muYCw3qfdhVVhLnOF-kL4mW9a00Xls_6nIZponGrqpHCwRQM5aSr365kqTNpfOnXgJTKG2459nqv8n4oSfmwV2iRUBlXEgTO-1Tvrq9doDwZCCHj__JKvbuPfyRBp5T7d-QJio0XRF1TO4QY36GtKMXWR264lS7g-T1xxtA.vFevA9zsyOnNA5RZanKqHA";

ECDsa privateKey = new X509Certificate2("ecc256.p12", "<password>").GetECDsaPrivateKey();

string token = Jose.JWT.Decode(token, privateKey);

// starting v5 can also
string json = Jose.JWT.Decrypt(token, secretKey);
string token = "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJrdHkiOiJFQyIsIngiOiJPbDdqSWk4SDFpRTFrcnZRTmFQeGp5LXEtY3pQME40RVdPM1I3NTg0aEdVIiwieSI6Ik1kU2V1OVNudWtwOWxLZGU5clVuYmp4a3ozbV9kTWpqQXc5NFd3Q0xaa3MiLCJjcnYiOiJQLTI1NiJ9fQ..E4XwpWZ2kO-Vg0xb.lP5LWPlabtmzS-m2EPGhlPGgllLNhI5OF2nAbbV9tVvtCckKpt358IQNRk-W8-JNL9SsLdWmVUMplrw-GO-KA2qwxEeh_8-muYCw3qfdhVVhLnOF-kL4mW9a00Xls_6nIZponGrqpHCwRQM5aSr365kqTNpfOnXgJTKG2459nqv8n4oSfmwV2iRUBlXEgTO-1Tvrq9doDwZCCHj__JKvbuPfyRBp5T7d-QJio0XRF1TO4QY36GtKMXWR264lS7g-T1xxtA.vFevA9zsyOnNA5RZanKqHA";

byte[] x = { 4, 114, 29, 223, 58, 3, 191, 170, 67, 128, 229, 33, 242, 178, 157, 150, 133, 25, 209, 139, 166, 69, 55, 26, 84, 48, 169, 165, 67, 232, 98, 9 };
byte[] y = { 131, 116, 8, 14, 22, 150, 18, 75, 24, 181, 159, 78, 90, 51, 71, 159, 214, 186, 250, 47, 207, 246, 142, 127, 54, 183, 72, 72, 253, 21, 88, 53 };
byte[] d = { 42, 148, 231, 48, 225, 196, 166, 201, 23, 190, 229, 199, 20, 39, 226, 70, 209, 148, 29, 70, 125, 14, 174, 66, 9, 198, 80, 251, 95, 107, 98, 206 };

var privateKey=EcdhKey.New(x, y, d, CngKeyUsages.KeyAgreement);

string json = Jose.JWT.Decode(token, privateKey);

// starting v5 can also
string json = Jose.JWT.Decrypt(token, secretKey);

PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW key management algorithms expects string passpharase as a key

string token = "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwicDJjIjo4MTkyLCJwMnMiOiJiMFlFVmxMemtaNW9UUjBMIn0.dhPAhJ9kmaEbP-02VtEoPOF2QSEYM5085V6zYt1U1qIlVNRcHTGDgQ.4QAAq0dVQT41dQKDG7dhRA.H9MgJmesbU1ow6GCa0lEMwv8A_sHvgaWKkaMcdoj_z6O8LaMSgquxA-G85R_5hEILnHUnFllNJ48oJY7VmAJw0BQW73dMnn58u161S6Ftq7Mjxxq7bcksWvFTVtG5RsqqYSol5BZz5xm8Fcj-y5BMYMvrsCyQhYdeGEHkAvwzRdvZ8pGMsU2XPzl6GqxGjjuRh2vApAeNrj6MwKuD-k6AR0MH46EiNkVCmMkd2w8CNAXjJe9z97zky93xbxlOLozaC3NBRO2Q4bmdGdRg5y4Ew.xNqRi0ouQd7uo5UrPraedg";

string json = Jose.JWT.Decode(token, "top secret");

// starting v5 can also
string json = Jose.JWT.Decrypt(token, secretKey);

JWE JSON Serialization support (RFC 7516)

As of version v3.1 jose-jwt library provides full support for json serialized encrypted content.

Decoding json serialized encrypted content

JweToken Jose.JWE.Decrypt(token, key) - can be used to decrypt JSON serialized token.

See Verifying and Decoding Tokens section for information about different key types usage.

The function returns object of type JweToken with following properties:

  • PlaintextBytes - byte array with decrypted content, only when decryption successfully performed
  • Plaintext - decrypted content as string, only when decryption successfully performed
  • Recipient - effective recipient that was used to decrypt token, only when decryption was successfully performed
  • Aad - additional authentication data
  • Iv - initialization vector
  • Ciphertext - ciphertext (encrypted content)
  • AuthTag - authenication tag
  • UnprotectedHeader - shared unprotected headers (key-value pairs)
  • ProtectedHeaderBytes - shared signature protected headers, binary blob
  • Recipients - list of JweRecipient objects as specified in token

JweRecipient object supports following properties:

  • Alg - Key encryption algorithm
  • Header - Per recipient set of headers
  • JoseHeader - effective headers for given recipient, calculated as union of shared headers and per-recipient headers, only when decryption successfully performed via Jose.JWE.Decrypt() or Jose.JWE.Headers() was called.
string token = @"
{
	""aad"": ""ZXlKaGJHY2lPaUpCTVRJNFMxY2lMQ0psYm1NaU9pSkJNVEk0UTBKRExVaFRNalUySW4w"",
	""ciphertext"": ""02VvoX1sUsmFi2ZpIbTI8g"",
	""encrypted_key"": ""kH4te-O3DNZoDlxeDnBXM9CNx2d5IgVGO-cVMmqTRW_ws0EG_RKDQ7FLLztMM83z2s-pSNSZtFf3bx9Aky8XOzhIYCIU7XvmiQ0pp5z1FRdrwO-RxEOJfb2hAjD-hE5lCJkkY722QGs4IrUQ5N5Atc9h9-0vDcg-gksFIuaLMeRQj3LxivhwJO-QWFd6sG0FY6fBCwS1X6zsrZo-m9DNvrB6FhMpkLPBDOlCNnjKf1_Mz_jAuXIwnVUhoq59m8tvxQY1Fyngiug6zSnM207-0BTXzuCTnPgPAwGWGDLO7o0ttPT6RI_tLvYE6AuOynsqsHDaecyIkJ26dif3iRmkeg"",
	""header"": {
		""alg"": ""RSA-OAEP-256"",
		""kid"": ""Ex-p1KJFz8hQE1S76SzkhHcaObCKoDPrtAPJdWuTcTc""
	},
	""iv"": ""E1BAiqIeAH_0eInT59zb8w"",
	""protected"": ""eyJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwidHlwIjoiSldFIn0"",
	""tag"": ""yYBiajF5oMtyK3mRVQyPnlJL25hXW8Ct8ZMcFK5ehDY""
}";

// Use key type approporiate for your recipient
var key = LoadPrivateRsaKey();

// decrypt JSON serialized token
JweToken jwe = Jose.JWE.Decrypt(token, key);

// generic form to access decrypted content as blob
byte[] binaryContent = jwe.PlaintextBytes;

// convinient helper to get decrypted content as string
string content = jwe.Plaintext;

// effective recipient information that was used for decryption
JweRecipient recipient = jwe.Recipient;

// accessing effective headers
string keyId = recipient.JoseHeaders["kid"];

Encrypting using JSON serialization

string Jose.JWE.Encrypt(plaintext, recipients, encryption, add, serialization, compression, extraProtectedHeaders, unprotectedHeaders, settings) - can be used to encrypt string content and produce JSON serialized token.

Alternate version to encrypt binary content is also available string Jose.JWE.EncryptBytes().

Where:

  • plaintext - content (payload) to be encrypted
  • recipients - one or more recipients information
  • encryption - encryption to be used
  • add - optional, Additional Authentication Data
  • serialization - optional, Json is default, final token encoding
  • compression - optional, compression algorithm
  • extraProtectedHeaders - optional, additional key-value pairs to include into protected header
  • unprotectedHeaders - optional, key-value pairs to include into unprotected header

See Creating encrypted Tokens section for information about different key types usage.

var payload = "Hello JWE !";
var blob = new byte[] { 72, 101, 108, 108, 111, 32, 74, 87, 69, 32, 33 };
var preSharedKey = LoadKey();

// generate JSON encoded token
string token_1 = JWE.Encrypt(payload, new[] { new JweRecipient(JweAlgorithm.A256KW, preSharedKey) }, JweEncryption.A256GCM);

// encrypt binary
string token_2 = JWE.EncryptBytes(payload, new[] { new JweRecipient(JweAlgorithm.A256KW, preSharedKey) }, JweEncryption.A256GCM);

// can opt-in for Compact encoded tokens with same interface
string token_3 = JWE.Encrypt(payload, new[] { new JweRecipient(JweAlgorithm.A256KW, preSharedKey), mode: SerializationMode.Compact }, JweEncryption.A256GCM);

Encrypt for multiple recipients at once:

var payload = "Hello World !";
JweRecipient r1 = new JweRecipient(JweAlgorithm.PBES2_HS256_A128KW, "secret");
JweRecipient r2 = new JweRecipient(JweAlgorithm.ECDH_ES_A128KW, ECPublicKey());
JweRecipient r3 = new JweRecipient(JweAlgorithm.RSA_OAEP_256, RsaPublicKey());

string token = JWE.Encrypt(payload, new[] { r1, r2, r3 }, JweEncryption.A256GCM);

Provide additional authentication data and unprotected shared headers:

var payload = "Hello World !";

// additional authenticatin data
var aad = new byte[] { 101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 66, 77, 84, 73, 52, 83, 49, 99, 105, 76, 67, 74, 108, 98, 109, 77, 105, 79, 105, 74, 66, 77, 84, 73, 52, 81, 48, 74, 68, 76, 85, 104, 84, 77, 106, 85, 50, 73, 110, 48 };

// shared unprotected headers
var unprotected = new Dictionary<string, object>
{
    { "jku", "https://server.example.com/keys.jwks" }
};

var preSharedKey = LoadKey();

string token = JWE.Encrypt(payload, new[] { r }, JweEncryption.A256GCM, aad, unprotectedHeaders: unprotected);

Working with Json Web Keys (JWKs)

As of v4.0.0 library provides full-blown support for Json Web Keys (aka RFC 7517), including parsing, contructing and bridging the gap with different .NET key types to be used in all signing and encryption algorithms.

See Creating encrypted Tokens, Creating signed Tokens and Verifying and Decoding Tokens for details on using JWK with different Json Web Tokens algorithms.

Most of JWK support will work on .NET 4.6.1 and .NET Core 2.0. But some key bridging requires .NET 4.7.2 or NETSTANDARD 2.1 and above.

The two core classes are:

  • Jwk - object model for Json Web Key
  • JwkSet - object model Json Web Key Set

Reading JWK

Both classes offers set of static methods to read or write model from JSON string or dictionary object respectively.

  • Jwk.FromJson(string, IJsonMapper), JwkSet.FromJson(string, IJsonMapper) - parses json and constructs object model from it
  • Jwk.FromDictionary(IDictionary<string, object>), JwkSet.FromDictionary(IDictionary<string, object>) - constructs object model from dictionary
  • Jwk.ToJson(IJsonMapper), JwkSet.ToJson(IJsonMapper) - searializes model to json
  • Jwk.ToDictionary(), JwkSet.ToDictionary() - writes model as dictionary

See Examples for usage details.

Constructing JWK

Model object can be constructed normal way via setting approporiate properties or calling collection methods, there are also set of convinient constructors:

// Oct key
Jwk octKey = new Jwk(new byte[] { 25, 172, 32, 130, 225, 114, 26, 181, 138, 106, 254, 192, 95, 133, 74, 82 });

// RSA key
Jwk rsaKey = new Jwk(
    e: "AQAB",
    n: "qFZv0pea_jn5Mo4qEUmStuhlulso8n1inXbEotd_zTrQp9K0RK0hf7t0K4BjKVhaiqIam4tVVQvkmYeBeYr1MmnO_0N97dMBz_7fmvyv0hgHaBdQ5mR5u3LTlHo8tjRE7-GzZmGs6jMcyj7HbXobDPQJZpqNy6JjliDVXxW8nWJDetxGBlqmTj1E1fr2RCsZLreDOPSDIedG1upz9RraShsIDzeefOcKibcAaKeeVI3rkAU8_mOauLSXv37hlk0h6sStJb3qZQXyOUkVkjXIkhvNu_ve0v7LiLT4G_OxYGzpOQcCnimKdojzNP6GtVDaMPh-QkSJE32UCos9R3wI2Q",
    p: "0qaOkT174vRG3E_67gU3lgOgoT6L3pVHuu7wfrIEoxycPa5_mZVG54SgvQUofGUYEGjR0lavUAjClw9tOzcODHX8RAxkuDntAFntBxgRM-IzAy8QzeRl_cbhgVjBTAhBcxg-3VySv5GdxFyrQaIo8Oy_PPI1L4EFKZHmicBd3ts",
    q: "zJPqCDKqaJH9TAGfzt6b4aNt9fpirEcdpAF1bCedFfQmUZM0LG3rMtOAIhjEXgADt5GB8ZNK3BQl8BJyMmKs57oKmbVcODERCtPqjECXXsxH-az9nzxatPvcb7imFW8OlWslwr4IIRKdEjzEYs4syQJz7k2ktqOpYI5_UfYnw1s",
    d: "lJhwb0pKlB2ivyDFO6thajotClrMA3nxIiSkIUbvVr-TToFtha36gyF6w6e6YNXQXs4HhMRy1_b-nRQDk8G4_f5urd_q-pOn5u4KfmqN3Xw-lYD3ddi9qF0NLeTVUNVFASeP0FFqbPYfdNwD-LyvwjhtT_ggMOAw3mYvU5cBfz6-3uPdhl3CwQFCTgwOud_BA9p2MPMUHG82wMK_sNO1I0TYpjm7TnwNBwiKbMf-i5CKnuohgoYrEDYLeMg3f32eBljlCFNYaoCtT-mr1Ze0OTJND04vbfLotV-BBKulIpbOOSeVpKG7gJxZHmv7in7PE5_WzaxKFVoHW3wR6v_GzQ",
    dp: "KTWmTGmf092AA1euOmRQ5IsfIIxQ5qGDn-FgsRh4acSOGE8L7WrTrTU4EOJyciuA0qz-50xIDbs4_j5pWx1BJVTrnhBin9vNLrVo9mtR6jmFS0ko226kOUpwEVLgtdQjobWLjtiuaMW-_Iw4gKWNptxZ6T1lBD8UWHaPiEFW2-M",
    dq: "Jn0lqMkvemENEMG1eUw0c601wPOMoPD4SKTlnKWPTlQS6YISbNF5UKSuFLwoJa9HA8BifDrD-Mfpo1M1HPmnoilEWUrfwMqqdCkOlbiJQhKY8AZ16QGH50kDXhmVVa8BRWdVQWBTUzWXS5kXMaeskVzextTgymPcOAhXN-ph7MU",
    qi: "sRAPigJpl8S_vsf1zhJTrHM97xRwuB26R6Tm-J8sKRPb7p5xxNlmOBBFvWmWxdto8dBElNlydSZan373yBLxzW-bZgVp-B2RKT1B3WhTYW_Vo5DLhWi84XMncJxH7avtxtF9yksaeKe0e2n3J6TTan53mDg4KF8U0OEO2ciqO9g"
);

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

// Key sets
JwkSet keySet = new JwkSet(octKey, rsaKey);
keySet.Add(eccKey);

Converting between JWK and .NET key types

Library provides two way bridging with different .NET key types. One can construct Jwk from underlying ECDsa, RSA or CngKey (elliptic keys only)

// Cng keys
CngKey eccCngKey = CngKey.Open(...);
Jwk jwk = new Jwk(eccCngKey, isPrivate: false); //or 'true' by defaut

// RSA keys
RSA rsaKey=new X509Certificate2("my-key.p12", "password").GetRSAPublicKey();
Jwk jwk = new Jwk(rsaKey, isPrivate: false); //or 'true' by defaut

// ECDsa keys
ECDsa ecdsaKey = new X509Certificate2("ecc521.p12", "password").GetECDsaPublicKey();
Jwk jwk = new Jwk(ecdsaKey, isPrivate: false); //or 'true' by defaut

or convert Jwk key to corresponding ECDsa, RSA or CngKey (elliptic keys only)

// Returns ephemeral exportable CngKey, handle is cached for subsequent calls
CngKey cngKey = jwk.CngKey(usage: CngKeyUsages.KeyAgreement); // or 'CngKeyUsages.Signing' by default

// Returns backing ECDsa key, constructs new on demand, cached for subsequent calls
ECDsa ecdaKey = jwk.ECDsaKey();

// Returns backing RSA key, constructs new on demand, cached for subsequent calls
RSA rsaKey = jwk.RsaKey();

Working with certificate chains

Direct interface with X509Certificate2 class is provided when working with chains in JWK:

X509Certificate2 root = new X509Certificate2("root.p12");
X509Certificate2 intermidiary = new X509Certificate2("inter.p12");
X509Certificate2 signing = new X509Certificate2("signing.p12");

Jwk key = new Jwk();

// set chain at once
key.SetX509Chain(new List<X509Certificate2>{ root, intermidiary });

// or add one by one
key.Add(signing);

// Read chain from key as list of certificates
List<X509Certificate2> test = key.GetX509Chain();

Helpers to set SHA-1 and SHA-256 thumbprints:

X509Certificate2 signing = new X509Certificate2("signing.p12");

Jwk key = new Jwk();

// Calculate and set certificate SHA-1 thumbprint
key.SetX5T(signing);

// Calculate and set certificate SHA-256 thumbprint
key.SetX5TSha256(signing);

Extra params

In addition to named params from RFC 7517 library allows to include any custom key value params to the key objects:

Jwk key = new Jwk();

// add custom params
key.OtherParams = new Dictionary<string, object>();
key.OtherParams["s"] = "2WCTcJZ1Rvd_CJuJripQ1w";
key.OtherParams["c"] = 4096;

// or read them from the key same way
string s = (string)key.OtherParams["s"];

Searching JwkSet with Linq

JwkSet is Linq compatible and it is preffered way to locate keys of interest within collection:

JwkSet keySet = new JwkSet(....);

IEnumerable<Jwk> rsaKeys =
    from key in keySet
    where key.Alg == "enc" && key.Kty == Jwk.KeyTypes.RSA
    select key;

Examples

  1. Decrypt symmetric JWK of Oct type from payload and use it for further signing:
    string token = "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMmMiOjgxOTIsInAycyI6Inp3eUF0TElqTXpQM01pQ0giLCJlbmMiOiJBMjU2R0NNIn0.4geBbNNUErAkSiNmUVL23tnH3Jah0B0QkvhAaEcHeUgxRGKmWvkOjg.CCNy7C1HOH-qq5Lo.Uzi9FZ_b8bHenXF7h-D63gZCASdvLA7WqnKRSXwsr7G94SnB5bHiZrUT.l6D2hJSoFPpnXPXLyOloxg";

    // Decrypt symmetric key in payload with PBES2
    Jwk key = Jose.JWT.Decode<Jwk>(token, "secret");

    // And use it to sign new messages
    string signed = JWT.Encode(@"{""hello"": ""world""}", key, JwsAlgorithm.HS512);
  1. Use Jwk from token header for verification
string token = "eyJhbGciOiJSUzI1NiIsImp3ayI6eyJrdHkiOiJSU0EiLCJlIjoiQVFBQiIsIm4iOiJxRlp2MHBlYV9qbjVNbzRxRVVtU3R1aGx1bHNvOG4xaW5YYkVvdGRfelRyUXA5SzBSSzBoZjd0MEs0QmpLVmhhaXFJYW00dFZWUXZrbVllQmVZcjFNbW5PXzBOOTdkTUJ6XzdmbXZ5djBoZ0hhQmRRNW1SNXUzTFRsSG84dGpSRTctR3pabUdzNmpNY3lqN0hiWG9iRFBRSlpwcU55NkpqbGlEVlh4VzhuV0pEZXR4R0JscW1UajFFMWZyMlJDc1pMcmVET1BTREllZEcxdXB6OVJyYVNoc0lEemVlZk9jS2liY0FhS2VlVkkzcmtBVThfbU9hdUxTWHYzN2hsazBoNnNTdEpiM3FaUVh5T1VrVmtqWElraHZOdV92ZTB2N0xpTFQ0R19PeFlHenBPUWNDbmltS2RvanpOUDZHdFZEYU1QaC1Ra1NKRTMyVUNvczlSM3dJMlEifX0.eyJoZWxsbyI6ICJ3b3JsZCJ9.BX7lG2iQvYM7vO_DTsPPWbuTTpBP9dsDmEITd_Ofq9Ds8ucWrDVUjlMHBXUCfgTuoHHfeNtFE7sfuLxd0RseEY2Q4OnoFyJC_Gc63DwhrEvzY09i_sTTskc5rfQK9s32K595WjIceWnJh6s03dVEPmBWl_xwihV56LRzy4m8c15d1ZMlNByjpLibPGSVoJT4ae64Ux25hhbEageO-6gsTaYH9zofP3WGUzGf5PGq6nBtmrlQgyPhTkxzB1DUUBx0cA5IpnzQLwEDljKrgKRGn86TUrQc5dlIIKETZcTCnF2-CXq3oiqF81oEkFxfcW2yX5H0kmZmY_dQkKs1JR65yA";

// Grab 'jwk' header
var headers = Jose.JWT.Headers(token);

// And turn it into key
Jwk publicKey = Jwk.FromDictionary(
    (IDictionary<string, object>)headers["jwk"]
);

// ATTENTION: always ensure this is the key you know and expect from partner
// EnsureKnownKey(publicKey);

// Use it to decode payload and verify signature
string payload = Jose.JWT.Decode(token, publicKey);
  1. Fetching key set from jwks endpoint and locating verification one by thumbprint:
HttpClient client = new HttpClient()

// Grab public keys from partner endpoint
string keys = await client.GetStringAsync("https://acme.com/.well-known/jwks.json");

JwkSet jwks = JwkSet.FromJson(keys, JWT.DefaultSettings.JsonMapper);

// Get hint from token headers
var headers = Jose.JWT.Headers(token);

// Find matching public key by thumbprint
Jwk pubKey = (
    from key in jwks
    where key.Alg == Jwk.KeyUsage.Signature &&
            key.KeyOps != null && key.KeyOps.Contains(Jwk.KeyOperations.Verify) &&
            key.Kty == Jwk.KeyTypes.RSA &&
            key.X5T == (string)headers["x5t"]
    select key
).First();

// Finally verify token
var payload = Jose.JWT.Decode(token, pubKey);

Additional utilities

Unencoded and detached content (aka RFC 7797)

As of v2.5.0 library support b64 header to control payload decoding and encoding and optional content detaching.

Encoding can be controlled with optional JwtOptions parameter, that support:

  • DetachPayload - whether we'd like to omit payload part in token produced (false by default)
  • EncodePayload - whether to apply base64url encoding to payload part (true by default)

Options can be mixed in any combinations. To match RFC 7797:

string token = Jose.JWT.Encode(json, secretKey, JwsAlgorithm.HS256, options: new JwtOptions { DetachPayload = true, EncodePayload = false});

or just skip payload for instance:

string token = Jose.JWT.Encode(json, secretKey, JwsAlgorithm.HS256, options: new JwtOptions { DetachPayload = true });

Decoding automatically respect b64 header if present. In case of detached payload one can provide optional payload param:

string token = "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJSUzI1NiJ9..iyormYw6b0zKjx4K-fpeZO8xrLghkeUFMb2l4alz03CRLVdlXkdeKVG7N5lBbS-kXB4-8hH1ELFA5fUJzN2QYR6ZZIWjDF77HYTw7lsyjTJDNABjBFn-BIXlWatjNdgtRi2BZg2q_Wos87ZQT6Sl-h5hvxsFEsR0kGPMQ4Fjp-sxOyfnls8jAlziqmkpN-K6I3tK2vCLCQgnaN9sYrsIcrzuEA30YeXsgUe3m44yxLCXczXWKE3kgGiZ0MRpVvKOZt4B2DZLcRmNArhxjhWWd1nKZvv8c7kN0TqOjcNEUGWzwDs4ikCSz1aYKaLPXgjzpKnzbajUM117F3aCAaWH9g";

// will echo provided payload back as return value, for consistency
string json = Jose.JWT.Decode(token, PubKey(), payload: @"{""hello"": ""world""}");

// as of v5 can also be used with Verify:
string json = Jose.JWT.Verify(token, PubKey(), payload: @"{""hello"": ""world""}");

also works with binary payloads:

string token = "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJSUzI1NiJ9..ToCewDcERVLuqImwDkOd9iSxvTC8vzh-HrhuohOIjWMrGpTZi2FdzVN4Ll3fb2Iz3s_hj-Lno_c6m_7VcmOHfRLC9sPjSu2q9dbNkKo8Zc2FQmsCBdQi06XGAEJZW2M9380pxoYKiJ51a4EbGl4Ag7lX3hXeTPYRMVifacgdlpg2SYZzDPZQbWvibgtXFsBsIqPd-8i6ucE2eMdaNeWMLsHv-b5s7uWn8hN2nMKHj000Qce5rSbpK58l2LNeWw4IR6wNOqSZfbeerMxq1u0p-ZKIQxP24MltaPjZtqMdD4AzjrP4UCEf7VaLSkSuNVSf6ZmLmE_OYgQuQe7adFdoPg";

// will echo provided payload back as return value, for consistency
byte[] payload = Jose.JWT.DecodeBytes(token, PubKey(), payload: BinaryPayload);

Adding extra headers

jose-jwt allows to pass extra headers when encoding token to overide deafault values*. extraHeaders: named param can be used, it accepts IDictionary<string, object> type. jose-jwt is NOT allow to override alg and enc headers.

var payload = new Dictionary<string, object>()
{
     { "sub", "[email protected]" },
     { "exp", 1300819380 }
};

var headers = new Dictionary<string, object>()
{
     { "typ", "JWT" },
     { "cty", "JWT" },
     { "keyid", "111-222-333"}
};

var secretKey = new byte[]{164,60,194,0,161,189,41,38,130,89,141,164,45,170,159,209,69,137,243,216,191,131,47,250,32,107,231,117,37,158,225,234};

string token = Jose.JWT.Encode(payload, secretKey, JweAlgorithm.A256GCMKW, JweEncryption.A256CBC_HS512, extraHeaders: headers);
var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var headers = new Dictionary<string, object>()
{
     { "typ", "JWT" },
     { "cty", "JWT" },
     { "keyid", "111-222-333"}
};

var privateKey=new X509Certificate2("my-key.p12", "password", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet).PrivateKey as RSACryptoServiceProvider;

string token=Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.RS256, extraHeaders: headers);

* For backwards compatibility signing uses pre-configured typ: 'JWT' header by default.

Two-phase validation

In some cases validation (decoding) key can be unknown prior to examining token content. For instance one can use different keys per token issuer or rely on headers information to determine which key to use, do logging or other things.

jose-jwt provides helper methods to examine token content without performing actual integrity validation or decryption.

IDictionary<string, object> Jose.JWT.Headers(String token) to return header information as dictionary and T Jose.JWT.Headers<T>(string token) to return headers information as unmarshalled type.

string Jose.JWT.Payload(string token) to return unparsed payload and T Jose.JWT.Payload<T>(string token) to return unmarshalled payload type. Those 2 methods works only with signed tokens and will throw JoseException when applied on encrypted token.

With JWE JSON (RFC 7516) serialized tokens JweToken JWE.Headers() method can be used for same purpose. It will parse JSON structure into JweToken object and pre-populate effective headers (JweRecipient.JoseHeader property, see JWE) per every recipient in token. But will not perform actual decryption or integrity verification.

Security warning: please note, you should NOT rely on infromation extracted by given helpers without performing token validation as second step.

Below are couple examples on how two-phase validation can be implemented with jose-jwt:

//step 1a: get headers info
var headers = Jose.JWT.Headers(token);

//step 1b: lookup validation key based on header info
var key = FindKey(headers["keyid"]);

//step 2: perform actual token validation
var payload = Jose.JWT.Decode(token, key);
//step 1a: get payload as custom JwtToken object
var jwt = Jose.JWT.Payload<JwtToken>(token);

//step 1b: lookup validation key based on issuer
var key = FindKeyByIssuer(jwt.Iss);

//step 2: perform actual token validation
var payload = Jose.JWT.Decode<JwtToken>(token, key);
// Validate token with a public RSA key published by the IDP as a list of JSON Web Keys (JWK)
// step 0: you've read the keys from the jwks_uri URL found in http://<IDP authority URL>/.well-known/openid-configuration endpoint
Dictionary<string, ServiceStack.Text.JsonObject> keys = GetKeysFromIdp();

// step 1a: get headers info
var headers = Jose.JWT.Headers(token);

// step 1b: lookup validation key based on header info
var jwk = keys[headers["keyid"]];

// step 1c: load the JWK data into an RSA key
RSACryptoServiceProvider key = new RSACryptoServiceProvider();
key.ImportParameters(new RSAParameters
{
    Modulus = Base64Url.Decode(jwk["n"]),
    Exponent = Base64Url.Decode(jwk["e"])
});

// step 2: perform actual token validation
var paylod = Jose.JWT.Decode(token, key);
// Validate JWE JSON token with dynamic key

// step 1a: parse token, extract public JWK set and keyid for some recipient
var parsed = JWE.Headers(token);
var keysUrl = parsed.UnprotectedHeader["jku"];
var keyId = parsed.Recipients[0].Header["keyid"];

// step 1b: find/retrieve/ensure actual key for decryption
var key = FindKey(keysUrl, keyId);

// step 2: perform actual token validation
var payload = JWE.Decrypt(token, key).Plaintext;

Strict validation

It is possible to use strict validation before decoding a token. This means that you will specify which algorithm and possibly encryption type you are expecting to receive in the header. If the received header doesn't match with the types that you have specified an exception will be thrown and the parsing will be stopped.

Additionally starting v5 jose-jwt offering dedicated methods:

  • JWT.Verify(), JWT.VerifyBytes() - same as JWT.Decode() but works only with signed tokens, use when you want to explicitly restrict only to signing algs.
  • JWT.Decrypt(), JWT.DecryptBytes() - same as JWT.Decode() but works only with encrypted tokens, use when you want to explicitly restrict only to encryption algs.

Both can be additionally combined with strict validation.

Example of how to strictly validate an encrypted token:

string token = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..Fmz3PLVfv-ySl4IJ.LMZpXMDoBIll5yuEs81Bws2-iUUaBSpucJPL-GtDKXkPhFpJmES2T136Vd8xzvp-3JW-fvpRZtlhluqGHjywPctol71Zuz9uFQjuejIU4axA_XiAy-BadbRUm1-25FRT30WtrrxKltSkulmIS5N-Nsi_zmCz5xicB1ZnzneRXGaXY4B444_IHxGBIS_wdurPAN0OEGw4xIi2DAD1Ikc99a90L7rUZfbHNg_iTBr-OshZqDbR6C5KhmMgk5KqDJEN8Ik-Yw.Jbk8ZmO901fqECYVPKOAzg";

byte[] secretKey = new byte[] { 164, 60, 194, 0, 161, 189, 41, 38, 130, 89, 141, 164, 45, 170, 159, 209, 69, 137, 243, 216, 191, 131, 47, 250, 32, 107, 231, 117, 37, 158, 225, 234 };

string json = Jose.JWT.Decode(token, secretKey, JweAlgorithm.DIR, JweEncryption.A256GCM);

// starting v5 also applies to dedicated methods
string json = Jose.JWT.Decrypt(token, secretKey, JweAlgorithm.DIR, JweEncryption.A256GCM);

Example of how to strictly validate a signed token:

string token = "eyJhbGciOiJFUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.EVnmDMlz-oi05AQzts-R3aqWvaBlwVZddWkmaaHyMx5Phb2NSLgyI0kccpgjjAyo1S5KCB3LIMPfmxCX_obMKA";

byte[] x = { 4, 114, 29, 223, 58, 3, 191, 170, 67, 128, 229, 33, 242, 178, 157, 150, 133, 25, 209, 139, 166, 69, 55, 26, 84, 48, 169, 165, 67, 232, 98, 9 };
byte[] y = { 131, 116, 8, 14, 22, 150, 18, 75, 24, 181, 159, 78, 90, 51, 71, 159, 214, 186, 250, 47, 207, 246, 142, 127, 54, 183, 72, 72, 253, 21, 88, 53 };

var publicKey = EccKey.New(x, y);

string json = Jose.JWT.Decode(token, publicKey, JwsAlgorithm.ES256);

// starting v5 also applies to dedicated methods
string json = Jose.JWT.Verify(token, secretKey, JwsAlgorithm.ES256);

Working with binary payload

It is possible to encode and decode JOSE objects that have a payload consisting of arbitrary binary data. The methods that work with a binary payload have the Bytes suffix in the name to distinguish them in cases of potential ambiguity, e.g. EncodeBytes().

Example of working with signed binary payloads in JOSE objects:

var payload = new byte[] { 1, 2, 3, 0, 255 };
var signingKey = Convert.FromBase64String("WbQs8GowdRX1zYCFi3/VuQ==");

// Encoding a token with a binary payload.
var token = Jose.JWT.EncodeBytes(payload, signingKey, Jose.JwsAlgorithm.HS256);

// Reading the binary payload from a token (with signature verification).
var decoded = Jose.JWT.DecodeBytes(token, signingKey);

// Starting v5: reading the binary payload from a token (with signature verification) with explicit method
var decoded = Jose.JWT.VerifyBytes(token, signingKey);

// Starting v5: reading the binary payload from a token (with decryption) with explicit method
var decoded = Jose.JWT.DecryptBytes(token, signingKey);

// Reading the binary payload from a token (without signature verification).
decoded = Jose.JWT.PayloadBytes(token);

Parsing and mapping json to object model directly

jose-jwt library is agnostic about object model used to represent json payload as well as underlying framework used to serialize/parse json objects. Library provides convinient generic methods to work directly with your object model:

MyDomainObject obj=Jose.JWT.Decode<MyDomainObject>(token,secretKey); //will invoke configured IJsonMapper to perform parsing/mapping of content to MyDomainObject

string data=Jose.JWT.Encode(obj,secrectKey,JwsAlgorithm.HS256); //for object argument configured IJsonMapper will be invoked to serialize object to json string before encoding

Potential security risk

While deserializing a token, if a field is not provided in token (may due to payload schema changes), the field will remain its default value. This is Newtonsoft.Json's behavior. This hehavior is quite dangerous, which could give attacker chances.

Suppose the payload class is Payload.

class Payload
{
    public int UserId { get; set; }
}

Later the Payload class is changed to:

class Payload
{
    public int Id { get; set; } // UserId -> Id
}

Now, if the library deserializes a token issued before the change of Payload class, proterty Id is not provided in the token and will remain its default value 0. The payload data will be: {Id = 0}.

The user will get someone else's identity (id: 0) .

So developers should always use nullable data types for payload class properties.

Settings

As of v2.3.0 settings can be configured either globally or on a per-call basis using a JwtSettings object. The JWT.DefaultSettings object can be modified to change global settings, or a JwtSettings instance can be passed to any public method on JWT to override the global settings for particular method call. It is possible to provide custom implementations of:

  • specific signing JwtSettings.RegisterJws(alg, impl)
  • encryption, JwtSettings.RegisterJwe(alg, impl)
  • key management JwtSettings.RegisterJwa(alg, impl)
  • or compression JwtSettings.RegisterCompression(alg, impl)
  • json mapper JwtSettings.RegisterMapper(mapper)

as well as specify aliases when decoding tokens from 3rd party libraries that do not comply exactly to spec:

  • signing JwtSettings.RegisterJwsAlias(header, alg)
  • encryption JwtSettings.RegisterJweAlias(header, alg)
  • key management JwtSettings.RegisterJwaAlias(header, alg)
  • compression JwtSettings.RegisterCompressionAlias(header, alg)

Example of JWTSettings

// global setting
Jose.JWT.DefaultSettings.JsonMapper = new Jose.NewtonsoftMapper();


Jose.JWTSettings settings = new Jose.JwtSettings();
settings.JsonMapper = new Jose.JSSerializerMapper();

// override global settings for this call
Jose.JWT.Decode(token, secretKey, settings: settings);

//or simply
Jose.JWT.Decode(token, secretKey, settings: new JwtSettings().RegisterMapper(new Jose.JSSerializerMapper()));

// as of v5
Jose.JWT.Verify(token, secretKey, settings: settings);
Jose.JWT.Decrypt(token, secretKey, settings: settings);

Customizing json <-> object parsing & mapping

The library provides simple Jose.IJsonMapper interface to plug any json processing library or customize default behavior. The only requirement for mapping implementations is ability to correctly serialize/parse IDictionary<string,object> type.

The default supplied Jose.IJsonMapper implementation is based on System.Web.Script.Serialization.JavaScriptSerializer.

Example of Newtonsoft.Json mapper

public class NewtonsoftMapper : IJsonMapper
{
    public string Serialize(object obj)
    {
         var settings = new JsonSerializerSettings
         {
         	ContractResolver = new DictionaryKeysResolver(),
         	NullValueHandling = NullValueHandling.Ignore,
         };

        return JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
    }

    public T Parse<T>(string json)
    {
        var settings = new JsonSerializerSettings
        {
            ContractResolver = new DictionaryKeysResolver(),
            NullValueHandling = NullValueHandling.Ignore,
        };

        return JsonConvert.DeserializeObject<T>(json, settings);
    }
}

Jose.JWT.DefaultSettings.JsonMapper = new NewtonsoftMapper();

Example of ServiceStack mapper

public class ServiceStackMapper : IJsonMapper
{
    public string Serialize(object obj)
    {
        return ServiceStack.Text.JsonSerializer.SerializeToString(obj);
    }

    public T Parse<T>(string json)
    {
        return ServiceStack.Text.JsonSerializer.DeserializeFromString<T>(json);
    }
}

Jose.JWT.DefaultSettings.JsonMapper = new ServiceStackMapper();

Customizing algorithm implementations

The default implementations of any of the signing, encryption, key management, or compression algorithms can be overridden.

Example of custom algorithm implementation

public class CustomKeyManagement : IKeyManagement
{
    public byte[] Unwrap(byte[] encryptedCek, object key, int cekSizeBits, IDictionary<string, object> header)
    {
        // implement custom key unwrapping (e.g. using Amazon KMS for instance)
    }

    public byte[][] WrapNewKey(int cekSizeBits, object key, IDictionary<string, object> header)
    {
        // implement custom key wrapping (e.g. using Amazon KMS for instance)
    }
}

...

// set default RSA-OAEP key management to use custom implementation
Jose.JWT.DefaultSettings.RegisterJwa(JweAlgorithm.RSA_OAEP, new CustomKeyManagement());

Providing aliases

It is possible to add any number of aliases when decoding for signing, encryption, key management, or compression algorithms. For example if you are dealing with tokens produced from 3rd party library which you have no control over and by mistake it is using RSA_OAEP_256 header value instead of RSA-OAEP-256 it is possible to register alias:

   Jose.JWT.Decode(token, key, settings: new JwtSettings().RegisterJwaAlias("RSA_OAEP_256", JweAlgorithm.RSA_OAEP_256));

Multiple calls can be chained for more convinience:

Jose.JWT.Decode(token, secretKey, settings: new JwtSettings()
						.RegisterMapper(customMapper)
						.RegisterJws(JwsAlgorithm.RS256, amazonKmsImpl)
						.RegisterJws(JwsAlgorithm.RS384, amazonKmsImpl)
						.RegisterJws(JwsAlgorithm.RS512, amazonKmsImpl)
						.RegisterJwa(JweAlgorithm.RSA_OAEP_256, hsmImpl)
						.RegisterJwe(JweEncryption.A128GCM, linuxGcmImpl)
						.RegisterJwaAlias("RSA_OAEP_256", JweAlgorithm.RSA_OAEP_256)
						.RegisterCompression(JweCompression.DEF, hardwareAcceleratedDeflate)
);

Customizing library for security

In response to ever increasing attacks on various JWT implementations, jose-jwt as of version v4.1 and beyond introduced number of additional security controls to limit potential attack surface on services and projects using the library.

Deregister algorithm implementations

One can use following methods to deregister any signing, encryption, key management or compression algorithms from runtime suite, that is considered unsafe or simply not expected by service.

  • JwtSettings.DeregisterJws(JwsAlgorithm alg) - to remove signing algorithm
  • JwtSettings.DeregisterJwa(JweAlgorithm alg)) - to remove key management algorithm
  • JwtSettings.DeregisterJwe(JweEncryption alg) - to remove signing algorithm
  • JwtSettings.DeregisterCompression(JweCompression alg) - to remove signing algorithm
   JWT.DefaultSettings.DeregisterJws(JwsAlgorithm.none)
                      .DeregisterJwe(JweAlgorithm.RSA1_5)
                      .DeregisterJwe(JweAlgorithm.DIR)
                      .DeregisterCompression(JweCompression.DEF);

Customizing compression

There were denial-of-service attacks reported on JWT libraries that supports deflate compression by constructing malicious payload that explodes in terms of RAM on decompression. See for details: #237

As of v5 jose-jwt limits decompression buffer to 250Kb to limit memory consumption and additionaly provides a way to adjust the limit according to specific scenarios:

    // Override compression alg with new limits (10Kb example)
    Jose.JWT.DefaultSettings.RegisterCompression(JweCompression.DEF, new DeflateCompression(10 * 1024));

Customizing PBKDF2

As it quite easy to abuse PBES2 family of algorithms via forging header with extra large p2c values, jose-jwt library introduced iteration count limits in v4.1 to reduce runtime exposure.

By default, maxIterations is set according to OWASP PBKDF2 Recomendations

    PBES2-HS256+A128KW: 310000
    PBES2-HS384+A192KW: 250000
    PBES2-HS512+A256KW: 120000

, while minIterations kept at 0 for backward compatibility.

If it is desired to implement different limits, it can be achieved via registering Pbse2HmacShaKeyManagementWithAesKeyWrap implementation with different parameters:

    Jose.JWT.DefaultSettings
        // Pick your own min/max limits
        .RegisterJwe(JweAlgorithm.PBES2_HS256_A128KW, new Pbse2HmacShaKeyManagementWithAesKeyWrap(128, new AesKeyWrapManagement(128), 310000, 310000));
        .RegisterJwe(JweAlgorithm.PBES2_HS384_A192KW, new Pbse2HmacShaKeyManagementWithAesKeyWrap(192, new AesKeyWrapManagement(192), 250000, 250000));
        .RegisterJwe(JweAlgorithm.PBES2_HS512_A256KW, new Pbse2HmacShaKeyManagementWithAesKeyWrap(256, new AesKeyWrapManagement(256), 120000, 120000));

In case you can't upgrade to latest version, but would like to have protections against PBES2 abuse, it is recommended to stick with Two-phase validation precheck before decoding:

    IDictionary<string, object> headers = Jose.JWT.Headers(token);

    string alg = (string)headers["alg"];
    long p2c = Convert.ToInt32(headers["p2c"]);

    if(alg.StartsWith("PBES2-") && p2c > 310000)
    {
        // potentially can be forged/abused token
    }
    else
    {
        // continue with decoding routine
        Jose.JWT.Decode(token, key);
    }

Confusion attacks and how to nail them

There are number of algorithm confusion attacks reported in general on different JWT libraries in recent years. Typically attacks exploits public keys published on server side (or obtained by other means) via forging bogus JWT tokens signed or encrypted with given public key but with tampered alg header to confuse server validation implementation (hence the attack name). For example take a look at #236

By nature most of confusion attacks targeting specific usage of libraries rather then libraries itself, as library can't predict in what type of applications and conditions it will be used.

Here are some design practices to consider in your applications to avoid confusion attacks with jose-jwt:

  1. Clearly separate your signing and encryption keys. Do not allow to use signing keys to decrypt tokens and vice versa.

  2. When you can, be explicit whether you working with signed or encrypted tokens. As of v5 library provides dedicated verification methods: Verify() and Decrypt()

  3. Use Strict Validation and assert algorithms explicitly if possible.

  4. Always good idea to deregister algorithms you are not planning to use to limit attack surface.

  5. For highly dynamic environments consider two-phase validation practice to implement more flexible protection measures.

More examples

Checkout UnitTests/TestSuite.cs for more examples.

Dealing with keys

Below is collection of links and approaches to nail down some common questions around key management:

RSACryptoServiceProvider

When you dealing with RSACryptoServiceProvider you can face Invalid algorithm specified exception while performing signing or encryption operations. The reason usually is that underneath RSACryptoServiceProvider is not using Microsoft Enhanced RSA and AES Cryptographic Provider. There are several ways to fix that:

  1. re-import RSAParameters:
public static RSACryptoServiceProvider FixCSP(RSACryptoServiceProvider key)
{
    var privKey = key.PrivateKey;

    RSACryptoServiceProvider newKey = new RSACryptoServiceProvider();
    newKey.ImportParameters(privKey.ExportParameters(true));

    return newKey;
}

The limitation of this approach is that private key should be marked exportable. It is not recommended for production environments but can be handy for testing.

  1. Enforce correct CSP:
public static RSACryptoServiceProvider FixCSP(RSACryptoServiceProvider key)
{
    var privKey = key.PrivateKey;
    var enhCsp = new RSACryptoServiceProvider().CspKeyContainerInfo;
    var cspParams = new CspParameters(enhCsp.ProviderType, enhCsp.ProviderName, privKey.CspKeyContainerInfo.KeyContainerName);

    return new RSACryptoServiceProvider(cspParams);
}

For more details see: http://stackoverflow.com/questions/7444586/how-can-i-sign-a-file-using-rsa-and-sha256-with-net

  1. Actually use certificate which supports SHA-2, see http://hintdesk.com/c-how-to-fix-invalid-algorithm-specified-when-signing-with-sha256/ for details how to create one.

If you have only RSA private key

E.g. if you don't have .p12 file where certificate is combined with private key that can be loaded via X509Certificate2 but rather have only private key:

-----BEGIN RSA PRIVATE KEY-----
............................
-----END RSA PRIVATE KEY-----

Then take a look at: http://www.donaldsbaconbytes.com/2016/08/create-jwt-with-a-private-rsa-key/

Strong-Named assembly

jose-jwt is not providing standalone strong-named assembly as of now. If you need one in your project, please take a look at #5

Usually people have success with https://github.com/brutaldev/StrongNameSigner

ASP.NET Core MVC JWT Authentication

Securing Controllers Using AuthorizeAttribute

ASP.NET Team provides Microsoft.AspNetCore.Authentication.JwtBearer that can be used to authorize web service routes using JWT Tokens created using JOSE-JWT that are passed via Authorize: Bearer HTTP header.

In startup.cs, you can add JWT Authorization middleware by passing options to the services.AddAuthentication extension method in void Configure method.

Below is the example for setting up the middleware using HS-* signed token:

using System;
using System.Text;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Builder;

// The key length needs to be of sufficient length, or otherwise an error will occur.
var tokenSecretKey = Encoding.UTF8.GetBytes(Configuration["TokenSecretKey"]);

var tokenValidationParameters = new TokenValidationParameters
{
    // Token signature will be verified using a private key.
    ValidateIssuerSigningKey = true,
    RequireSignedTokens = true,
    IssuerSigningKey = new SymmetricSecurityKey(tokenSecretKey),

    // Token will only be valid if contains "accelist.com" for "iss" claim.
    ValidateIssuer = true,
    ValidIssuer = "accelist.com",

    // Token will only be valid if contains "accelist.com" for "aud" claim.
    ValidateAudience = true,
    ValidAudience = "accelist.com",

    // Token will only be valid if not expired yet, with 5 minutes clock skew.
    ValidateLifetime = true,
    RequireExpirationTime = true,
    ClockSkew = new TimeSpan(0, 5, 0),

    ValidateActor = false,
};

services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(options => { options.TokenValidationParameters = tokenValidationParameters; });

After that, your Controllers or Actions can be secured by using [Authorize] attribute.

In addition, certain JWT reserved claims will be automatically be populated into HttpContext.User as the following Claim Type (from System.Security.Claims namespace):

JWT Claim Name Data Type Claim Type
sub string ClaimTypes.NameIdentifier
email string ClaimTypes.Email
unique_name string ClaimTypes.Name
roles List<string> ClaimTypes.Role

This list is anything but complete. There might be more claims that are transformed but not listed yet.

Therefore, you can use role-based authorization as well, for example: [Authorize(Roles = "Administrator")]

If you wish to do more than one type of authentication to separate routes, you should use app.UseWhen, for example:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseStaticFiles();
    AuthenticateUI(app);
    AuthenticateAPI(app);
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

public void AuthenticateAPI(IApplicationBuilder app)
{
    // IsAPI method returns TRUE when a request route is started with "/api".
    // For those routes, we'll use JWT Authorization:
    app.UseWhen(context => IsAPI(context), builder =>
    {
        builder.UseJwtBearerAuthentication(new JwtBearerOptions
        {
            AutomaticAuthenticate = true,
            TokenValidationParameters = tokenValidationParameters,
        });
    });
}

public void AuthenticateUI(IApplicationBuilder app)
{
    // For non-API routes, we'll use Cookie Authorization, as an example.
    app.UseWhen(context => !IsAPI(context), builder =>
    {
        builder.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationScheme = "Accelist_Identity",
            LoginPath = new PathString("/auth/login"),
            AutomaticAuthenticate = true,
            AutomaticChallenge = true
        });
    });
}

Creating and Using a JWT Token

We can use an MVC web API to accept a request containing a user's credentials in exchange for a JWT token.

public class TokenRequest
{
    [Required]
    public string Username { set; get; }

    [Required]
    public string Password { set; get; }
}

[Route("api/v1/token")]
public class TokenApiController : Controller
{
    private readonly AuthService AuthService;

    public TokenApiController(AuthService authService)
    {
        // AuthService is your own class that handles your application's authentication functions.
        // AuthService is injected via Controller's constructor and registered At startup.cs
        // Read more: https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/dependency-injection
        this.AuthService = authService;
    }

    [HttpPost]
    public async Task<IActionResult> Post([FromBody]TokenRequest model)
    {
        if (ModelState.IsValid == false)
        {
            return BadRequest("Username and password must not be empty!");
        }

        // Authenticates username and password to your SQL Server database, for example.
        // If authentication is successful, return a user's claims.
        var claims = await AuthService.TryLogin(model.Username, model.Password);
        if (claims == null)
        {
            return BadRequest("Invalid username or password!");
        }

        // As an example, AuthService.CreateToken can return Jose.JWT.Encode(claims, YourTokenSecretKey, Jose.JwsAlgorithm.HS256);
        var token = AuthService.CreateToken(claims);
        return Ok(token);
    }
}

Therefore, by sending a HTTP POST request containing Username and Password to that endpoint, you will receive a token that is signed by the server.

Example for making a request from the client using JavaScript Promise using AngularJS $http:

$http.post("/api/v1/token", {
    username: "foo",
    password: "bar"
}).then(function(response) {
    // Request successful.
    MyToken = response.data;
}, function(response) {
    // Request failed! Do something with the response.
});

Then later, you can use the obtained token for sending requests to secured routes by attaching it to the request header.

$http.post("/api/v1/function", {
    foo: "bar"
}, {
    headers: {
        Authorization: "Bearer " + MyToken
    }
}).then(function(response) {
    // Request successful. Do something with the response.
}, function(response) {
    // Request failed! Do something with the response.
});

jose-jwt's People

Contributors

andresjacobo avatar anthocis avatar btrepp avatar claq2 avatar coryflucas avatar crclz avatar csrakowski avatar danatkinson avatar dvsekhvalnov avatar freeman avatar gjermystreeva avatar kek-sec avatar landaire avatar madsj avatar mariusgundersen avatar marta-krzyk-dev avatar mschmidt-healthx avatar robmen avatar ryanelian avatar sandersaares avatar ukeha avatar xav-cary avatar zzyzy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jose-jwt's Issues

JWT.Decode with JweAlgorithm.ECDH_ES & JweEncryption.A256GCM

I'm having trouble following the C# examples provided to create and decode a token when using elliptical encryption. I've posted the test file I used up at https://gist.github.com/jehrenzweig/95a176f00236be526f2641e19c2582a7 -- encoding works fine, but when I try to decode the token the following exception is thrown:

An unhandled exception of type 'System.Security.Cryptography.CryptographicException' occurred in System.Core.dll

Additional information: Key does not exist.

Content type for Nested JWT

First, my compliments on the library. It's extremely useful and saved me a ton of time.

When a JWE contains a nested JWT, per the spec https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32 the cty: header parameter is required and must be "JWT". There appears to be no way to adjust the JOSE header to include the content type parameter in this library.

A small nit to be sure but would be great to have an optional parameter or overload that supports this.

Thanks again for the great contribution.

How to create encrypted JWT in C# using RS256 with RSA private key

I am using the jose-jwt library and want to create an encrypted JWT in C# using the RS256 algorithm for encryption. I have no experience with cryptography, so please excuse my ignorance. I see the following example in the docs:

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var privateKey=new X509Certificate2("my-key.p12", "password", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet).PrivateKey as RSACryptoServiceProvider;

string token=Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.RS256);

which shows the use of a p12 file, but how do I use an RSA key file of the form below? I am looking at the docs for X509Certificate2, but I see no option for RSA private keys.

-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2
pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX
GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il
AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF
L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k
X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----

Finally, what is the difference between the two options listed in the docs, and how do I choose between the two?

-------------------------- OPTION 1 --------------------------

RS-* and PS-* family

CLR:

RS256, RS384, RS512 and PS256, PS384, PS512 signatures require
RSACryptoServiceProvider (usually private) key of corresponding
length. CSP need to be forced to use Microsoft Enhanced RSA and AES
Cryptographic Provider. Which usually can be done be re-importing
RSAParameters. See http://clrsecurity.codeplex.com/discussions/243156
for details.

-------------------------- OPTION 2 --------------------------

CORECLR: RS256, RS384, RS512 signatures require RSA (usually private) key of corresponding length.

Support JWK

Is there any plans to support JSON Web Keys (JWK) in the future?

Add support for Windows (Phone) 8.1 Apps

The current version of your library only supports the standard .NET framework. Windows 8 or Windows Phone 8.1 apps cannot use System.Security.Cryptography but have to rely on Windows.Security.Cryptography. This API offers similar functionalities but works in a slightly different way.

I have created a quick modified version of your framework to rely on this new API (I removed or disabled most of the functionality I didn't needed).

Example of a modification I have done:
https://github.com/pieterderycke/FxSyncNet/blob/master/JWT.Universal/jws/RsaUsingSha.cs

Could you make support for Windows (Phone) 8.1 official?

A tip if you would want to add support:

One project for .NET 4.x with all the files added and you could work with conditional compilation symbols to implement specific functionalities in different ways depending on the framework (standard .NET vs. WinRT API's).

A second project (type Windows Runtime Component) with all the files added as linked items. Other conditional compilation symbols defined to allow the other code to be compiled. (See for an example: https://github.com/pieterderycke/Jace/blob/master/Jace/Util/MemoryCache.cs)

Your NuGet package could then add the correct DLL depending on the type of application the user is adding it to (.NET 4.x vs Windows (Phone) 8.1)

strong name

Can you release this with a strong name?
We've had issues in the past with the wrong versions of DLLs being deployed and we use strong names to ensure that the right versions of DLLs are deployed. That's not so much an issue with your DLL, but if your DLL is not strong named, it'll mean we have to turn off strong names in our assemblies.
Cheers,
Clay

Support arbitrary payload

The assumption in the API currently seems to be that the payload is a JSON object (either serialized by jose-jwt or by the caller). This is more restrictive than the actual JOSE specifications, which allow any arbitrary data to be processed.

It would be desirable to use jose-jwt with arbitrary data (e.g. in the form of a byte[] API).

Sign AND encrypt token

EDIT: I may have found a bug in my code, just a moment!

I'm trying to sign and then encrypt a token. I saw #15, but that method doesn't appear to be working. Any ideas?

If I only sign the token, no encryption, the token is valid.

Generating the token:

var signedToken = Jose.JWT.Encode(payload, Globals.JwtSigningKey, Jose.JwsAlgorithm.HS256);
var encryptedToken = Jose.JWT.Encode(signedToken, Globals.JwtEncryptionKey, Jose.JweAlgorithm.DIR, Jose.JweEncryption.A256GCM);
return encryptedToken;

Decoding the token:

var decryptedToken = Jose.JWT.Decode(token, Globals.JwtEncryptionKey, Jose.JweAlgorithm.DIR, Jose.JweEncryption.A256GCM);
var decodedJson = Jose.JWT.Decode(decryptedToken, Globals.JwtSigningKey);
return decodedJson;

The generated token looks like this:

eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..BCS3zCOB6GoY92cF.6mgN34S6aGzOGzQ9ye_jL6ee3Ez8UrvdVnLzCY3N0EfDoZXzEBAUjZc6DfMsuES3K9J8FsU7NbrsIEAfi5Kh4YcvSZ0c28bly6PmbWtk5wzZhgX95n1ovMnuJsVfGyg7gKqw5ZOJ_MJIWKDEX1UrN93wWl95q4SDF98dTF4-g_a7SGzgqfLIVK3q7-Gkm6NCNhVmp46_t97DMMhleWHzqNoOB6_FeyJ6PrFlS_toHSgrq-1ysPh4l8OO9D4C--QrEu2PE4-m3Rt4-PvAtnZl-bPINgv073DArN3ZSB8EfEi-zBYcU7s8RXXs_6uawRS0z3c3L2z3I-kTBLH-_Lkawb7GyEFqNp8fx-hKvLXMx6M.3b2jBZKQYUQ1gde11b_DzA

encode/decode using streams

currently unable to work with large blocks of data. would be nice to have this as a stream reader/writer.

Decode without Verify

More of a question than an issue. I use the Issuer claim (iss) in the incoming jwt to pull a shared secret from a local DB for signature verification. I'd like to use Decode for this but it doesn't have a path that bypasses signature verification. So a couple of questions:

  1. Is there a reason I shouldn't do this? We're not encrypting here, just verifying signature in the end but I work with multiple partners. Yes key pairs would be better but not an option right now.
  2. If doing this is ok, is a non-verifying overload of Decode something to consider or should I just GetString the payload part of the token directly?

Thanks in advance.
Frank

Enable support for non-exportable keys in .NET 4.6 via GetRSAPrivateKey()

.NET Framework 4.6 introduced X509Certificate2.GetRSAPrivateKey() which returns an RSACng and enables modern cryptographic algorithms to be used without the need to re-import keys. It would be desirable for jose-jwt to support this natively, as exportable keys are a security problem.

Not sure how to best integrate this, especially as it seems to be .NET 4.6+ only (or perhaps I am mistaken). Regardless, I am attaching a sample project that demonstrates the implementation by overriding the relevant IKeyManagement and IJwsAlgorithm implementations.

ConsoleApplication5.zip

Invalid cast to string after JWT.jsMapper.Parse

Inside private static byte[] DecodeBytes(string token, object key = null, JwsAlgorithm? jwsAlg = null, JweAlgorithm? jweAlg = null, JweEncryption? jweEnc = null)

There's the line:

string algorithm = (string) JWT.jsMapper.Parse<Dictionary<string, object>>(Encoding.UTF8.GetString(bytes1))["alg"];

(Looking at this through R# in VS, possible it's not char for char exact)

So I see you're looking for alg, which you expect to be a string. However you're not deserializing Dictionary<string,string>. I assume this is for supporting the extraHeaders.

Using https://github.com/kevin-montrose/Jil deserializing Dictionary<string, object>, the objects created are dynamic objects since you asked for an object and string. This dynamic object is not directly castable to string. It does return the expected value when you use .ToString()

My recommendation would be changing this line of code to be similar to

var o = JWT.jsMapper.Parse<Dictionary<string, object>>(Encoding.UTF8.GetString(bytes1))["alg"];

string algorithm =  o as string ?? o.ToString();

To the repo maintainer(s): Can you create a GitHub release for v2.0.2?

First of all, thank you so much for this robust JWT library! I was, probably like many others, a reluctant user of System.IdentityModel.Tokens.Jwt in the past, but that library seems to be no longer maintained.

I work for a company that has a very strict OSS rules. We are not allowed to use NuGet packages that cannot be tied back to a specific GitHub release. We're currently using jose-jwt 2.0.2 from nuget.org. Do you think it's possible that you could create an official release on GitHub so that we can continue to use your great library? I noticed you have branches for a few different versions but, unfortunately, that won't satisfy our gatekeepers. :(

Invalid algorithm specified and enhanced RSA Provider

Testing JOSW-JWT I was getting "Invalid algorithm specified" errors with certificates that were working fine using Microsoft's JwtSecurityTokenHandler.

Following the advice of http://hintdesk.com/c-how-to-fix-invalid-algorithm-specified-when-signing-with-sha256/ I regenerated the certificates using the -sp "Microsoft Enhanced RSA and AES Cryptographic Provider" -sy 24 options in makecert and then was able to generate the token with JOSE-JWT. And also to verify with JOSE-JWT the token generated with JwtSecurityTokenHandler and viceversa.

By I wonder why it is necessary to specify the enhanced provider for JOSE-JWT and not for JwtSecurityTokenHandler.

Xamarin iOS issue

When adding 2.01 to a Xamarin project - I get

Could not install package 'System.Security.Cryptography.Cng 4.2.0'. You are trying to install this package into a project that targets 'Xamarin.iOS,Version=v1.0', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.

C# JWT ecdsa import private key

I am having difficulty loading an existing ecdsa (ES256) private key PEM file into a CNG key which is a requirement to call the encode method.

The directions state "the CngKey loaded via CngKey.Open(..) method from Key Storage Provider", but I haven't had any luck doing so. Since I don't have a key storage provider setup I was using the CngKey.Import() method instead, but that is returning an error "Invalid Parameter".

Code below:

byte[] b = Convert.FromBase64String(privateKey); CngKey cng = CngKey.Import(b, CngKeyBlobFormat.EccPrivateBlob); string token = Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.ES256);

Can someone please point me in the right direction? Thanks!

Support all .NET Standard platforms with a single codebase

It would be desirable to have a single jose-jwt codebase/package that works on all .NET Standard platforms. The idea being that if I make my own .NET Standard library that uses jose-jwt functionality, I can reference jose-jwt and it will "just work" on all .NET Standard platforms that my library itself targets.

For example, UWP support would be desirable. On installing the library into a brand new UWP project (just blank UWP app), I get the following error message:

Restoring packages for 'App8'.
Restoring packages for C:\Source\App8\App8\project.json...
System.Security.Cryptography.Csp 4.0.0 provides a compile-time reference assembly for System.Security.Cryptography.Csp on UAP,Version=v10.0, but there is no run-time assembly compatible with win10-arm.
One or more packages are incompatible with UAP,Version=v10.0 (win10-arm).
System.Security.Cryptography.Csp 4.0.0 provides a compile-time reference assembly for System.Security.Cryptography.Csp on UAP,Version=v10.0, but there is no run-time assembly compatible with win10-arm-aot.
System.Reflection.Emit.ILGeneration 4.0.1 provides a compile-time reference assembly for System.Reflection.Emit.ILGeneration on UAP,Version=v10.0, but there is no run-time assembly compatible with win10-arm-aot.
One or more packages are incompatible with UAP,Version=v10.0 (win10-arm-aot).
System.Security.Cryptography.Csp 4.0.0 provides a compile-time reference assembly for System.Security.Cryptography.Csp on UAP,Version=v10.0, but there is no run-time assembly compatible with win10-x64.
One or more packages are incompatible with UAP,Version=v10.0 (win10-x64).
System.Security.Cryptography.Csp 4.0.0 provides a compile-time reference assembly for System.Security.Cryptography.Csp on UAP,Version=v10.0, but there is no run-time assembly compatible with win10-x64-aot.
System.Reflection.Emit.ILGeneration 4.0.1 provides a compile-time reference assembly for System.Reflection.Emit.ILGeneration on UAP,Version=v10.0, but there is no run-time assembly compatible with win10-x64-aot.
One or more packages are incompatible with UAP,Version=v10.0 (win10-x64-aot).
System.Security.Cryptography.Csp 4.0.0 provides a compile-time reference assembly for System.Security.Cryptography.Csp on UAP,Version=v10.0, but there is no run-time assembly compatible with win10-x86.
One or more packages are incompatible with UAP,Version=v10.0 (win10-x86).
System.Security.Cryptography.Csp 4.0.0 provides a compile-time reference assembly for System.Security.Cryptography.Csp on UAP,Version=v10.0, but there is no run-time assembly compatible with win10-x86-aot.
System.Reflection.Emit.ILGeneration 4.0.1 provides a compile-time reference assembly for System.Reflection.Emit.ILGeneration on UAP,Version=v10.0, but there is no run-time assembly compatible with win10-x86-aot.
One or more packages are incompatible with UAP,Version=v10.0 (win10-x86-aot).
Package restore failed for 'App8'.
Package restore failed. Rolling back package changes for 'App8'.
========== Finished ==========
Time Elapsed: 00:00:01.9820806

Ability to customise algorithm and encryption mappings

Hello,

We use a vendor provided authentication solution where the JWT do not correctly implement the specifications. Namely the alg and enc headers are not set to the correct value.

Currently the mapping between the header values and the algorithm used is static and seems not modifiable by user of the library.

It seems there is some code for aiming in this direction in JWT.cs but it does not seem to be publicly accessible.

Would you be interested in a PR allowing to customize the mappings ?

Error while loading assembly: A strongly-named assembly is required

Hello,

I cannot used jose-jwt in my Asp.net 4.5.2 signed web application because it is requiring a signed assembly for jose-jwt.
If I remove signature on my web app it works.
Can you please sign your assembly for us to be able to use it?
Or maybe there is another way to do it?

Thank you.

Update NuGet package to support RS

I would want to use your library in an application requiring JWT with RS256. The version on NuGet does not yet support this. Could you update your NuGet package?

Changes in 2.3.0 break API

The changes in #61 added some optional parameters (e.g. Payload, Decode), which breaks compatibility with existing calling code. Calls to these methods generate MissingMethodExceptions. If you're following semantic versioning, the major version should be incremented. I suspect the intent wasn't to break compatibility, though, so perhaps overloads should be added and the 2.3.0 package should be pulled.

JwsAlgorithm.RS256 Signing Failed in .NET Framework 4.6+

jose-notsupported

Then I saw this MSDN:

https://msdn.microsoft.com/en-us/library/system.security.cryptography.rsa.decryptvalue(v=vs.110).aspx

NotSupportedException: This method call is not supported. This exception is thrown starting with the .NET Framework 4.6.
Remarks: In the .NET Framework 4.5.2 and earlier versions of the .NET Framework, this method was abstract. However, the only implementation by a derived class, RSACryptoServiceProvider.DecryptValue, also threw a NotSupportedException exception.

RS256 - Verify with a public key

Sorry this a dumb question.
Can we use a file containing a public cert only to verify that the key is valid?

scenario:
services have only access to the public key, and auth server has access to the public/private key.
auth server signs and hands out jwt tokens, clients use them on services which services verify using the public cert.

I would assume that this a common problem. But your docs say

Encode

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var privateKey=new X509Certificate2("my-key.p12", "password", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet).PrivateKey as RSACryptoServiceProvider;

string token=Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.RS256);

Decode

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var privateKey=new X509Certificate2("my-key.p12", "password", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet).PrivateKey as RSACryptoServiceProvider;

string token=Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.RS256);

both of which I understand is using the private key? or is the doc not correct.
I'm just looking to confirm we can use the public cert to decode and verify.
I'd try myself but we aren't using .p12 files, so there is extra work involved in loading the .key /.cert files

Signed and crypted JWT

This is more a question than a real issue.
Wondering if (and how) I could generate a token which is signed with key 1 and crypted with key 2 (and of course with specific algorithms).

Strong naming

Hi,

I'm hoping to get a strong-named version of this file to integrate into an enterprise project.

There are a few options to get this, and I'd like to know what your preferred method is:

Thanks.

Dotnetcore: RSA from X509Certificate2?

Apologies if this is an idiotic question, and it may not even be an issue per se, but maybe someone else will learn from this as well.

I'm using core and trying to decode a JWT token utilizing a RS256 public key stored inside a certificate file. I've read the cert into a X509Certificate2, but I'm not really sure how to convert that to requisite RSA type for the Jose.JWT.Decode method. In standard .NET, it appears that you can massage a X509Certificate2 into a RSACryptoServiceProvider and utilize that for decoding, but I'm not really sure where to go from where I am.

Any insight?

Here's what I'm trying (paraphrased a bit)

var byte[] keyContents;  //this is set somewhere else
var string token; //this is also set somewhere else
var cert = X509Certificate2(keyContents, "", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
var derp = Jose.JWT.Decode(token, cert.PublicKey);  // this blows up because PublicKey is not a RSA

CngKey from *.p8 file

Hi,

Im not exactly sure if this is issue for jose-jwt library, but i dont know where else i can find answer.

I try to create token with:
string token = Jose.JWT.Encode(payload, key, JwsAlgorithm.ES256, headers);
It accepts key as CngKey object. But i only have *.p8 file in which is this content:


-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgj6QsqUei+3Zz9sER
MrG/hQ511dfa3425vsdfvasdf+asqw33oZIzj0DAQehRANCAATHizygGyqvLkbv
3EkjCW1++MItok65sNjiUBKQQuN1SKi5Kn4NjbZ55zX5EfiELBq+PieeyDtUsPut
UGvS8FUT
-----END PRIVATE KEY-----

It is private key for signing tokens for Apple Push Notifications interface.
How can i construct CngKey object out of that file? I tried like this:

string privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgj6QsqUei+3Zz9sERMrG/hQ511dfa3425vsdfvasdf+asqw33oZIzj0DAQehRANCAATHizygGyqvLkbv3EkjCW1++MItok65sNjiUBKQQuN1SKi5Kn4NjbZ55zX5EfiELBq+PieeyDtUsPutUGvS8FUT"; if (CngKey.Exists(privateKey)) { key = CngKey.Open(privateKey); } else { key = CngKey.Create(CngAlgorithm.ECDsaP256, privateKey); }

This creates a key file in keystore, but thing does not work very well. I know this is not the right approach.
Can you please provide some help? It may be useful for future jose-jwt features, like creating signed tokens with ES256 with only private key in string format.

Summary: I cannot construct right CngKey object out of *.p8 file in which is private key.

Thanks

.NET Core

Are there plans to support .NET Core. I know there are still in the beta phase, but for my project this would be a criteria, because we want to use .NET Core once all our dependencies are compatible with it.

[BUG] - RS256 Invalid algorithm specified

Env:

  • jose-jwt 2.0.1
  • .NET framework 4.6.1
  • Use jwt-2048.p12 pem file in UnitTest project

Calling to this method:

var payload = new Dictionary<string, object>()
            {
                { "sub", "[email protected]" },
                { "exp", 1300819380 }
            };

var privateKey = new X509Certificate2(@"C:\path\to\jwt-2048.p12", "1", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet).PrivateKey as RSACryptoServiceProvider;

string token = Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.RS256);

Throwing this exception

**Unhandled Exception: System.Security.Cryptography.CryptographicException: Invalid algorithm specified.

at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash, Int32 cbHash, ObjectHandleOnStack retSignature)
at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash)
at System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, Int32 calgHash)
at System.Security.Cryptography.RSAPKCS1SignatureFormatter.CreateSignature(Byte[] rgbHash)
at Jose.RsaUsingSha.Sign(Byte[] securedInput, Object key)
at Jose.JWT.EncodeBytes(Byte[] payload, Object key, JwsAlgorithm algorithm, IDictionary2 extraHeaders) at Jose.JWT.Encode(String payload, Object key, JwsAlgorithm algorithm, IDictionary2 extraHeaders)
at Jose.JWT.Encode(Object payload, Object key, JwsAlgorithm algorithm, IDictionary`2 extraHeaders)**

AesEnc/Dec for NetStandard 1.4

Hi

Can you give an estimate on when AesEnc/Dec functionality (jose-jwt/crypto/AesKeyWrap.cs) is going to implemented for NetStandard 1.4?

/Christian

Which encryption works with FIPS?

We use JweAlgorithm.A256KW, JweEncryption.A256CBC_HS512 encryption.
On FIPS enabled environment this fails with:

Message=System.InvalidOperationException: This implementation is not part of the Windows Platform FIPS

validated cryptographic algorithms.
at System.Security.Cryptography.AesManaged..ctor()
at Jose.AesKeyWrap.AesEnc(Byte[] sharedKey, Byte[] plainText)
at Jose.AesKeyWrap.Wrap(Byte[] cek, Byte[] kek)
at Jose.AesKeyWrapManagement.WrapNewKey(Int32 cekSizeBits, Object key, IDictionary2 header) at Jose.JWT.Encode(String payload, Object key, JweAlgorithm alg, JweEncryption enc, Nullable1

compression, IDictionary`2 extraHeaders)

Which encryption is FIPS compliant?

Question: Encrypted tokens with ASP.NET core

So I think this might be more of a ASP.NET core question than jose-jwt - but I configured the asp.net core with UseJwtBearerAuthentication as in the front page. This works fine if I send in a unencrypted, signed token but fails when I send in an encrypted token. I've tried

String encryptionKey = "hex string here";
byte[] key = encryptionKey.ToByteArray();
tokenValidationParams.TokenDecryptionKey = new SymmetricSecurityKey(key);

but this just ends in IDX10600: Decryption failed. There are no security keys for decryption.

Is there an example showing how to use encrypted tokens?

Option to entirely remove headers

I would like to remove the alg and typ headers since they expose internal security details unnecessarily. Either the messages match my configured algorithm or they don't. This also has a side benefit of a reduction in message size, which every byte counts when it comes to headers sent with every single mobile request.

NotSupportedException

I'm getting NotSupportedException with the message "Method is not supported." when running an application using the jose-jwt library on any computer other than my development computer. It works perfectly on the development computer. The entire build directory contents is being placed on the test computer, but is still generating the error.

Below is the code generating the error (some things have been changed like the payload contents since they contain sensitive information).

DateTimeOffset expiration = new DateTimeOffset(DateTime.Now);

var payload = new Dictionary<string, object>()
{
    { "sub", "[email protected]" },
    { "exp", expiration.ToUnixTimeSeconds() }
};
RSA pKey = new X509Certificate2(@"C:\directory\comb.pfx").GetRSAPrivateKey();
return Jose.JWT.Encode(payload, pKey, Jose.JwsAlgorithm.RS256);

Readme - code examples

Hi,
in your code examples is (except in the first one) a variable named "payload" which is called "json" later...

var payload = new Dictionary<string, object>()  // "payload"
{
    { "sub", "[email protected]" },
    { "exp", 1300819380 }
};

var secretKey = new byte[]{164,60,194,0,161,189,41,38,130,89,141,164,45,170,159,209,69,137,243,216,191,131,47,250,32,107,231,117,37,158,225,234};

string token=Jose.JWT.Encode(json, secretKey, JwsAlgorithm.HS256) // "json"

Netcore: ConcatKDF and ECDH key management

Right now, ConcatKDF (SP800_56A_CONCAT) is not supported under netcore. And therefore ECDH key management family of algorithms unsupported as well.

Fix as netcore supports concatkdf or find other way to implement it directly in a library.

Get CngKey from X509Certificate2

I have an .Net Core1.0 application and was trying to decode the JWT token using Jose.Decode() method. I am having a X509Certificate2 type certificate against which the token should be validated.
Most of the samples decodes the token using the below code:
`var publicKey = new X509Certificate2(@"certs\certificate_pub.crt", "", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet).PublicKey.Key;

var decodedToken = JWT.Decode(token, publicKey);`

When I tried the same code it seems like there is no PublicKey.Key property, instead the PublicKey.EncodedKeyValue is available. What should I do to make the Decode working? How the certificate should be manipulated for getting the required key?
image
Any help would be appreciated.

Linux support

I've been using this as I develop on my Windows machine and it's awesome! I'm publishing to an Ubuntu machine, however, and I'm getting an exception that bcrypt.dll could not be found. That's obviously something on Windows machines and not Ubuntu. Is there something I can do to fix this? I tried manually copying bcrypt.dll from my Windows machine, the same error occurs.

I'm specifically calling this code to encrypt my tokens. Signing works correctly, but encrypting does not.

// JwtSigningKey is a byte array
// JwtEncryptionKey is a byte array

var payload = new Jwt
{
    // create JWT
};

var signedToken = JWT.Encode(payload, JwtSigningKey, JwsAlgorithm.HS256);

return JWT.Encode(signedToken, JwtEncryptionKey, JweAlgorithm.DIR, JweEncryption.A256GCM, JweCompression.DEF);

Why managed crypto implementations instead of CSP or CNG?

I noticed there's a few places where XXXManaged implementations are being used instead of CSP or CNG. Is there a particular reason for that? Would it be reasonable to switch those out to use CSP or CNG? I've got a fork where I've been testing these changes: master...SteveSyfuhs:feature/convert-to-provider

E.g.

https://github.com/dvsekhvalnov/jose-jwt/blob/a77bc9bec23964cceb58fe2b581250672ab9667b/JWT/jwe/AesCbcHmacEncryption.cs
https://github.com/dvsekhvalnov/jose-jwt/blob/a77bc9bec23964cceb58fe2b581250672ab9667b/JWT/crypto/AesKeyWrap.cs
https://github.com/dvsekhvalnov/jose-jwt/blob/a77bc9bec23964cceb58fe2b581250672ab9667b/JWT/jws/RsaUsingSha.cs

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.