diff --git a/src/Microsoft.IdentityModel.Tokens/EncryptingCredentials.cs b/src/Microsoft.IdentityModel.Tokens/EncryptingCredentials.cs
index 00cbbed15d..213aebb951 100644
--- a/src/Microsoft.IdentityModel.Tokens/EncryptingCredentials.cs
+++ b/src/Microsoft.IdentityModel.Tokens/EncryptingCredentials.cs
@@ -25,67 +25,109 @@
//
//------------------------------------------------------------------------------
+using System;
+using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.Logging;
namespace Microsoft.IdentityModel.Tokens
{
///
- /// A wrapper class for properties that are used for token encryption.
+ /// A class for properties that are used for token encryption.
///
public class EncryptingCredentials
{
+ private string _alg;
+ private string _enc;
+ private SecurityKey _key;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// .
+ /// A key wrap encryption algorithm to apply when encrypting a session key.
+ /// Data encryption algorithm to apply when encrypting plaintext.
+ /// if 'certificate' is null.
+ /// if 'alg' is null or empty.
+ /// if 'enc' is null or empty.
+ protected EncryptingCredentials(X509Certificate2 certificate, string alg, string enc)
+ {
+ if (certificate == null)
+ throw LogHelper.LogArgumentNullException(nameof(certificate));
+
+ Key = new X509SecurityKey(certificate);
+ Alg = alg;
+ Enc = enc;
+ }
+
///
/// Initializes a new instance of the class.
///
///
- /// The key encryption algorithm to apply.
- /// The encryption algorithm to apply.
+ /// A key wrap encryption algorithm to apply when encrypting a session key.
+ /// Data encryption algorithm to apply when encrypting plaintext.
+ /// if 'key' is null.
+ /// if 'alg' is null or empty.
+ /// if 'enc' is null or empty.
public EncryptingCredentials(SecurityKey key, string alg, string enc)
{
- if (key == null)
- throw LogHelper.LogArgumentNullException(nameof(key));
+ Key = key;
+ Alg = alg;
+ Enc = enc;
+ }
- if (string.IsNullOrWhiteSpace(alg))
- throw LogHelper.LogArgumentNullException(nameof(alg));
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Used in scenarios when a key represents a 'shared' symmetric key.
+ /// For example, SAML 2.0 Assertion will be encrypted using a provided symmetric key
+ /// which won't be serialized to a SAML token.
+ ///
+ ///
+ /// Data encryption algorithm to apply when encrypting plaintext.
+ /// If the is not .
+ /// if 'enc' is null or empty.
+ public EncryptingCredentials(SecurityKey key, string enc)
+ {
+ Key = key;
- if (string.IsNullOrWhiteSpace(enc))
- throw LogHelper.LogArgumentNullException(nameof(enc));
+ if (key.GetType() != typeof(SymmetricSecurityKey))
+ throw LogHelper.LogArgumentException("key", LogMessages.IDX10704);
- Alg = alg;
+ //explicitly setting Alg to None
+ Alg = SecurityAlgorithms.None;
Enc = enc;
- Key = key;
}
///
- /// Gets the algorithm which used for token encryption.
+ /// Gets the key wrap encryption algorithm used for a session key encryption.
///
public string Alg
{
- get;
- private set;
+ get => _alg;
+ private set => _alg = string.IsNullOrEmpty(value) ? throw LogHelper.LogArgumentNullException("alg") : value;
}
///
- /// Gets the algorithm which used for token encryption.
+ /// Gets the data encryption algorithm used for plaintext encryption.
///
public string Enc
{
- get;
- private set;
+ get => _enc;
+ private set => _enc = string.IsNullOrEmpty(value) ? throw LogHelper.LogArgumentNullException("enc") : value;
}
///
- /// Users can override the default with this property. This factory will be used for creating encryition providers.
+ /// Users can override the default with this property. This factory will be used for creating encryption providers.
///
public CryptoProviderFactory CryptoProviderFactory { get; set; }
///
- /// Gets the which used for signature valdiation.
+ /// Gets the which used for signature validation.
///
public SecurityKey Key
{
- get;
- private set;
+ get => _key;
+ private set => _key = value ?? throw LogHelper.LogArgumentNullException("key");
}
}
}
diff --git a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs
index a843eebbe2..7beb5108d2 100644
--- a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs
+++ b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs
@@ -178,6 +178,7 @@ internal static class LogMessages
public const string IDX10701 = "IDX10701: Invalid JsonWebKey rsa keying material: '{0}'. Both modulus and exponent should be present";
public const string IDX10702 = "IDX10702: One or more private RSA key parts are null in the JsonWebKey: '{0}'";
public const string IDX10703 = "IDX10703: Cannot create symmetric security key. Key length is zero.";
+ public const string IDX10704 = "IDX10704: The SecurityKey provided is not a SymmetricSecurityKey.";
// Json specific errors
public const string IDX10801 = "IDX10801: Unable to create an RSA public key from the Exponent and Modulus found in the JsonWebKey: E: '{0}', N: '{1}'. See inner exception for additional details.";
diff --git a/src/Microsoft.IdentityModel.Tokens/SecurityAlgorithms.cs b/src/Microsoft.IdentityModel.Tokens/SecurityAlgorithms.cs
index a9f50d116c..76eaa8fe84 100644
--- a/src/Microsoft.IdentityModel.Tokens/SecurityAlgorithms.cs
+++ b/src/Microsoft.IdentityModel.Tokens/SecurityAlgorithms.cs
@@ -113,6 +113,9 @@ public static class SecurityAlgorithms
public const string Aes192CbcHmacSha384 = "A192CBC-HS384";
public const string Aes256CbcHmacSha512 = "A256CBC-HS512";
+ internal const string DefaultAsymmetricKeyWrapAlgorithm = RsaOaepKeyWrap;
+ internal const string DefaultSymmetricAlgorithm = Aes128CbcHmacSha256;
+
#pragma warning restore 1591
}
}
diff --git a/src/Microsoft.IdentityModel.Tokens/X509EncryptingCredentials.cs b/src/Microsoft.IdentityModel.Tokens/X509EncryptingCredentials.cs
new file mode 100644
index 0000000000..b62f126e8e
--- /dev/null
+++ b/src/Microsoft.IdentityModel.Tokens/X509EncryptingCredentials.cs
@@ -0,0 +1,77 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//------------------------------------------------------------------------------
+
+using System;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Microsoft.IdentityModel.Tokens
+{
+ ///
+ /// An designed for to construct an based on a x509 certificate.
+ ///
+ public class X509EncryptingCredentials : EncryptingCredentials
+ {
+ ///
+ /// Constructs an based on a x509 certificate.
+ ///
+ /// A
+ ///
+ /// algorithm will be used by default as a key wrap encryption algorithm
+ /// algorithm will be used by default as a data encryption algorithm
+ ///
+ /// if 'certificate' is null.
+ public X509EncryptingCredentials(X509Certificate2 certificate)
+ : this(certificate, SecurityAlgorithms.DefaultAsymmetricKeyWrapAlgorithm, SecurityAlgorithms.DefaultSymmetricAlgorithm)
+ {
+
+ }
+
+ ///
+ /// Constructs an based on the x509 certificate, key wrapping algorithm, and data encryption algorithm.
+ ///
+ /// A
+ /// A key wrapping algorithm
+ /// A data encryption algorithm
+ /// if 'certificate' is null.
+ /// if 'keyWrapEncryptionAlgorithm' is null or empty.
+ /// if 'dataEncryptionAlgorithm' is null or empty.
+ public X509EncryptingCredentials(X509Certificate2 certificate, string keyWrapEncryptionAlgorithm, string dataEncryptionAlgorithm)
+ : base(new X509SecurityKey(certificate), keyWrapEncryptionAlgorithm, dataEncryptionAlgorithm)
+ {
+ Certificate = certificate;
+ }
+
+ ///
+ /// Gets a used by this instance.
+ ///
+ public X509Certificate2 Certificate
+ {
+ get;
+ private set;
+ }
+ }
+}
diff --git a/test/Microsoft.IdentityModel.Tests/Default.cs b/test/Microsoft.IdentityModel.Tests/Default.cs
index fd817b58b4..61e2994ab1 100644
--- a/test/Microsoft.IdentityModel.Tests/Default.cs
+++ b/test/Microsoft.IdentityModel.Tests/Default.cs
@@ -117,6 +117,11 @@ public static SecurityKey AsymmetricSigningKeyPublic
get => new X509SecurityKey(KeyingMaterial.DefaultCert_2048_Public);
}
+ public static SecurityKey AsymmetricEncryptionKeyPublic
+ {
+ get => new X509SecurityKey(KeyingMaterial.DefaultCert_2048_Public);
+ }
+
#if !CrossVersionTokenValidation
public static TokenValidationParameters AsymmetricEncryptSignTokenValidationParameters
{
diff --git a/test/Microsoft.IdentityModel.Tests/TestUtilities.cs b/test/Microsoft.IdentityModel.Tests/TestUtilities.cs
index fe3469699b..dd340edc52 100644
--- a/test/Microsoft.IdentityModel.Tests/TestUtilities.cs
+++ b/test/Microsoft.IdentityModel.Tests/TestUtilities.cs
@@ -417,6 +417,14 @@ public static void CheckForArgumentNull(CompareContext context, string name, Exc
context.Diffs.Add($"!(ex is ArgumentNullException) || !ex.Message.Contains({name})");
}
+ public static void CheckForArgumentException(CompareContext context, string name, Exception ex)
+ {
+ if (ex == null)
+ context.Diffs.Add($"expecting ArgumentException for parameter {name}. Exception is null.");
+ else if (!(ex is ArgumentException) || !ex.Message.Contains(name))
+ context.Diffs.Add($"!(ex is ArgumentException) || !ex.Message.Contains({name})");
+ }
+
public static byte[] HexToByteArray(string hexString)
{
byte[] bytes = new byte[hexString.Length / 2];
diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/EncryptingCredentialsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/EncryptingCredentialsTests.cs
new file mode 100644
index 0000000000..b9bee3efc4
--- /dev/null
+++ b/test/Microsoft.IdentityModel.Tokens.Tests/EncryptingCredentialsTests.cs
@@ -0,0 +1,146 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//------------------------------------------------------------------------------
+
+using System;
+using Microsoft.IdentityModel.Tests;
+using Xunit;
+
+namespace Microsoft.IdentityModel.Tokens.Tests
+{
+ public class EncryptingCredentialsTests
+ {
+ [Fact]
+ public void Constructors()
+ {
+ var context = TestUtilities.WriteHeader($"{this}", "Constructors", true);
+
+ // public EncryptingCredentials(SecurityKey key, string alg, string enc)
+ try
+ {
+ new EncryptingCredentials(null, null, null);
+ TestUtilities.CheckForArgumentNull(context, "key", null);
+ }
+ catch (Exception ex)
+ {
+ TestUtilities.CheckForArgumentNull(context, "key", ex);
+ }
+
+ try
+ {
+ new EncryptingCredentials(Default.SymmetricEncryptionKey128, null, SecurityAlgorithms.Aes128CbcHmacSha256);
+ TestUtilities.CheckForArgumentNull(context, "alg", null);
+ }
+ catch (Exception ex)
+ {
+ TestUtilities.CheckForArgumentNull(context, "alg", ex);
+ }
+
+ try
+ {
+ new EncryptingCredentials(Default.AsymmetricEncryptionKeyPublic, SecurityAlgorithms.RsaOaepKeyWrap, null);
+ TestUtilities.CheckForArgumentNull(context, "enc", null);
+ }
+ catch (Exception ex)
+ {
+ TestUtilities.CheckForArgumentNull(context, "enc", ex);
+ }
+
+ try
+ {
+ new EncryptingCredentials(Default.AsymmetricEncryptionKeyPublic, string.Empty, string.Empty);
+ TestUtilities.CheckForArgumentNull(context, "alg", null);
+ }
+ catch (Exception ex)
+ {
+ TestUtilities.CheckForArgumentNull(context, "alg", ex);
+ }
+
+ var key = Default.AsymmetricEncryptionKeyPublic;
+ var encryptingCredentials = new EncryptingCredentials(key, SecurityAlgorithms.RsaOaepKeyWrap, SecurityAlgorithms.Aes192CbcHmacSha384);
+ if (!ReferenceEquals(encryptingCredentials.Key, key))
+ context.Diffs.Add("!object.ReferenceEquals(encryptingCredentials.Key, key)");
+
+ if (!SecurityAlgorithms.RsaOaepKeyWrap.Equals(encryptingCredentials.Alg))
+ context.Diffs.Add("!SecurityAlgorithms.RsaOaepKeyWrap.Equals(encryptingCredentials.Alg)");
+
+ if (!SecurityAlgorithms.Aes192CbcHmacSha384.Equals(encryptingCredentials.Enc))
+ context.Diffs.Add("!SecurityAlgorithms.Aes192CbcHmacSha384.Equals(encryptingCredentials.Enc)");
+
+ // public EncryptingCredentials(SecurityKey key, string enc)
+ try
+ {
+ new EncryptingCredentials(null, null);
+ TestUtilities.CheckForArgumentNull(context, "key", null);
+ }
+ catch (Exception ex)
+ {
+ TestUtilities.CheckForArgumentNull(context, "key", ex);
+ }
+
+ try
+ {
+ new EncryptingCredentials(Default.AsymmetricEncryptionKeyPublic, SecurityAlgorithms.Aes128CbcHmacSha256);
+ TestUtilities.CheckForArgumentException(context, "key", null);
+ }
+ catch (Exception ex)
+ {
+ TestUtilities.CheckForArgumentException(context, "key", ex);
+ }
+
+ try
+ {
+ new EncryptingCredentials(Default.SymmetricEncryptionKey128, null);
+ TestUtilities.CheckForArgumentNull(context, "enc", null);
+ }
+ catch (Exception ex)
+ {
+ TestUtilities.CheckForArgumentNull(context, "enc", ex);
+ }
+
+ try
+ {
+ new EncryptingCredentials(Default.SymmetricEncryptionKey128, String.Empty);
+ TestUtilities.CheckForArgumentNull(context, "enc", null);
+ }
+ catch (Exception ex)
+ {
+ TestUtilities.CheckForArgumentNull(context, "enc", ex);
+ }
+
+ key = Default.SymmetricEncryptionKey128;
+ encryptingCredentials = new EncryptingCredentials(key, SecurityAlgorithms.Aes128CbcHmacSha256);
+ if (!ReferenceEquals(encryptingCredentials.Key, key))
+ context.Diffs.Add("!ReferenceEquals(encryptingCredentials.Key, key)");
+
+ if (!SecurityAlgorithms.Aes128CbcHmacSha256.Equals(encryptingCredentials.Enc))
+ context.Diffs.Add("!SecurityAlgorithms.Aes128CbcHmacSha256.Equals(encryptingCredentials.Enc)");
+
+ //check context for errors
+ TestUtilities.AssertFailIfErrors(context);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/X509EncryptingCredentialsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/X509EncryptingCredentialsTests.cs
new file mode 100644
index 0000000000..1ee5953d02
--- /dev/null
+++ b/test/Microsoft.IdentityModel.Tokens.Tests/X509EncryptingCredentialsTests.cs
@@ -0,0 +1,119 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation.
+// All rights reserved.
+//
+// This code is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//------------------------------------------------------------------------------
+
+using System;
+using Microsoft.IdentityModel.Tests;
+using Xunit;
+
+namespace Microsoft.IdentityModel.Tokens.Tests
+{
+ public class X509EncryptingCredentialsTests
+ {
+ [Fact]
+ public void Constructors()
+ {
+ var context = TestUtilities.WriteHeader($"{this}", "Constructors", true);
+
+ // public X509EncryptingCredentials(Certificate2 certificate)
+ try
+ {
+ new X509EncryptingCredentials(null);
+ TestUtilities.CheckForArgumentNull(context, "certificate", null);
+ }
+ catch (Exception ex)
+ {
+ TestUtilities.CheckForArgumentNull(context, "certificate", ex);
+ }
+
+ var cert = Default.Certificate;
+ var encryptingCredentials = new X509EncryptingCredentials(cert);
+
+ if (!ReferenceEquals(encryptingCredentials.Certificate, cert))
+ context.Diffs.Add("!ReferenceEquals(encryptingCredentials.Certificate, cert)");
+
+ if (!SecurityAlgorithms.RsaOaepKeyWrap.Equals(encryptingCredentials.Alg))
+ context.Diffs.Add("!SecurityAlgorithms.RsaOaepKeyWrap.Equals(encryptingCredentials.Alg)");
+
+ if (!SecurityAlgorithms.Aes128CbcHmacSha256.Equals(encryptingCredentials.Enc))
+ context.Diffs.Add("!SecurityAlgorithms.Aes128CbcHmacSha256.Equals(encryptingCredentials.Enc)");
+
+ // public X509EncryptingCredentials(Certificate2 certificate, string alg, string enc)
+ try
+ {
+ new X509EncryptingCredentials(null, null, null);
+ TestUtilities.CheckForArgumentNull(context, "certificate", null);
+ }
+ catch (Exception ex)
+ {
+ TestUtilities.CheckForArgumentNull(context, "certificate", ex);
+ }
+
+ try
+ {
+ new X509EncryptingCredentials(Default.Certificate, null, SecurityAlgorithms.Aes128CbcHmacSha256);
+ TestUtilities.CheckForArgumentNull(context, "alg", null);
+ }
+ catch (Exception ex)
+ {
+ TestUtilities.CheckForArgumentNull(context, "alg", ex);
+ }
+
+ try
+ {
+ new X509EncryptingCredentials(Default.Certificate, SecurityAlgorithms.RsaOaepKeyWrap, null);
+ TestUtilities.CheckForArgumentNull(context, "enc", null);
+ }
+ catch (Exception ex)
+ {
+ TestUtilities.CheckForArgumentNull(context, "enc", ex);
+ }
+
+ try
+ {
+ new X509EncryptingCredentials(Default.Certificate, string.Empty, string.Empty);
+ TestUtilities.CheckForArgumentNull(context, "alg", null);
+ }
+ catch (Exception ex)
+ {
+ TestUtilities.CheckForArgumentNull(context, "alg", ex);
+ }
+
+ encryptingCredentials = new X509EncryptingCredentials(cert, SecurityAlgorithms.RsaOaepKeyWrap, SecurityAlgorithms.Aes192CbcHmacSha384);
+ if (!ReferenceEquals(encryptingCredentials.Certificate, cert))
+ context.Diffs.Add("!ReferenceEquals(encryptingCredentials.Certificate, cert)");
+
+ if (!SecurityAlgorithms.RsaOaepKeyWrap.Equals(encryptingCredentials.Alg))
+ context.Diffs.Add("!SecurityAlgorithms.RsaOaepKeyWrap.Equals(encryptingCredentials.Alg)");
+
+ if (!SecurityAlgorithms.Aes192CbcHmacSha384.Equals(encryptingCredentials.Enc))
+ context.Diffs.Add("!SecurityAlgorithms.Aes192CbcHmacSha384.Equals(encryptingCredentials.Enc)");
+
+ //check context for errors
+ TestUtilities.AssertFailIfErrors(context);
+ }
+ }
+}
\ No newline at end of file