Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Windows support for AESGCM EncryptingCredentials #2083

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,27 @@ public static byte[] GenerateKeyBytes(int sizeInBits)
return key;
}

/// <summary>
/// Generates key bytes.
/// </summary>
public static byte[] GenerateAesGcmKeyBytes(int sizeInBits)
{
byte[] key = null;
if (sizeInBits != 128 && sizeInBits != 192 && sizeInBits != 256)
throw LogHelper.LogExceptionMessage(new ArgumentException(TokenLogMessages.IDX10402, nameof(sizeInBits)));

using (var aes = Aes.Create())
{
int sizeInBytes = sizeInBits >> 3;
key = new byte[sizeInBytes];
aes.KeySize = sizeInBits;
aes.GenerateKey();
Array.Copy(aes.Key, key, sizeInBytes);
}

return key;
}

internal static SecurityKey GetSecurityKey(
EncryptingCredentials encryptingCredentials,
CryptoProviderFactory cryptoProviderFactory,
Expand Down Expand Up @@ -362,9 +383,17 @@ internal static SecurityKey GetSecurityKey(
securityKey = new SymmetricSecurityKey(GenerateKeyBytes(384));
else if (SecurityAlgorithms.Aes256CbcHmacSha512.Equals(encryptingCredentials.Enc))
securityKey = new SymmetricSecurityKey(GenerateKeyBytes(512));

// only 128, 192 and 256 AesGcm for CEK algorithm
else if(SecurityAlgorithms.Aes128Gcm.Equals(encryptingCredentials.Enc))
securityKey = new SymmetricSecurityKey(GenerateAesGcmKeyBytes(128));
else if (SecurityAlgorithms.Aes192Gcm.Equals(encryptingCredentials.Enc))
securityKey = new SymmetricSecurityKey(GenerateAesGcmKeyBytes(192));
else if (SecurityAlgorithms.Aes256Gcm.Equals(encryptingCredentials.Enc))
securityKey = new SymmetricSecurityKey(GenerateAesGcmKeyBytes(256));
else
throw LogHelper.LogExceptionMessage(
new SecurityTokenEncryptionFailedException(LogHelper.FormatInvariant(TokenLogMessages.IDX10617, LogHelper.MarkAsNonPII(SecurityAlgorithms.Aes128CbcHmacSha256), LogHelper.MarkAsNonPII(SecurityAlgorithms.Aes192CbcHmacSha384), LogHelper.MarkAsNonPII(SecurityAlgorithms.Aes256CbcHmacSha512), LogHelper.MarkAsNonPII(encryptingCredentials.Enc))));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if I provide a custom CryptoProviderFactory with my own implementation of AuthenticatedEncryptionProvider, this code still throws and prevents me from creating tokens of type AESGCM

One alternate approach would be to have two separate PRs:

  1. Enable support for AES-GCM with custom CryptoProviderFactory. This would allow consumers of the API to plug-in their own AuthenticatedEncryptionProvider (e.g. Windows Interop / .NET AES GCM)

  2. A separate PR that adds support for AES-GCM Encryption. Later .NET APIs have a cross-platform API to call, but folks on .NET Framework would need to use Windows Interop

new SecurityTokenEncryptionFailedException(LogHelper.FormatInvariant(TokenLogMessages.IDX10617, LogHelper.MarkAsNonPII(SecurityAlgorithms.Aes128CbcHmacSha256), LogHelper.MarkAsNonPII(SecurityAlgorithms.Aes192CbcHmacSha384), LogHelper.MarkAsNonPII(SecurityAlgorithms.Aes256CbcHmacSha512), LogHelper.MarkAsNonPII(SecurityAlgorithms.Aes128Gcm), LogHelper.MarkAsNonPII(SecurityAlgorithms.Aes192Gcm), LogHelper.MarkAsNonPII(SecurityAlgorithms.Aes256Gcm), LogHelper.MarkAsNonPII(encryptingCredentials.Enc))));

kwProvider = cryptoProviderFactory.CreateKeyWrapProvider(encryptingCredentials.Key, encryptingCredentials.Alg);
wrappedKey = kwProvider.WrapKey(((SymmetricSecurityKey)securityKey).Key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,35 @@ internal bool ValidKeySize()

private AuthenticatedEncryptionResult EncryptWithAesGcm(byte[] plaintext, byte[] authenticatedData, byte[] iv)
{
throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10715, LogHelper.MarkAsNonPII(Algorithm))));
_ = _keySizeIsValid.Value;

byte[] nonce = new byte[Tokens.AesGcm.NonceSize];
byte[] cipherText = new byte[plaintext.Length];
byte[] authenticationTag = new byte[Tokens.AesGcm.TagSize];

using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
{
rng.GetBytes(nonce);
}

AesGcm aes = null;
try
{
aes = _aesGcmObjectPool.Allocate();
aes.Encrypt(nonce, plaintext, cipherText, authenticationTag, authenticatedData);
}
catch
{
Dispose(true);
throw;
}
finally
{
if (!_disposed)
_aesGcmObjectPool.Free(aes);
}

return new AuthenticatedEncryptionResult(Key, cipherText, nonce, authenticationTag);
}

private AesGcm CreateAesGcmInstance()
Expand Down
3 changes: 2 additions & 1 deletion src/Microsoft.IdentityModel.Tokens/LogMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ internal static class LogMessages
// public const string IDX10614 = "IDX10614:";
public const string IDX10615 = "IDX10615: Encryption failed. No support for: Algorithm: '{0}', SecurityKey: '{1}'.";
public const string IDX10616 = "IDX10616: Encryption failed. EncryptionProvider failed for: Algorithm: '{0}', SecurityKey: '{1}'. See inner exception.";
public const string IDX10617 = "IDX10617: Encryption failed. Keywrap is only supported for: '{0}', '{1}' and '{2}'. The content encryption specified is: '{3}'.";
public const string IDX10617 = "IDX10617: Encryption failed. Keywrap is only supported for: '{0}', '{1}', '{2}', '{3}', '{4}', and '{5}'. The content encryption specified is: '{6}'.";
public const string IDX10618 = "IDX10618: Key unwrap failed using decryption Keys: '{0}'.\nExceptions caught:\n '{1}'.\ntoken: '{2}'.";
public const string IDX10619 = "IDX10619: Decryption failed. Algorithm: '{0}'. Either the Encryption Algorithm: '{1}' or none of the Security Keys are supported by the CryptoProviderFactory.";
public const string IDX10620 = "IDX10620: Unable to obtain a CryptoProviderFactory, both EncryptingCredentials.CryptoProviderFactory and EncryptingCredentials.Key.CrypoProviderFactory are null.";
Expand All @@ -126,6 +126,7 @@ internal static class LogMessages
// Formating
public const string IDX10400 = "IDX10400: Unable to decode: '{0}' as Base64url encoded string.";
public const string IDX10401 = "IDX10401: Invalid requested key size. Valid key sizes are: 256, 384, and 512.";
public const string IDX10402 = "IDX10402: Invalid requested key size. Valid key sizes are: 128, 192, and 256.";

// Crypto Errors
public const string IDX10621 = "IDX10621: '{0}' supports: '{1}' of types: '{2}' or '{3}'. SecurityKey received was of type '{4}'.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,18 +437,16 @@ public static TheoryData<CreateTokenTheoryData> CreateJWEWithAesGcmTheoryData
};

tokenHandler.InboundClaimTypeMap.Clear();
var encryptionCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_AesGcm128;
encryptionCredentials.CryptoProviderFactory = new CryptoProviderFactoryMock();
return new TheoryData<CreateTokenTheoryData>
{
new CreateTokenTheoryData
{
First = true,
TestId = "AesGcm128EncryptionWithMock",
TestId = "AesGcm128Encryption",
TokenDescriptor = new SecurityTokenDescriptor
{
SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials,
EncryptingCredentials = encryptionCredentials,
EncryptingCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_AesGcm128,
Subject = new ClaimsIdentity(Default.PayloadClaims),
TokenType = "TokenType"
},
Expand All @@ -463,6 +461,27 @@ public static TheoryData<CreateTokenTheoryData> CreateJWEWithAesGcmTheoryData
}
},
new CreateTokenTheoryData
{
First = true,
TestId = "AesGcm192Encryption",
TokenDescriptor = new SecurityTokenDescriptor
{
SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials,
EncryptingCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_AesGcm192,
Subject = new ClaimsIdentity(Default.PayloadClaims),
TokenType = "TokenType"
},
JsonWebTokenHandler = new JsonWebTokenHandler(),
JwtSecurityTokenHandler = tokenHandler,
ValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key,
TokenDecryptionKey = KeyingMaterial.DefaultSymmetricSecurityKey_192,
ValidAudience = Default.Audience,
ValidIssuer = Default.Issuer
}
},
new CreateTokenTheoryData
{
TestId = "AesGcm256Encryption",
TokenDescriptor = new SecurityTokenDescriptor
Expand All @@ -474,15 +493,81 @@ public static TheoryData<CreateTokenTheoryData> CreateJWEWithAesGcmTheoryData
},
JsonWebTokenHandler = new JsonWebTokenHandler(),
JwtSecurityTokenHandler = tokenHandler,
ExpectedException = ExpectedException.SecurityTokenEncryptionFailedException("IDX10616:", typeof(NotSupportedException))
ValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key,
TokenDecryptionKey = KeyingMaterial.DefaultSymmetricSecurityKey_256,
ValidAudience = Default.Audience,
ValidIssuer = Default.Issuer
}
},
new CreateTokenTheoryData
{
TestId = "AesGcm128Encryption_Aes128KW",
TokenDescriptor = new SecurityTokenDescriptor
{
SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials,
EncryptingCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_AesGcm128_Aes128KW,
Subject = new ClaimsIdentity(Default.PayloadClaims),
TokenType = "TokenType"
},
JsonWebTokenHandler = new JsonWebTokenHandler(),
JwtSecurityTokenHandler = tokenHandler,
ValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key,
TokenDecryptionKey = KeyingMaterial.DefaultSymmetricSecurityKey_128,
ValidAudience = Default.Audience,
ValidIssuer = Default.Issuer
}
},
new CreateTokenTheoryData
{
TestId = "AesGcm192Encryption_Aes192KW",
TokenDescriptor = new SecurityTokenDescriptor
{
SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials,
EncryptingCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_AesGcm192_Aes192KW,
Subject = new ClaimsIdentity(Default.PayloadClaims),
TokenType = "TokenType"
},
JsonWebTokenHandler = new JsonWebTokenHandler(),
JwtSecurityTokenHandler = tokenHandler,
ValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key,
TokenDecryptionKey = KeyingMaterial.DefaultSymmetricSecurityKey_192,
ValidAudience = Default.Audience,
ValidIssuer = Default.Issuer
}
},
new CreateTokenTheoryData
{
TestId = "AesGcm256Encryption_Aes256KW",
TokenDescriptor = new SecurityTokenDescriptor
{
SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials,
EncryptingCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_AesGcm256_Aes256KW,
Subject = new ClaimsIdentity(Default.PayloadClaims),
TokenType = "TokenType"
},
JsonWebTokenHandler = new JsonWebTokenHandler(),
JwtSecurityTokenHandler = tokenHandler,
ValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key,
TokenDecryptionKey = KeyingMaterial.DefaultSymmetricSecurityKey_256,
ValidAudience = Default.Audience,
ValidIssuer = Default.Issuer
}
},
new CreateTokenTheoryData
{
TestId = "AesGcm_InvalidDecryptionKeySize",
TokenDescriptor = new SecurityTokenDescriptor
{
SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials,
EncryptingCredentials = encryptionCredentials,
EncryptingCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_AesGcm128,
Subject = new ClaimsIdentity(Default.PayloadClaims),
TokenType = "TokenType"
},
Expand Down
8 changes: 8 additions & 0 deletions test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,11 +226,19 @@ public static RsaSecurityKey RsaSecurityKey2
public static byte[] DefaultSymmetricKeyBytes_128 = Convert.FromBase64String(DefaultSymmetricKeyEncoded_128);
public static SymmetricSecurityKey DefaultSymmetricSecurityKey_128 = new SymmetricSecurityKey(DefaultSymmetricKeyBytes_128) { KeyId = "DefaultSymmetricSecurityKey_128" };
public static EncryptingCredentials DefaultSymmetricEncryptingCreds_AesGcm128 = new EncryptingCredentials(DefaultSymmetricSecurityKey_128, "dir", SecurityAlgorithms.Aes128Gcm);
public static EncryptingCredentials DefaultSymmetricEncryptingCreds_AesGcm128_Aes128KW = new EncryptingCredentials(DefaultSymmetricSecurityKey_128, SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128Gcm);

public static string DefaultSymmetricKeyEncoded_192 = "06P7WdwAEybptADtJis9n0oWnG5imp8G";
public static byte[] DefaultSymmetricKeyBytes_192 = Convert.FromBase64String(DefaultSymmetricKeyEncoded_192);
public static SymmetricSecurityKey DefaultSymmetricSecurityKey_192 = new SymmetricSecurityKey(DefaultSymmetricKeyBytes_192) { KeyId = "DefaultSymmetricSecurityKey_192" };
public static EncryptingCredentials DefaultSymmetricEncryptingCreds_AesGcm192 = new EncryptingCredentials(DefaultSymmetricSecurityKey_192, "dir", SecurityAlgorithms.Aes192Gcm);
public static EncryptingCredentials DefaultSymmetricEncryptingCreds_AesGcm192_Aes192KW = new EncryptingCredentials(DefaultSymmetricSecurityKey_192, SecurityAlgorithms.Aes192KW, SecurityAlgorithms.Aes192Gcm);

public static string DefaultSymmetricKeyEncoded_256 = "Vbxq2mlbGJw8XH+ZoYBnUHmHga8/o/IduvU/Tht70iE=";
public static byte[] DefaultSymmetricKeyBytes_256 = Convert.FromBase64String(DefaultSymmetricKeyEncoded_256);
public static SymmetricSecurityKey DefaultSymmetricSecurityKey_256 = new SymmetricSecurityKey(DefaultSymmetricKeyBytes_256) { KeyId = "DefaultSymmetricSecurityKey_256" };
public static EncryptingCredentials DefaultSymmetricEncryptingCreds_AesGcm256 = new EncryptingCredentials(DefaultSymmetricSecurityKey_256, "dir", SecurityAlgorithms.Aes256Gcm);
public static EncryptingCredentials DefaultSymmetricEncryptingCreds_AesGcm256_Aes256KW = new EncryptingCredentials(DefaultSymmetricSecurityKey_256, SecurityAlgorithms.Aes256KW, SecurityAlgorithms.Aes256Gcm);
public static SigningCredentials DefaultSymmetricSigningCreds_256_Sha2 = new SigningCredentials(DefaultSymmetricSecurityKey_256, SecurityAlgorithms.HmacSha256Signature, SecurityAlgorithms.Sha256);
public static EncryptingCredentials DefaultSymmetricEncryptingCreds_Aes128_Sha2 = new EncryptingCredentials(DefaultSymmetricSecurityKey_256, "dir", SecurityAlgorithms.Aes128CbcHmacSha256);
public static SymmetricSecurityKey DefaultSymmetricSecurityKey_256_NoKeyId = new SymmetricSecurityKey(DefaultSymmetricKeyBytes_256);
Expand Down