Я использую защиту данных для защиты секретов своего приложения в отдельном файле json. Каждому моему приложению я назначаю идентификатор Guid, который я использую в качестве appName для защиты данных. Каждое приложение имеет параметр cli для запуска сбора параметров, затем он передает объект параметра в этот метод общей библиотеки, который создает поставщик защиты данных, а затем защищает каждое свойство собранных параметров для шифрования
public static void WriteSecrets<T>(T secrets, string appName, string secretsFileName = "secrets.json") where T: class
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddDataProtection().SetApplicationName(appName);
var services = serviceCollection.BuildServiceProvider();
var dataProtectionProvider = services.GetService<IDataProtectionProvider>();
var protector = dataProtectionProvider.CreateProtector(appName);
var props = secrets.BasicProperties();
foreach (var prop in props)
{
var plainText = secrets.ObjectStringValue(prop);
if (!string.IsNullOrEmpty(plainText))
secrets.SetObjectValue(prop, protector.Protect(plainText));
}
var json = JsonSerializer.Serialize(secrets);
var path = Path.Combine(CurrentDirectory, secretsFileName);
File.WriteAllText(path, json);
Console.WriteLine($"Writing secret to '{path}' completed successfully.");
}
I затем используйте IO C, чтобы получить IDataProtectionProvider в нужных ему классах (например, мой класс, который обрабатывает генерацию токена JWT). Кажется, что IDataProtectionProvider регистрируется по умолчанию, я могу получить его через IApplicationBuilder.ApplicationServices.GetService в методе Configure моего Startup.cs.
Этот подход прекрасно работал для двух ASP. NET Core 3.1 Приложения, которые я создал.
Теперь я столкнулся с проблемой. Приложение, на которое я сейчас нацеливаюсь, дважды использует компоновщик хостов приложения c. Это сервисное приложение, размещающее веб-API. Приложение запускается с помощью средства запуска * generic c, созданного на основе узла службы generi c.
Это мой подход к размещению служб
public class ServiceLauncher<T> where T : class, IGenericService
{
private readonly IHostBuilder builder;
private readonly T service;
public ServiceLauncher(string[] args)
{
builder = CreateHostBuilder(args);
}
public ServiceLauncher(T service, string[] args): this(args)
{
this.service = service;
}
public void StartService()
{
if (service != null) // a service was already provided, register it as singleton
builder.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<IGenericService>(service);
});
else // register
builder.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<IGenericService, T>();
});
var host = builder.Build();
host.Run();
}
private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService() // enables running as a windows service
.ConfigureLogging(loggerFactory =>
{
loggerFactory.AddFile(options => // for file system logging
{
options.LogDirectory = @"c:\temp\";
options.FileName = "wrappedservice";
});
})
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<GenericServiceWorker>();
services.Configure<HostOptions>(options => // sett custom shutdown timeout
{
options.ShutdownTimeout = TimeSpan.FromSeconds(30);
});
});
}
, где GenericServiceWorker - это BackgroundService, который переопределяет StartAsync / StopAsync / ExecuteAsyn c для запуска приложения / остановки / работы. Затем само приложение запускается следующим образом:
var service = new PresenceManager()
var launcher = new ServiceLauncher<PresenceManager>(service, args);
launcher.StartService();
Когда запускается BackgroundService, запускается хостинг моего веб-API, поэтому создается новый хост и настраивается все так, как обычно, когда у меня есть обычный не обслуживаемый хостинг ASP. NET Базовое приложение. Сборка хоста довольно стандартна и выглядит следующим образом:
var builder = Host.CreateDefaultBuilder()
.UseServiceProviderFactory(new DryIocServiceProviderFactory(container)) // register to use DryIoc
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
config.AddJsonFile("secrets.json", optional: true, reloadOnChange: true);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.CaptureStartupErrors(true);
webBuilder.UseStartup<Startup>();
webBuilder.UseUrls(apiUrl);
});
host = builder.Build();
await host.StartAsync(ServerShutDownSource.Token).ConfigureAwait(false);
Обратите внимание, что это точно такой же способ, которым я запускаю свои не обслуживаемые хост-приложения ASP. NET ядра, а также практически тот же самый Автозагрузка. cs (различия относятся к ведению журнала и IO C, так как приложение с оболочкой использует DryIo c). Теперь мое приложение запускается, а затем я выполняю первую операцию, которая требует защиты данных и снятия защиты с одной из строк в секретах. json.
И теперь средство защиты данных выдает:
CryptographicException при вызове Unprotect говорит мне 'Полезная нагрузка была недействительной' .
Глядя на зашифрованную строку, она совпадает с тем, что находится в secrets.json
, и когда я вызываю метод в моей вспомогательной библиотеке lib который выполнил защиту от CLI, он может правильно расшифровать ту же самую строку. имя приложения, которое я использую для создания своего DataProtector, такое же, как и учетная запись, под которой выполняется все.
Таким образом, я озадачен - почему на моем хосте службы не работает сценарий хоста web api, почему не работает ?
@ edit: в качестве эксперимента я переписал код, который записывает секреты в secrets.json
- теперь я инициализирую хост Web API, в Startup.Configure, получаю экземпляр IDataProtectionProvider и устанавливаю его как единичный экземпляр для DryIo c. И тогда я защищаю свои секреты с этим IDataProtectionProvider. Сгенерированные зашифрованные строки отличаются от ранее, и теперь я могу расшифровать строки в моих контроллерах (которые вводят IDataProtectionProvider).
Так что я был на правильном пути, но почему я получаю разные зашифрованные строки в зависимости от того, откуда я получаю IDataProtectionProvider?