Confusion with encrypt, validation, and decrypt of JWE using JWT and Microsoft.IdentityModel.Tokens classes

I have a specific use-case where I need to JSON serialize a payload class, encrypt, pass along via handshakes between multiple application APIs in disparate domains, and each receiver must validate and decrypt the serialized class for rehydration and usage within the APIs. Leveraging the foundation and features of JWTs was a logical choice as the carrier for this payload. JWE was selected to gain confidentiality in the JWT’s payload.
Keys will be generated with legitimate certs for actual production implementation. Existing software infra is mostly all MS dotnet core.

I’m struggling specifically with the usage of the dotnet 8 Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor class and the JsonWebTokenHandler.CreateToken method as well as the Decrypt method. Details below:

Specific question:
How do you select the proper CreateToken method? Looking at the docs, it has 13 different signatures, which implies the developers wanted to support a wide array of implementations. MS’s documentation here: JsonWebTokenHandler.CreateToken Method I must be grossly misinterpreting something to not understand this. I need to supply a serialized payload string, but I also need to supply SecurityDescriptor data for the receiver to validate. None of the 13 signatures allow for both, so I must be going about this incorrectly.

Specific Issue To Address #1:
It appears that to use validation in JsonWebTokenHandler.ValidateTokenAsync() on the JWE receiving side of the application/API app, you must supply data for the Issuer and Audience and they must match that same data that was used during token creation. The IssueAt and Expires datetime stamps must be correct as well (e.g. not out of range at reception and validation time.)

Current setup

JWE creation app set up to use RSA private key for encryption read from PEM file and an ECC key for signing, read from a PEM file and both were generated by openssl:

string rsaKeys;
using (var streamReader = System.IO.File.OpenText(@"rsa-crypt.pem"))
   rsaKeys = streamReader.ReadToEnd();

string eccSigningKey;
using (var streamReader = System.IO.File.OpenText(@"ecc-crypt.pem"))
eccSigningKey = streamReader.ReadToEnd();

var privateEncKeyId = "somerandomid1234";
var privateKey = RSA.Create();
privateKey.ImportFromPem(rsaKeys.ToCharArray());

var privateSigningKeyId = "somerandomid5678";
var privateSignKey = ECDsa.Create(ECCurve.NamedCurves.nistP256);
privateSignKey.ImportFromPem(eccSigningKey.ToCharArray());

var privateEncryptionKey = new RsaSecurityKey(privateKey) { KeyId = privateEncKeyId };
var publicEncryptionKey = new RsaSecurityKey(privateKey.ExportParameters(false)) { 
    KeyId = privateEncKeyId };
var privateSigningKey = new ECDsaSecurityKey(privateSignKey) { KeyId = 
    privateSigningKeyId };
var publicSigningKey = new 
    ECDsaSecurityKey(ECDsa.Create(privateSignKey.ExportParameters(false))) { KeyId = 
    privateSigningKeyId };

var signingCredentials = new SigningCredentials(
    privateSigningKey, SecurityAlgorithms.EcdsaSha256);

var encryptingCredentials = new EncryptingCredentials(
    publicEncryptionKey, SecurityAlgorithms.RsaOAEP, 
    SecurityAlgorithms.Aes256CbcHmacSha512);

var mySerializedClassPayload = "{"id":1,"phone":"1112223333"}";

At this point I can use the following CreateToken method with this signature

var handler = new JsonWebTokenHandler();
    handler.SetDefaultTimesOnTokenCreation = true;
    handler.TokenLifetimeInMinutes = 1;
var token = handler.CreateToken(mySerializedClassPayload, 
    signingCredentials,encryptingCredentials,CompressionAlgorithms.Deflate);

Now I can attempt a validation operation

var result = decryptHandler.ValidateTokenAsync(token,
    new TokenValidationParameters{
    IssuerSigningKey = publicSigningKey,
    TokenDecryptionKey = privateEncryptionKey}
);

Even though I setDefaultTimesOnTokenCreation and TokenlifetimeInMinutes, the validation result shows IsValid=false with an error:

IDX10225: Lifetime validation failed. The token is missing an Expiration Time. Tokentype: ‘Microsoft.IdentityModel.JsonWebTokens.JsonWebToken’.”} System.Exception {Microsoft.IdentityModel.Tokens.SecurityTokenNoExpirationException

Ok, I can accept this for the moment, all good. Lets move on to the better example using “proper” JWT data which I know will pacify the Validation method in the example below.

Specific Issue to Address #2:
If I switch over to using the SecurityTokenDescriptor class, I have a different issue. Where do I provide my payload when using a SecurityTokenDescriptor? I can satisfy the Issuer, Audience, time expiry, and use claims (both outer and inner) for validation purposes, but I don’t seem to understand how to use this technique with a custom payload. For example, if I instantiate this way, and randomly toss the serialized class into the inner claims dictionary since it is in fact encrypted here, just as a sloppy experiment:

var descriptor = new Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor {
    Issuer = "test-issuer",
    Audience = "https://test.com",
    IssuedAt = DateTime.UtcNow,
    Expires = DateTime.UtcNow.AddMinutes(1),
    AdditionalHeaderClaims = new Dictionary<string, object> { { "test-outer-header- 
        claim", "abc123456" } },
    AdditionalInnerHeaderClaims = new Dictionary<string, object> { { "serializedclass", 
        mySerializedClassPayload } },
    EncryptingCredentials = encryptingCredentials,
    SigningCredentials = signingCredentials
};
var secondTestHandler = new JsonWebTokenHandler();
var secondTestToken = secondTestHandler.CreateToken(descriptor); //<-- CreateToken with descriptor AND a payload?

The validation here works OK since we used the SecurityDescriptor class, using this:

var validationHandler = new JsonWebTokenHandler();
var theToken = new JsonWebToken(secondTestToken);
var validationResult = validationHandler.ValidateTokenAsync(
    theToken,
    new TokenValidationParameters{
        ValidIssuer = "test-issuer",
        ValidAudience = "https://test.com",
        IssuerSigningKey = publicSigningKey,
        TokenDecryptionKey = privateEncryptionKey}
);

The Final Big Question:
How does one get the payload in the “right” region of the JWT? Sticking it in the inner claims collection couldn’t possibly be the right path. BUT, amazingly, in both of these examples #1 and #2, I’m able to decrypt the JWT and in both examples, actually see the serialized payload. In the first example, using this signature for CreateToken:

handler.CreateToken(mySerializedClassPayload, 
    signingCredentials,encryptingCredentials,CompressionAlgorithms.Deflate);

I’m able to see in the decryption result a section in the decoded JWT that says

Header: {
"alg": "ES256",
"kid": "somerandomid5678",
"typ": "JWT"
}
Payload: {
"id":1,
"phone":"1112223333"
}

which is good. It obviously fails validation due to lack of audience, time, and other headers not being available.

In the second example using this signature for CreateToken:

handler.CreateToken(descriptor);

Yes it passes validation using the TokenValidationParameters above, very excellent, and I’m able to see in the decryption result section the JWT and when decoded it contains:

Header: {
"alg": "ES256",
"kid": "somerandomid5678",
"serializedclass": "{"id":1,"phone":"1112223333"}",
"typ": "JWT"
}
Payload: {
"aud": "https://test.com",
"iss": "test-issuer",
"exp": 1725826344,
"iat": 1725826284,
"nbf": 1725826284
}

If you made it this far, congratulations, I know I just posted a ton of text. 🙂 I’m more than grateful for any direction or guidance on this.

1

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật