diff --git a/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs b/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs index 1fb8ff37..db06c0ad 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs +++ b/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); } - /// - /// Registers (and generates if necessary) a user-specific development - /// certificate used to decrypt the tokens issued by OpenIddict. - /// - /// The . - public OpenIddictValidationBuilder AddDevelopmentEncryptionCertificate() - => AddDevelopmentEncryptionCertificate(new X500DistinguishedName("CN=OpenIddict Validation Encryption Certificate")); - - /// - /// Registers (and generates if necessary) a user-specific development - /// certificate used to decrypt the tokens issued by OpenIddict. - /// - /// The subject name associated with the certificate. - /// The . - 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() - .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 - } - - /// - /// 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. - /// - /// The . - public OpenIddictValidationBuilder AddEphemeralEncryptionKey() - => AddEphemeralEncryptionKey(SecurityAlgorithms.RsaOAEP); - - /// - /// 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. - /// - /// The algorithm associated with the encryption key. - /// The . - 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 - } - } - /// /// Registers a that is used to decrypt the tokens issued by OpenIddict. ///