Reuse DataProtectionProvider to create JWT SigningCredentials #24966
-
In an ASP.NET Core + Angular application, I am generating a custom JWT, signed with a random signing key. The random signing key is currently generated like this: const int numberOfBits = 256;
var secureRandomData = new byte[numberOfBits / 8];
RandomNumberGenerator.Fill(secureRandomData);
var key = new SymmetricSecurityKey(secureRandomData);
return new SigningCredentials(key, SecurityAlgorithms.HmacSha256); Side info: the token is not used for authentication, but for some kind of temporary user-elevation mechanism: the BE generates a temporary token which the front-end includes in a HTTP header for every API request, as long as the token is valid, the elevation takes place. Of course, each time we restart the back-end (local development) or re-deploy (qa, production environment), any elevation token in the wild is no longer valid (signing key no longer matches). I would like to bind the SigningKey to the machine or application pool, and if I remember correctly, this is exactly how What I thought would work is this: const int numberOfBits = 256;
IDataProtector dataProtector = _dataProtectionProvider.CreateProtector(purpose: "user-elevation");
byte[] keyData = dataProtector.Protect(new byte[numberOfBits / 8]);
var key = new SymmetricSecurityKey(keyData);
return new SigningCredentials(key, SecurityAlgorithms.HmacSha256); But it doesn't as So two questions:
|
Beta Was this translation helpful? Give feedback.
Replies: 4 comments
-
The DataProtection APIs are an authenticated encryption with additional data (AEAD) mechanism (see docs for implementation details). Every call to AEAD mechanisms can be used to produce a MAC (a symmetric "signature"). This is normally done by passing a zero-length plaintext and feeding all of the to-be-signed data into the additional data argument of the algorithm. In the DataProtection stack, this additional data argument is the "purpose" string; though I strongly suggest reading the docs about best practices when generating these strings, including prepending However, this means that the MAC ("signature") would be computed or validated fully by the AEAD algorithm itself. There's by design no way to export a stable key - such as an HMACSHA256 key - from this system. You'd have to use whatever extensibility mechanism your JWT library provides to say "I'm not going to give you a raw key. Instead, call back into me every time you need to generate or validate a signature." This should solve the problem, but I don't know how much of an undertaking this would be. /cc @HaoK @blowdart for identity-related questions and all things JWT |
Beta Was this translation helpful? Give feedback.
-
As Levi explains it would not be possible for you to use data protection, nor do we plan to make it possible as it would be contrary to what we aimed data protection at, a way to hide keys and encryption mechanisms away from a user so they don't have to make choices. |
Beta Was this translation helpful? Give feedback.
-
Thanks both for answering my question. I'll go ahead and have the back-end reply with something like a 4XX HTTP response whenever the token seems no longer valid because of a system restart. |
Beta Was this translation helpful? Give feedback.
-
Eh, I wouldn't say it's "not possible". As with any AEAD crypto system, you could use DataProtection (or even ASP.NET 4.5's |
Beta Was this translation helpful? Give feedback.
The DataProtection APIs are an authenticated encryption with additional data (AEAD) mechanism (see docs for implementation details). Every call to
Protect
introduces some randomness by design, which will result in unique outputs, even given identical inputs.AEAD mechanisms can be used to produce a MAC (a symmetric "signature"). This is normally done by passing a zero-length plaintext and feeding all of the to-be-signed data into the additional data argument of the algorithm. In the DataProtection stack, this additional data argument is the "purpose" string; though I strongly suggest reading the docs about best practices when generating these strings, including prepending
typeof(T).FullName
…