diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs index 7b24360058..e615334fe9 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs @@ -300,6 +300,27 @@ public static byte[] GenerateKeyBytes(int sizeInBits) return key; } + /// + /// Generates key bytes. + /// + 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, @@ -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)))); + 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); diff --git a/src/Microsoft.IdentityModel.Tokens/Encryption/AuthenticatedEncryptionProvider.cs b/src/Microsoft.IdentityModel.Tokens/Encryption/AuthenticatedEncryptionProvider.cs index bf137b3491..a75e6d71bc 100644 --- a/src/Microsoft.IdentityModel.Tokens/Encryption/AuthenticatedEncryptionProvider.cs +++ b/src/Microsoft.IdentityModel.Tokens/Encryption/AuthenticatedEncryptionProvider.cs @@ -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() diff --git a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs index d01e092787..74fb6219a5 100644 --- a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs @@ -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."; @@ -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}'."; diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs index 1357f6cff2..7e790960b5 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs @@ -437,18 +437,16 @@ public static TheoryData CreateJWEWithAesGcmTheoryData }; tokenHandler.InboundClaimTypeMap.Clear(); - var encryptionCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_AesGcm128; - encryptionCredentials.CryptoProviderFactory = new CryptoProviderFactoryMock(); return new TheoryData { 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" }, @@ -463,6 +461,27 @@ public static TheoryData 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 @@ -474,7 +493,73 @@ public static TheoryData 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 { @@ -482,7 +567,7 @@ public static TheoryData CreateJWEWithAesGcmTheoryData TokenDescriptor = new SecurityTokenDescriptor { SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, - EncryptingCredentials = encryptionCredentials, + EncryptingCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_AesGcm128, Subject = new ClaimsIdentity(Default.PayloadClaims), TokenType = "TokenType" }, diff --git a/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs b/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs index ce44b35a13..3ae3bd211a 100644 --- a/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs +++ b/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs @@ -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);