Browse Source

Remove AddDevelopmentEncryptionCertificate()/AddEphemeralEncryptionKey() from OpenIddictValidationBuilder

pull/898/head
Kévin Chalet 6 years ago
parent
commit
8d4c0dec68
  1. 175
      src/OpenIddict.Validation/OpenIddictValidationBuilder.cs

175
src/OpenIddict.Validation/OpenIddictValidationBuilder.cs

@ -9,8 +9,6 @@ using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using JetBrains.Annotations;
@ -189,179 +187,6 @@ namespace Microsoft.Extensions.DependencyInjection
key.CryptoProviderFactory.IsSupportedAlgorithm(algorithm, key);
}
/// <summary>
/// Registers (and generates if necessary) a user-specific development
/// certificate used to decrypt the tokens issued by OpenIddict.
/// </summary>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
public OpenIddictValidationBuilder AddDevelopmentEncryptionCertificate()
=> AddDevelopmentEncryptionCertificate(new X500DistinguishedName("CN=OpenIddict Validation Encryption Certificate"));
/// <summary>
/// Registers (and generates if necessary) a user-specific development
/// certificate used to decrypt the tokens issued by OpenIddict.
/// </summary>
/// <param name="subject">The subject name associated with the certificate.</param>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
public OpenIddictValidationBuilder AddDevelopmentEncryptionCertificate([NotNull] X500DistinguishedName subject)
{
if (subject == null)
{
throw new ArgumentNullException(nameof(subject));
}
using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
// Try to retrieve the development certificate from the specified store.
// If a certificate was found but is not yet or no longer valid, remove it
// from the store before creating and persisting a new encryption certificate.
var certificate = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, subject.Name, validOnly: false)
.OfType<X509Certificate2>()
.SingleOrDefault();
if (certificate != null && (certificate.NotBefore > DateTime.Now || certificate.NotAfter < DateTime.Now))
{
store.Remove(certificate);
certificate = null;
}
#if SUPPORTS_CERTIFICATE_GENERATION
// If no appropriate certificate can be found, generate and persist a new certificate in the specified store.
if (certificate == null)
{
using var algorithm = RSA.Create(keySizeInBits: 2048);
var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true));
certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(2));
// Note: setting the friendly name is not supported on Unix machines (including Linux and macOS).
// To ensure an exception is not thrown by the property setter, an OS runtime check is used here.
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
certificate.FriendlyName = "OpenIddict Validation Development Encryption Certificate";
}
// Note: CertificateRequest.CreateSelfSigned() doesn't mark the key set associated with the certificate
// as "persisted", which eventually prevents X509Store.Add() from correctly storing the private key.
// To work around this issue, the certificate payload is manually exported and imported back
// into a new X509Certificate2 instance specifying the X509KeyStorageFlags.PersistKeySet flag.
var data = certificate.Export(X509ContentType.Pfx, string.Empty);
try
{
var flags = X509KeyStorageFlags.PersistKeySet;
// Note: macOS requires marking the certificate private key as exportable.
// If this flag is not set, a CryptographicException is thrown at runtime.
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
flags |= X509KeyStorageFlags.Exportable;
}
certificate = new X509Certificate2(data, string.Empty, flags);
}
finally
{
Array.Clear(data, 0, data.Length);
}
store.Add(certificate);
}
return AddEncryptionCertificate(certificate);
#else
throw new PlatformNotSupportedException("X.509 certificate generation is not supported on this platform.");
#endif
}
/// <summary>
/// Registers a new ephemeral key used to decrypt the tokens issued by OpenIddict: the key
/// is discarded when the application shuts down and tokens encrypted using this key are
/// automatically invalidated. This method should only be used during development.
/// On production, using a X.509 certificate stored in the machine store is recommended.
/// </summary>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
public OpenIddictValidationBuilder AddEphemeralEncryptionKey()
=> AddEphemeralEncryptionKey(SecurityAlgorithms.RsaOAEP);
/// <summary>
/// Registers a new ephemeral key used to decrypt the tokens issued by OpenIddict: the key
/// is discarded when the application shuts down and tokens encrypted using this key are
/// automatically invalidated. This method should only be used during development.
/// On production, using a X.509 certificate stored in the machine store is recommended.
/// </summary>
/// <param name="algorithm">The algorithm associated with the encryption key.</param>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
public OpenIddictValidationBuilder AddEphemeralEncryptionKey([NotNull] string algorithm)
{
if (string.IsNullOrEmpty(algorithm))
{
throw new ArgumentException("The algorithm cannot be null or empty.", nameof(algorithm));
}
switch (algorithm)
{
case SecurityAlgorithms.Aes256KW:
return AddEncryptionCredentials(new EncryptingCredentials(CreateSymmetricSecurityKey(256),
algorithm, SecurityAlgorithms.Aes256CbcHmacSha512));
case SecurityAlgorithms.RsaOAEP:
case SecurityAlgorithms.RsaOaepKeyWrap:
return AddEncryptionCredentials(new EncryptingCredentials(CreateRsaSecurityKey(2048),
algorithm, SecurityAlgorithms.Aes256CbcHmacSha512));
default: throw new InvalidOperationException("The specified algorithm is not supported.");
}
static SymmetricSecurityKey CreateSymmetricSecurityKey(int size)
{
var data = new byte[size / 8];
#if SUPPORTS_STATIC_RANDOM_NUMBER_GENERATOR_METHODS
RandomNumberGenerator.Fill(data);
#else
using var generator = RandomNumberGenerator.Create();
generator.GetBytes(data);
#endif
return new SymmetricSecurityKey(data);
}
static RsaSecurityKey CreateRsaSecurityKey(int size)
{
#if SUPPORTS_DIRECT_KEY_CREATION_WITH_SPECIFIED_SIZE
return new RsaSecurityKey(RSA.Create(size));
#else
// Note: a 1024-bit key might be returned by RSA.Create() on .NET Desktop/Mono,
// where RSACryptoServiceProvider is still the default implementation and
// where custom implementations can be registered via CryptoConfig.
// To ensure the key size is always acceptable, replace it if necessary.
var algorithm = RSA.Create();
if (algorithm.KeySize < size)
{
algorithm.KeySize = size;
}
if (algorithm.KeySize < size && algorithm is RSACryptoServiceProvider)
{
algorithm.Dispose();
algorithm = new RSACryptoServiceProvider(size);
}
if (algorithm.KeySize < size)
{
throw new InvalidOperationException("RSA key generation failed.");
}
return new RsaSecurityKey(algorithm);
#endif
}
}
/// <summary>
/// Registers a <see cref="X509Certificate2"/> that is used to decrypt the tokens issued by OpenIddict.
/// </summary>

Loading…
Cancel
Save