Skip to content

Commit

Permalink
Add X509EncryptedCredentials class
Browse files Browse the repository at this point in the history
If only a certificate is provided, the key wrap algorithm and data
encryption algorithm will be set by default to RsaOaepKeyWrap and
A128CBC-HS256, respectively.

* Add new ctor to EncryptingCredentials to allow users to pass
only a 'shared' symmetric key which will be used to encrypt data, but
it will not be serialized to a SAML token.

* Add internal const strings DefaultAsymmetricKeyWrapAlgorithm and
DefaultSymmetricEncryptionAlgorithm to indicate default algorithms
used for key wrap and data encryption

* Add protected ctor to EncryptingCredentials to check if a certificate
passed to X509EncryptedCredentials is null. Provides cleaner stack
trace in case of an exception caused by a null cert.

* Refactor EncryptingCredentials. Move null/empty checks to setters
and provide clearer comments

* Add tests for X509EncryiptingCredentials and EncryptingCredentials
classes

Resolves: #995
See also: #734
  • Loading branch information
GeoK committed Aug 21, 2018
1 parent 236bf92 commit e9fe530
Show file tree
Hide file tree
Showing 7 changed files with 449 additions and 23 deletions.
81 changes: 58 additions & 23 deletions src/Microsoft.IdentityModel.Tokens/EncryptingCredentials.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,67 +25,102 @@
//
//------------------------------------------------------------------------------

using System;
using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.Logging;

namespace Microsoft.IdentityModel.Tokens
{
/// <summary>
/// A wrapper class for properties that are used for token encryption.
/// A class for properties that are used for token encryption.
/// </summary>
public class EncryptingCredentials
{
private string _alg;
private string _enc;
private SecurityKey _key;

/// <summary>
/// Initializes a new instance of the <see cref="EncryptingCredentials"/> class.
/// </summary>
/// <param name="key"><see cref="SecurityKey"/></param>
/// <param name="alg">The key encryption algorithm to apply.</param>
/// <param name="enc">The encryption algorithm to apply.</param>
public EncryptingCredentials(SecurityKey key, string alg, string enc)
/// <param name="certificate"><see cref="X509Certificate2"/>.</param>
/// <param name="alg">A key wrap algorithm to use when encrypting a session key.</param>
/// <param name="enc">Data encryption algorithm to apply.</param>
/// <exception cref="ArgumentNullException">if 'certificate' is null.</exception>
/// <exception cref="ArgumentNullException">if 'alg' is null or empty.</exception>
/// <exception cref="ArgumentNullException">if 'enc' is null or empty.</exception>
protected EncryptingCredentials(X509Certificate2 certificate, string alg, string enc)
{
if (key == null)
throw LogHelper.LogArgumentNullException(nameof(key));

if (string.IsNullOrWhiteSpace(alg))
throw LogHelper.LogArgumentNullException(nameof(alg));

if (string.IsNullOrWhiteSpace(enc))
throw LogHelper.LogArgumentNullException(nameof(enc));
if (certificate == null)
throw LogHelper.LogArgumentNullException(nameof(certificate));

Key = new X509SecurityKey(certificate);
Alg = alg;
Enc = enc;
}

/// <summary>
/// Initializes a new instance of the <see cref="EncryptingCredentials"/> class.
/// </summary>
/// <param name="key"><see cref="SecurityKey"/> to use when encrypting a session key.</param>
/// <param name="alg">A key wrap algorithm to use when encrypting a session key.</param>
/// <param name="enc">Data encryption algorithm to apply.</param>
/// <exception cref="ArgumentNullException">if 'key' is null.</exception>
/// <exception cref="ArgumentNullException">if 'alg' is null or empty.</exception>
/// <exception cref="ArgumentNullException">if 'enc' is null or empty.</exception>
public EncryptingCredentials(SecurityKey key, string alg, string enc)
{
Key = key;
Alg = alg;
Enc = enc;
}

/// <summary>
/// Initializes a new instance of the <see cref="EncryptingCredentials"/> class.
/// </summary>
/// <remarks> 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.
/// </remarks>
/// <param name="key"><see cref="SymmetricSecurityKey"/> to apply.</param>
/// <param name="enc">Data encryption algorithm to apply.</param>
/// <exception cref="ArgumentException">If the <see cref="SecurityKey"/> is not a <see cref="SymmetricSecurityKey"/>.</exception>
/// <exception cref="ArgumentNullException">if 'enc' is null or empty.</exception>
public EncryptingCredentials(SymmetricSecurityKey key, string enc)
: this(key, SecurityAlgorithms.None, enc)
{
}

/// <summary>
/// Gets the algorithm which used for token encryption.
/// Gets the key wrap algorithm used for session key encryption.
/// </summary>
public string Alg
{
get;
private set;
get => _alg;
private set => _alg = string.IsNullOrEmpty(value) ? throw LogHelper.LogArgumentNullException("alg") : value;
}

/// <summary>
/// Gets the algorithm which used for token encryption.
/// Gets the data encryption algorithm.
/// </summary>
public string Enc
{
get;
private set;
get => _enc;
private set => _enc = string.IsNullOrEmpty(value) ? throw LogHelper.LogArgumentNullException("enc") : value;
}

/// <summary>
/// Users can override the default <see cref="CryptoProviderFactory"/> with this property. This factory will be used for creating encryition providers.
/// Users can override the default <see cref="CryptoProviderFactory"/> with this property. This factory will be used for creating encryption providers.
/// </summary>
public CryptoProviderFactory CryptoProviderFactory { get; set; }

/// <summary>
/// Gets the <see cref="SecurityKey"/> which used for signature valdiation.
/// Gets the <see cref="SecurityKey"/> used for encryption.
/// </summary>
public SecurityKey Key
{
get;
private set;
get => _key;
private set => _key = value ?? throw LogHelper.LogArgumentNullException("key");
}
}
}
3 changes: 3 additions & 0 deletions src/Microsoft.IdentityModel.Tokens/SecurityAlgorithms.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 DefaultSymmetricEncryptionAlgorithm = Aes128CbcHmacSha256;

#pragma warning restore 1591
}
}
76 changes: 76 additions & 0 deletions src/Microsoft.IdentityModel.Tokens/X509EncryptingCredentials.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//------------------------------------------------------------------------------
//
// 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
{
/// <summary>
/// An <see cref="X509EncryptingCredentials"/> designed to construct <see cref="EncryptingCredentials"/> based on a x509 certificate.
/// </summary>
public class X509EncryptingCredentials : EncryptingCredentials
{
/// <summary>
/// Designed to construct <see cref="EncryptingCredentials"/> based on a x509 certificate.
/// </summary>
/// <param name="certificate">A <see cref="X509Certificate2"/></param>
/// <remarks>
/// <see cref="SecurityAlgorithms.DefaultAsymmetricKeyWrapAlgorithm"/> will be used as the key wrap algorithm
/// <see cref="SecurityAlgorithms.DefaultSymmetricEncryptionAlgorithm"/> will be used as the data encryption algorithm
/// </remarks>
/// <exception cref="ArgumentNullException">if 'certificate' is null.</exception>
public X509EncryptingCredentials(X509Certificate2 certificate)
: this(certificate, SecurityAlgorithms.DefaultAsymmetricKeyWrapAlgorithm, SecurityAlgorithms.DefaultSymmetricEncryptionAlgorithm)
{
}

/// <summary>
/// Designed to construct <see cref="EncryptingCredentials"/> based on the x509 certificate, a key wrap algorithm, and data encryption algorithm.
/// </summary>
/// <param name="certificate">A <see cref="X509Certificate2"/></param>
/// <param name="keyWrapAlgorithm">A key wrap algorithm</param>
/// <param name="dataEncryptionAlgorithm">Data encryption algorithm</param>
/// <exception cref="ArgumentNullException">if 'certificate' is null.</exception>
/// <exception cref="ArgumentNullException">if 'keyWrapAlgorithm' is null or empty.</exception>
/// <exception cref="ArgumentNullException">if 'dataEncryptionAlgorithm' is null or empty.</exception>
public X509EncryptingCredentials(X509Certificate2 certificate, string keyWrapAlgorithm, string dataEncryptionAlgorithm)
: base(certificate, keyWrapAlgorithm, dataEncryptionAlgorithm)
{
Certificate = certificate;
}

/// <summary>
/// Gets the <see cref="X509Certificate2"/> used by this instance.
/// </summary>
public X509Certificate2 Certificate
{
get;
private set;
}
}
}
5 changes: 5 additions & 0 deletions test/Microsoft.IdentityModel.Tests/Default.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
8 changes: 8 additions & 0 deletions test/Microsoft.IdentityModel.Tests/TestUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
Loading

0 comments on commit e9fe530

Please sign in to comment.