-
Notifications
You must be signed in to change notification settings - Fork 0
how it works hybrid encryption
This is something I never thought I'd ever implement in this solution
Some things I saw made me a little nervous:
- The customers api was wide-open
- The solution [at the time] wasn't running over https
This prompted me to watch a course on cryptography, Cryptography in .NET 6 by Stephen Haunts
Even after that, I needed to figure out if the private key that IdentityServer4 has can be accessed so I checked the docs, and it could be
With the customers api sitting in the same application as IdentityServer4, the ISigningCredentialStore
could be injected into the customers controller and handed over to the DecryptAsync
method to extract the private key and decrypt the EncryptedAesKey
That choice of keeping it together allows this to work
IdentityServer4 publishes the public key of the RSA key pair usually in the form of a json web key [JWK] usually used for token validation, however, another advantage here is that asymmetric encryption can be utilized
Now, hybrid encryption is implemented using both symmetric and asymmetric encryption, one question that you may be wanting to ask is, "Why not directly encrypt the data with asymmetric encryption and avoid symmetric encryption altogether?"
That would be a valid question to ask
The problem, however, is that if you try to encrypt data that is larger than the size of the key, it breaks, and we have no idea how large the data to encrypt is going to be
So to solve this, encrypt the data with symmetric encryption, and since that key will always be smaller than the asymmetric encryption key, it wouldn't break and the key used for symmetric encryption can then be encrypted with asymmetric encryption
The algorithm used for symmetric encryption is AesGcm
, and the advantage of using it is that manual integrity checking can be omitted as it fills an array [of 16 bytes size] with a tag it generated during encryption
That tag will be used to validate the integrity of the data during decryption
This can be accessed via the jwks url /.well-known/openid-configuration/jwks
at the Identity Service
Here's the public key, and what's important to create the RSA public key is the e
[exponent] and n
[modulus] values
Also worth pointing out is that those values are base64url encoded
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"e": "AQAB",
"n": "5Xm3AZI8rQYx3pCp0XSWPAYZHgE-eXjz26a3kRH0FjjOYJTS-H3oq8UW4KBJqX2vVBLxWKQH-3XdPkyY4IOrdfwY0OTZSyBODP-YygYrt9tj88hzMcEz2kSfUYJnkQvS02kwI6aQpNBksL4gf3dDpzAhC4Uspj7aEeeyTUC7u7_pxmtxxeYsolasNtAQlWFyR1voOyyKGVEah77NeypfL0Cml0XKqIlSjW8q1l1_eP-g_-KpkQt9fDO9tzjm4gwPyqXjg2XArpnCm5j6AiYAcvOlla13fEvUKjZ6g6jLOt7aiFoq3KjYRprtnuOOGLZ0fwUWGLtDxZBl9JFJRZnthQ",
"alg": "RS256"
}
]
}
Though this endpoint exists, in the EncryptAsync
method, the discovery document is retrieved and it's extracted from there
This is the result of the encryption process
public class EncryptedDataModel
{
public byte[] AesGcmNonce { get; set; } = RandomNumberGenerator.GetBytes(12);
public byte[] EncryptedAesKey { get; set; } = Array.Empty<byte>();
public byte[] AesGcmCipherText { get; set; } = Array.Empty<byte>();
public byte[] AesGcmTag { get; set; } = new byte[16];
}
The AesGcmNonce
has to be unique so it's created on the fly with the RandomNumberGenerator
, and the others are set during the encryption process
Since copying and pasting the code here will explain it well enough, I'll just link to it and provide a high level flowchart for each process
Also, these don't follow the Single-Responsibility Principal of SOLID, so as it's currently implemented, it cannot be extracted into a new library
A better way would be to handle the retrieval of the asymmetric keys outside of the EncryptAsync
and DecryptAsync
methods and pass it in as RSAParameters
Its method signature
public static async Task<EncryptedDataModel> EncryptAsync<T>(
this T model,
HttpClient client,
IConfiguration configuration,
ILogger logger) where T : class
{
// ...
}
The full implementation is here
flowchart TD
start[Start]
utf8json[Serialize Model to UTF8 JSON]
aesKey[Randomly Generate Aes Key]
cipherTextModelSize[Set AesGcmCipherText Size to Model Size]
aesGcm[Create AesGcm Instance with Aes Key]
aesGcmEncrypt[Encrypt Model with Symmetric Encryption]
discoveryDocument[Retrieve Discovery Document]
extractJwk[Extract JWK and Create RSA Parameters]
rsa[Create RSA Instance with RSA Parameters]
encryptAesKey[Encrypt Aes Key with Asymmetric Encryption]
returnEncryptedDataModel[Return EncryptedDataModel]
last[End]
start --> utf8json
utf8json --> aesKey
aesKey --> cipherTextModelSize
cipherTextModelSize --> aesGcm
aesGcm --> aesGcmEncrypt
aesGcmEncrypt --> discoveryDocument
discoveryDocument --> extractJwk
extractJwk --> rsa
rsa --> encryptAesKey
encryptAesKey --> returnEncryptedDataModel
returnEncryptedDataModel --> last
var response = await client.PostAsync("api/customers", JsonContent.Create(await registerModel.EncryptAsync(client, configuration, logger)));
Its method signature
public static async Task<T?> DecryptAsync<T>(
this EncryptedDataModel encryptedDataModel,
ISigningCredentialStore signingCredentialStore,
ILogger logger) where T : class
{
// ...
}
The full implementation is here
flowchart TD
start[Start]
extractRsaSecurityKey[Extract RSA Security Key from Credential Store]
rsa[Create RSA Instance with RSA Parameters]
aesKey[Decrypt Encrypted Aes Key]
modelAsBytes[Create Empty Byte Array Matching the Size of the Cipher Text]
aesGcm[Create AesGcm Instance with Aes Key]
attemptDecryption[Attempt Decryption with AesGcm]
succeeds{Decryption Succeeds?}
returnModelOfT[Deserialize Model to T and Return]
returnNull[Return Null]
last[End]
start --> extractRsaSecurityKey
extractRsaSecurityKey --> rsa
rsa --> aesKey
aesKey --> modelAsBytes
modelAsBytes --> aesGcm
aesGcm --> attemptDecryption
attemptDecryption --> succeeds
succeeds --> |Yes| returnModelOfT
succeeds --> |No| returnNull
returnModelOfT --> last
returnNull --> last
var registerModel = await encryptedDataModel.DecryptAsync<RegisterModel>(signingCredentialStore, logger);
-
How Encryption Works
- John Savill did a video on encryption that includes this type of hybrid encryption, and its major flaw
- Health Checks UI
- Mvc Frontend
- Web Backend-For-Frontend
- Address Service
- Address Worker
- Identity Service
- Order Service
- Order Worker
- Tyres Service