Использование .NET Core X509Certificate2 (под Windows / IIS, Docker, Linux) - PullRequest
0 голосов
/ 16 ноября 2018

Я очень долго пытаюсь использовать сертификаты в .NET Core API.

В основном, где я должен их использовать, это веб-API .NET Core, работающий на IIS и докере.

Сертификаты, которые мне нужно использовать, предназначены для:

Microsoft.AspNetCore.DataProtection

public void ConfigureServices(IServiceCollection services)
{
  services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(dataProtectionKeystorePath))
    .ProtectKeysWithCertificate
    (
      new X509Certificate2(dataProtectionCertificatePath, dataProtectionCertificateSecret)
    );
}

Опции Kestrel для установки SSL-сертификата

public static IWebHost BuildWebHost(string[] args) =>
  WebHost.CreateDefaultBuilder(args)
    .UseKestrel
    (
      options =>
        {
          options.Listen(address, port, listenOptions =>
            {
              listenOptions.UseHttps(new X509Certificate2(sslCertificatePath, sslCertifciateSecret));
            }
          );
        }
      )
      // other setup
     ;

IdentityServer4.SigningCredentials

Примечание: этот код работает на компьютере разработчика, начиная с VS2017, но выдает эти исключения на тестовом сервере Windows 2008 R2 IIS.

services.AddIdentityServer()
  .AddSigningCredential
  (
    new X509Certificate2(tokenCertificatePath, tokenCertificatePassphrase)
  )
  // other setup
  ;

Все три подразумевают размещение файла сертификата, загрузку его с помощью конструктора, передачу секрета и пойдем.

sarkasm> Счастливо, это так просто.

Итак, я создал поддерево сертификатов, содержащее сертификаты. Добавлены настройки для секретов. Проверено все значения загружены / созданы как задумано. Проверенные файлы находятся в намеченных местах и ​​для этого существуют. Короче что-то вроде:

string dataProtectionKeystorePath = System.Path.Combine(Environment.ContentRootPath, "keystore");
string dataProtectionCertificatePath = System.Path.Combine(Environment.ContentRootPath, "certs", "keystore.pfx");
string dataProtectionSecret = Configuration.GetSection("CertificateSecrets").GetValue<string>("keystoreSecret", null);

string tokenCertificatePath = System.Path.Combine(Environment.ContentRootPath, "certs", "token.pfx");
string tokenCertificateSecret = Configuration.GetSection("CertificateSecrets").GetValue<string>("tokenSecret", null);

string sslCertificatePath = System.Path.Combine(Environment.ContentRootPath, "certs", "ssl.pfx");
string sslCertificateSecret = Configuration.GetSection("CertificateSecrets").GetValue<string>("tokenSecret", null);

Давай повеселимся, сказали они. Итак, пойдем и развернем.

В результате я получаю следующие исключения:

Исключение защиты данных:

info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[58]
      Creating key {39560783-e349-475e-8e3f-748abb8c6c8b} with creation date 2018-11-16 08:01:49Z, activation date 2018-11-16 08:01:49Z, and expiration date 2019-02-14 08:01:49Z.
info: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[39]
      Writing data to file '[intentionally removed for post]'.
fail: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[24]
      An exception occurred while processing the key element '<key id="39560783-e349-475e-8e3f-748abb8c6c8b" version="1" />'.
Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Keyset does not exist
   at Internal.NativeCrypto.CapiHelper.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
   at System.Security.Cryptography.RSACryptoServiceProvider.get_SafeProvHandle()
   at System.Security.Cryptography.RSACryptoServiceProvider.get_SafeKeyHandle()
   at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 keySize, CspParameters parameters, Boolean useDefaultKeySize)
   at System.Security.Cryptography.RSACryptoServiceProvider..ctor(CspParameters parameters)
   at Internal.Cryptography.Pal.CertificatePal.<>c.<GetRSAPrivateKey>b__61_0(CspParameters csp)
   at Internal.Cryptography.Pal.CertificatePal.GetPrivateKey[T](Func`2 createCsp, Func`2 createCng)
   at Internal.Cryptography.Pal.CertificatePal.GetRSAPrivateKey()
   at Internal.Cryptography.Pal.CertificateExtensionsCommon.GetPrivateKey[T](X509Certificate2 certificate, Predicate`1 matchesConstraints)
   at System.Security.Cryptography.X509Certificates.RSACertificateExtensions.GetRSAPrivateKey(X509Certificate2 certificate)
   at Microsoft.AspNetCore.DataProtection.XmlEncryption.EncryptedXmlDecryptor.EncryptedXmlWithCertificateKeys.GetKeyFromCert(EncryptedKey encryptedKey, KeyInfoX509Data keyInfo)
   at Microsoft.AspNetCore.DataProtection.XmlEncryption.EncryptedXmlDecryptor.EncryptedXmlWithCertificateKeys.DecryptEncryptedKey(EncryptedKey encryptedKey)
   at System.Security.Cryptography.Xml.EncryptedXml.GetDecryptionKey(EncryptedData encryptedData, String symmetricAlgorithmUri)
   at System.Security.Cryptography.Xml.EncryptedXml.DecryptDocument()
   at Microsoft.AspNetCore.DataProtection.XmlEncryption.EncryptedXmlDecryptor.Decrypt(XElement encryptedElement)
   at Microsoft.AspNetCore.DataProtection.XmlEncryption.XmlEncryptionExtensions.DecryptElement(XElement element, IActivator activator)
   at Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager.Microsoft.AspNetCore.DataProtection.KeyManagement.Internal.IInternalXmlKeyManager.DeserializeDescriptorFromKeyElement(XElement keyElement)
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[12]
      Key {39560783-e349-475e-8e3f-748abb8c6c8b} is ineligible to be the default key because its CreateEncryptor method failed.
Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Keyset does not exist
   at Internal.NativeCrypto.CapiHelper.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
   at System.Security.Cryptography.RSACryptoServiceProvider.get_SafeProvHandle()
   at System.Security.Cryptography.RSACryptoServiceProvider.get_SafeKeyHandle()
   at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 keySize, CspParameters parameters, Boolean useDefaultKeySize)
   at System.Security.Cryptography.RSACryptoServiceProvider..ctor(CspParameters parameters)
   at Internal.Cryptography.Pal.CertificatePal.<>c.<GetRSAPrivateKey>b__61_0(CspParameters csp)
   at Internal.Cryptography.Pal.CertificatePal.GetPrivateKey[T](Func`2 createCsp, Func`2 createCng)
   at Internal.Cryptography.Pal.CertificatePal.GetRSAPrivateKey()
   at Internal.Cryptography.Pal.CertificateExtensionsCommon.GetPrivateKey[T](X509Certificate2 certificate, Predicate`1 matchesConstraints)
   at System.Security.Cryptography.X509Certificates.RSACertificateExtensions.GetRSAPrivateKey(X509Certificate2 certificate)
   at Microsoft.AspNetCore.DataProtection.XmlEncryption.EncryptedXmlDecryptor.EncryptedXmlWithCertificateKeys.GetKeyFromCert(EncryptedKey encryptedKey, KeyInfoX509Data keyInfo)
   at Microsoft.AspNetCore.DataProtection.XmlEncryption.EncryptedXmlDecryptor.EncryptedXmlWithCertificateKeys.DecryptEncryptedKey(EncryptedKey encryptedKey)
   at System.Security.Cryptography.Xml.EncryptedXml.GetDecryptionKey(EncryptedData encryptedData, String symmetricAlgorithmUri)
   at System.Security.Cryptography.Xml.EncryptedXml.DecryptDocument()
   at Microsoft.AspNetCore.DataProtection.XmlEncryption.EncryptedXmlDecryptor.Decrypt(XElement encryptedElement)
   at Microsoft.AspNetCore.DataProtection.XmlEncryption.XmlEncryptionExtensions.DecryptElement(XElement element, IActivator activator)
   at Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager.Microsoft.AspNetCore.DataProtection.KeyManagement.Internal.IInternalXmlKeyManager.DeserializeDescriptorFromKeyElement(XElement keyElement)
   at Microsoft.AspNetCore.DataProtection.KeyManagement.DeferredKey.<>c__DisplayClass1_0.<GetLazyDescriptorDelegate>b__0()
   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
   at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
   at System.Lazy`1.CreateValue()
   at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyBase.get_Descriptor()
   at Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.CngGcmAuthenticatedEncryptorFactory.CreateEncryptorInstance(IKey key)
   at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyBase.CreateEncryptor()
   at Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver.CanCreateAuthenticatedEncryptor(IKey key)
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver[12]
      Key {39560783-e349-475e-8e3f-748abb8c6c8b} is ineligible to be the default key because its CreateEncryptor method failed.
Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Keyset does not exist
   at Internal.NativeCrypto.CapiHelper.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
   at System.Security.Cryptography.RSACryptoServiceProvider.get_SafeProvHandle()
   at System.Security.Cryptography.RSACryptoServiceProvider.get_SafeKeyHandle()
   at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 keySize, CspParameters parameters, Boolean useDefaultKeySize)
   at System.Security.Cryptography.RSACryptoServiceProvider..ctor(CspParameters parameters)
   at Internal.Cryptography.Pal.CertificatePal.<>c.<GetRSAPrivateKey>b__61_0(CspParameters csp)
   at Internal.Cryptography.Pal.CertificatePal.GetPrivateKey[T](Func`2 createCsp, Func`2 createCng)
   at Internal.Cryptography.Pal.CertificatePal.GetRSAPrivateKey()
   at Internal.Cryptography.Pal.CertificateExtensionsCommon.GetPrivateKey[T](X509Certificate2 certificate, Predicate`1 matchesConstraints)
   at System.Security.Cryptography.X509Certificates.RSACertificateExtensions.GetRSAPrivateKey(X509Certificate2 certificate)
   at Microsoft.AspNetCore.DataProtection.XmlEncryption.EncryptedXmlDecryptor.EncryptedXmlWithCertificateKeys.GetKeyFromCert(EncryptedKey encryptedKey, KeyInfoX509Data keyInfo)
   at Microsoft.AspNetCore.DataProtection.XmlEncryption.EncryptedXmlDecryptor.EncryptedXmlWithCertificateKeys.DecryptEncryptedKey(EncryptedKey encryptedKey)
   at System.Security.Cryptography.Xml.EncryptedXml.GetDecryptionKey(EncryptedData encryptedData, String symmetricAlgorithmUri)
   at System.Security.Cryptography.Xml.EncryptedXml.DecryptDocument()
   at Microsoft.AspNetCore.DataProtection.XmlEncryption.EncryptedXmlDecryptor.Decrypt(XElement encryptedElement)
   at Microsoft.AspNetCore.DataProtection.XmlEncryption.XmlEncryptionExtensions.DecryptElement(XElement element, IActivator activator)
   at Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager.Microsoft.AspNetCore.DataProtection.KeyManagement.Internal.IInternalXmlKeyManager.DeserializeDescriptorFromKeyElement(XElement keyElement)
   at Microsoft.AspNetCore.DataProtection.KeyManagement.DeferredKey.<>c__DisplayClass1_0.<GetLazyDescriptorDelegate>b__0()
   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
--- End of stack trace from previous location where exception was thrown ---
   at System.Lazy`1.CreateValue()
   at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyBase.get_Descriptor()
   at Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.CngGcmAuthenticatedEncryptorFactory.CreateEncryptorInstance(IKey key)
   at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyBase.CreateEncryptor()
   at Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver.CanCreateAuthenticatedEncryptor(IKey key)

Исключение для учетных данных для подписи Identity Server 4

Application startup exception: Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: An internal error occurred
   at Internal.Cryptography.Pal.CertificatePal.FilterPFXStore(Byte[] rawData, SafePasswordHandle password, PfxCertStoreFlags pfxCertStoreFlags)
   at Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile(Byte[] rawData, String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
   at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName, String password, X509KeyStorageFlags keyStorageFlags)
   at AuthServer.Startup.ConfigureServices(IServiceCollection services) in [intentionally removed for post]\Startup.cs:line 136
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.ConfigureServices(IServiceCollection services)
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.EnsureApplicationServices()
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.Initialize()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
crit: Microsoft.AspNetCore.Hosting.Internal.WebHost[6]
      Application startup exception
Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: An internal error occurred
   at Internal.Cryptography.Pal.CertificatePal.FilterPFXStore(Byte[] rawData, SafePasswordHandle password, PfxCertStoreFlags pfxCertStoreFlags)
   at Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile(Byte[] rawData, String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
   at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName, String password, X509KeyStorageFlags keyStorageFlags)
   at AuthServer.Startup.ConfigureServices(IServiceCollection services) in [intentionally removed for post]\Startup.cs:line 136
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.ConfigureServices(IServiceCollection services)
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.EnsureApplicationServices()
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.Initialize()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()

Исключение для сертификата SSL

Unhandled Exception: Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: An internal error occurred
   at Internal.Cryptography.Pal.CertificatePal.FilterPFXStore(Byte[] rawData, SafePasswordHandle password, PfxCertStoreFlags pfxCertStoreFlags)
   at Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile(Byte[] rawData, String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
   at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName, String password, X509KeyStorageFlags keyStorageFlags)
   at AuthServer.Program.<>c.<BuildWebHost>b__1_3(ListenOptions listenOptions) in [intentionally removed for post]\Program.cs:line 58
   at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.Listen(IPEndPoint endPoint, Action`1 configure)
   at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.Listen(IPAddress address, Int32 port, Action`1 configure)
   at Microsoft.Extensions.Options.ConfigureNamedOptions`1.Configure(String name, TOptions options)
   at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name)
   at Microsoft.Extensions.Options.OptionsManager`1.<>c__DisplayClass5_0.<Get>b__0()
   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
--- End of stack trace from previous location where exception was thrown ---
   at System.Lazy`1.CreateValue()
   at Microsoft.Extensions.Options.OptionsCache`1.GetOrAdd(String name, Func`1 createOptions)
   at Microsoft.Extensions.Options.OptionsManager`1.Get(String name)
   at Microsoft.Extensions.Options.OptionsManager`1.get_Value()
   at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer.CreateServiceContext(IOptions`1 options, ILoggerFactory loggerFactory)
   at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer..ctor(IOptions`1 options, ITransportFactory transportFactory, ILoggerFactory loggerFactory)
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitSingleton(SingletonCallSite singletonCallSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.EnsureServer()
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
   at Microsoft.AspNetCore.Hosting.Internal.WebHost.StartAsync(CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Hosting.WebHostExtensions.RunAsync(IWebHost host, CancellationToken token, String shutdownMessage)
   at Microsoft.AspNetCore.Hosting.WebHostExtensions.RunAsync(IWebHost host, CancellationToken token)
   at Microsoft.AspNetCore.Hosting.WebHostExtensions.Run(IWebHost host)
   at MyProject.Program.Main(String[] args) in [intentionally removed for post]\Program.cs:line 47

Когда я уже несколько месяцев сталкиваюсь с этими исключениями для сертификатов, много гуглю и снова сталкиваюсь с этими исключениями, что бы я ни пытался (в соответствии с результатами Google, вопросами, заданными на github и т. Д.), Мне приходит в голову, что я что-то пропускаю при импорте.

Я пытался использовать StorageFlags, но не нашел ни одной (комбинации) работающей.

Проекты должны выполняться в Windows (IIS), Docker и, возможно, один раз в Linux (Ubuntu, Debian). Вот почему мы решили разместить сертификаты в подкаталоге (смонтированном в нескольких экземплярах). Поэтому сообщения, которые я обнаружил, предлагающие установить сертификаты в хранилище сертификатов Microsoft, не будут работать. Как это может быть на Docker и Linux?

Поскольку все сертификаты, которыми я обычно пользуюсь, затронуты, я пришел к выводу, что проблема может заключаться в том, что у меня возникло серьезное недопонимание в отношении того, как использовать сертификаты.

Может ли кто-нибудь помочь мне выяснить главный момент, который я пропустил, и, наконец, заставить работать сертификаты? Нужно ли что-то настраивать? Если так, что?


Просто краткое изложение расследования:

Я уверен, что файлы существуют: да, потому что в противном случае исключение было бы

System.Security.Cryptography.CryptographicException: System cannot find specified file.

Уверен ли я, что пароль правильный: да, потому что в противном случае исключение будет

Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: The specified network password is not correct.

Права доступа к файлам: благодаря CheshireCat, напоминающей мне. В Windows Server 2008 R2 IIS 7.5 я проверил права доступа к файлу в подкаталоге certs для пользователя DefaultAppPool (думал, что теперь это будет другой пользовательский unitl). Полные права доступа к файлу для подкаталога certs были предоставлены пользователю DefaultAppPool , как показано в этой ссылке .

Обычно при запуске приложения возникают исключения учетных данных подписи IS4. Использование X509KeyStorageFlags.MachineKeySet не вызывает при запуске, но показывает окно входа в систему и выбрасывает после входа в систему. Токен не возвращается, но сеанс создан, чтобы пользователь мог изменить настройки учетной записи ASP NET Identity.

Срок действия сертификата: срок действия сертификата может истечь. Защита данных не проверяет дату истечения срока действия, поэтому сертификаты можно использовать даже после истечения срока их действия. Учетные данные подписи Identity Server 4 по-прежнему можно использовать, но проверка маркера ядра ASP.NET вызовет несанкционированный доступ, если срок действия сертификата истечет.


Решение проблемы

По истечении полутора лет решение заключается в следующем: каким-то образом испорченный (самоподписанный / самостоятельно созданный) сертификат.

Если сертификат работает, можно проверить с помощью этого кода:

  static void Main(string[] args)
  {
    X509Certificate2 cert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "cert.pfx"), "password");
    Console.WriteLine("cert private key: " + cert.PrivateKey);
  }

Если вы видите следующий вывод, сертификат хорош, в противном случае вы получите исключения, как описано выше.

cert private key: System.Security.Cryptography.RSACng

Создание сертификатов для разработки

Сертификат SSL

Используйте dev-certs инструмента dotnet. Этот вызов открывает приглашение, а затем импортирует SSL-сертификат localhost в хранилище сертификатов CurrentUser ( просмотреть его с MMC ). Никаких изменений кода в Программе не требуется.

dotnet dev-certs https --trust
Другие сертификаты

"Сломанные" сертификаты были созданы с использованием makecert . Я переключился на OpenSSL , но также мог использовать инструмент New-SelfSignedCertificate (если вы используете Win 10).

Если OpenSSL установлен правильно и добавлен в переменную PATH, следующий пакетный файл создает рабочие сертификаты (требуется взаимодействие с пользователем!).

  openssl genrsa 2048 > private.pem
  openssl req -x509 -days 365 -new -key private.pem -out public.pem
  openssl pkcs12 -export -in public.pem -inkey private.pem -out cert.pfx
  REM openssl pkcs12 -info -in cert.pfx
  certutil -dump cert.pfx
  PAUSE

Настройка для локального развития (Win 7, VS 2017, IIS Express / Kestrel)

Как Cheshire-Cat (опять же большое спасибо) описал код для всех частей, работающих, как и ожидалось, в .NET Core 2.1. Включая учетные данные для защиты данных и подписи Identity Server 4.


Развертывание в IIS 7.5 в Windows 2008 R2

Веселье продолжается. Получение сертификатов для работы на локальной машине разработки и развертывание их на IIS вызывает «новые» исключения.

  • проверены права доступа к файлам в IIS (у DefaultAppPool есть полные права)
  • Сертификаты такие же, что и на локальной машине
  • используемые сертификаты не установлены (не на компьютере разработчика, не на компьютере IIS), поэтому должны быть взяты фактические файлы.

журналы показывают в основном хорошую настройку

  using data protection keystore path C:\inetpub\wwwroot\auth\keystore
  data protection keystore is protected with certificate at path 'C:\inetpub\wwwroot\auth\certs\keystore.pfx'
  loading keystore certificate from file 'C:\inetpub\wwwroot\auth\certs\keystore.pfx'
  loading keystore certificate with passphrase ********

  Application startup exception: Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: An internal error occurred
     at Internal.Cryptography.Pal.CertificatePal.FilterPFXStore(Byte[] rawData, SafePasswordHandle password, PfxCertStoreFlags pfxCertStoreFlags)
     at Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile(Byte[] rawData, String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
     at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName, String password, X509KeyStorageFlags keyStorageFlags)
     at [SomeOfMyNamespaces].X509Certificate2Loader.LoadFromFile(String certName, String certPath, String certPassphrase, ILogger logger) in [intentionally-blanked]\X509Certificate2Loader.cs:line 57
     at [SomeOfMyNamespaces].DataProtectionExtensions.AddDataProtection(IServiceCollection services, IConfiguration config, IHostingEnvironment environment, String applicationName, String applicationVersion, ILogger logger) in [intentionally-blanked]\DataProtectionExtensions.cs:line 207
     at [SomeOfMyNamespaces].Startup.ConfigureServices(IServiceCollection services) in [intentionally-blanked]\Startup.cs:line 96
  --- End of stack trace from previous location where exception was thrown ---
     at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.ConfigureServices(IServiceCollection services)
     at Microsoft.AspNetCore.Hosting.Internal.WebHost.EnsureApplicationServices()
     at Microsoft.AspNetCore.Hosting.Internal.WebHost.Initialize()
  --- End of stack trace from previous location where exception was thrown ---
     at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
  crit: Microsoft.AspNetCore.Hosting.Internal.WebHost[6]
        Application startup exception
  Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: An internal error occurred
     at Internal.Cryptography.Pal.CertificatePal.FilterPFXStore(Byte[] rawData, SafePasswordHandle password, PfxCertStoreFlags pfxCertStoreFlags)
     at Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile(Byte[] rawData, String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
     at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName, String password, X509KeyStorageFlags keyStorageFlags)
     at [SomeOfMyNamespaces].X509Certificate2Loader.LoadFromFile(String certName, String certPath, String certPassphrase, ILogger logger) in [intentionally-blanked]\X509Certificate2Loader.cs:line 57
     at [SomeOfMyNamespaces].DataProtectionExtensions.AddDataProtection(IServiceCollection services, IConfiguration config, IHostingEnvironment environment, String applicationName, String applicationVersion, ILogger logger) in [intentionally-blanked]\DataProtectionExtensions.cs:line 207
     at [SomeOfMyNamespaces].Startup.ConfigureServices(IServiceCollection services) in [intentionally-blanked]\Startup.cs:line 96
  --- End of stack trace from previous location where exception was thrown ---
     at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.ConfigureServices(IServiceCollection services)
     at Microsoft.AspNetCore.Hosting.Internal.WebHost.EnsureApplicationServices()
     at Microsoft.AspNetCore.Hosting.Internal.WebHost.Initialize()
  --- End of stack trace from previous location where exception was thrown ---
     at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()

Ссылочная строка 57:

  cert = new X509Certificate2(certPath, certPassphrase);

Такое же исключение возникает для учетных данных подписи Identity Server 4.

Предоставление еще одной награды в 100 за ответ, помогающий мне запустить его на IIS в Windows Server 2008 R2.

Решение для IIS и Docker (и локальной разработки тоже): при использовании этого и ни IIS, ни настройка Docker не будут жаловаться:

cert = new X509Certificate2(certPath, certPassphrase, X509KeyStorageFlags.MachineKeySet);

1 Ответ

0 голосов
/ 16 ноября 2018

Мой настоящий код, запущенный в моем проекте IS4, выглядит так:

X509Certificate2 certificate = null;

// Load certificate from Certificate Store using the configured Thumbprint
using (X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
    store.Open(OpenFlags.ReadOnly);
    X509Certificate2Collection certificates = store.Certificates.Find(X509FindType.FindByThumbprint, appConfiguration.CertificateThumbprint, false);

    if (certificates.Count > 0)
        certificate = certificates[0];
}

// Fallback to load certificate from local file
if (certificate == null)
{
    string path = Path.Combine("C:\\Certificates", appConfiguration.CertificateFilename);

    try
    {
        certificate = new X509Certificate2(path, "CertificateSecret123$");
        logger.LogInformation($"Found from file {certificate.Thumbprint}");
    }
    catch (Exception ex)
    {
        logger.LogError(ex, $"Certificate file error {path}");
        certificate = null;
    }
}

if (certificate == null)
    throw new Exception($"Certificate {appConfiguration.CertificateThumbprint} not found.");

builder.AddSigningCredential(certificate);

Если я изменю отпечаток, чтобы заставить код искать сертификат из локального файла, я получу ожидаемый результат:

Result

ОБНОВЛЕНИЕ 2018/11/19

Я исправил ошибку в коде из-за того, что в случае, если файл сертификата не найден, конструктор класса X509Certificate2 выдает исключение. Таким образом, предыдущий элемент управления if (certificate == null) после строки certificate = new X509Certificate2... никогда бы не был достигнут.

Шаги, чтобы заставить код работать:

  1. Создайте сертификат и поместите его в каталог
  2. Проверьте права доступа к папке для пользователя, который запускает код.
    • Если вы на машине разработки , убедитесь, что у пользователя, который запускает VS, есть права доступа к каталогу, в котором находится файл.
    • Если вы используете IIS , отметьте AppPool, в котором размещается ваше приложение и пользователя, с которым оно выполняется. Он должен иметь разрешения на каталог сертификатов.
  3. Загрузить сертификат из файла , используя образец кода. Будьте уверены в правильности пути.
  4. Загрузить сертификат из магазина с помощью отпечатка пальца. Чтобы получить отпечаток сертификата, вы можете:
    • Используйте PowerShell , команду Get-ChildItem -path cert:\LocalMachine\My, указав правильное место в хранилище, где вы зарегистрировали сертификат.
    • Используйте Windows Explorer, щелкнув правой кнопкой мыши файл .pfx -> Открыть -> найдите свой файл в консоли MMC -> дважды щелкните по нему -> Детали -> Отпечаток. ВАЖНОЕ ПРИМЕЧАНИЕ : в Windows существует известная уродливая ошибка при копировании / вставке значения Thumbprint из текстового поля предварительного просмотра. Скрытый символ добавляется в начале отпечатка при вставке значения. Поэтому сначала скопируйте / вставьте его в текстовый редактор, а затем проверьте его, перемещая клавиши со стрелками рядом с первым символом. См. Также this .
...