public void StoreElement_WithInvalidFriendlyName_CreatesNewGuidAsName(string friendlyName) { WithUniqueTempDirectory(dirInfo => { // Arrange var element = XElement.Parse("<element1 />"); var repository = new FileSystemXmlRepository(dirInfo, NullLoggerFactory.Instance); // Act repository.StoreElement(element, friendlyName); // Assert var fileInfos = dirInfo.GetFiles(); var fileInfo = fileInfos.Single(); // only one file should've been created // filename should be "{GUID}.xml" var filename = fileInfo.Name; Assert.EndsWith(".xml", filename, StringComparison.OrdinalIgnoreCase); var filenameNoSuffix = filename.Substring(0, filename.Length - ".xml".Length); Guid parsedGuid = Guid.Parse(filenameNoSuffix, CultureInfo.InvariantCulture); Assert.NotEqual(Guid.Empty, parsedGuid); // file contents should be "<element1 />" var parsedElement = XElement.Parse(File.ReadAllText(fileInfo.FullName)); XmlAssert.Equal("<element1 />", parsedElement); }); }
public void GetAllElements_EmptyOrNonexistentDirectory_ReturnsEmptyCollection() { WithUniqueTempDirectory(dirInfo => { // Arrange var repository = new FileSystemXmlRepository(dirInfo, NullLoggerFactory.Instance); // Act var allElements = repository.GetAllElements(); // Assert Assert.Equal(0, allElements.Count); }); }
public void Directory_Property() { WithUniqueTempDirectory(dirInfo => { // Arrange var repository = new FileSystemXmlRepository(dirInfo, NullLoggerFactory.Instance); // Act var retVal = repository.Directory; // Assert Assert.Equal(dirInfo, retVal); }); }
public void Logs_DockerEphemeralFolders() { // Arrange var loggerFactory = new StringLoggerFactory(LogLevel.Warning); WithUniqueTempDirectory(dirInfo => { // Act var repo = new FileSystemXmlRepository(dirInfo, loggerFactory); // Assert Assert.Contains(Resources.FormatFileSystem_EphemeralKeysLocationInContainer(dirInfo.FullName), loggerFactory.ToString()); }); }
public void StoreElements_ThenRetrieve_SeesAllElements() { WithUniqueTempDirectory(dirInfo => { // Arrange var repository = new FileSystemXmlRepository(dirInfo, NullLoggerFactory.Instance); // Act repository.StoreElement(new XElement("element1"), friendlyName: null); repository.StoreElement(new XElement("element2"), friendlyName: null); repository.StoreElement(new XElement("element3"), friendlyName: null); var allElements = repository.GetAllElements(); // Assert var orderedNames = allElements.Select(el => el.Name.LocalName).OrderBy(name => name); Assert.Equal(new[] { "element1", "element2", "element3" }, orderedNames); }); }
/// <summary> /// https://github.com/dotnet/aspnetcore/blob/d8906c8523f071371ce95d4e2d2fdfa89858047e/src/DataProtection/DataProtection/src/KeyManagement/XmlKeyManager.cs#L105 /// </summary> /// <returns></returns> internal IXmlRepository GetFallbackKeyRepositoryEncryptorPair() { IXmlRepository key; var forAzureWebSites = DefaultKeyStorageDirectories.Instance.GetKeyStorageDirectoryForAzureWebSites(); if (forAzureWebSites != null) { key = new FileSystemXmlRepository(forAzureWebSites, this._loggerFactory); } else { var storageDirectory = DefaultKeyStorageDirectories.Instance.GetKeyStorageDirectory(); if (storageDirectory != null) { key = new FileSystemXmlRepository(storageDirectory, this._loggerFactory); } else { RegistryKey registryKey = null; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { registryKey = RegistryXmlRepository.DefaultRegistryKey; } if (registryKey != null) { var defaultRegistryKey = RegistryXmlRepository.DefaultRegistryKey; key = new RegistryXmlRepository(defaultRegistryKey, this._loggerFactory); } else { throw new Exception( "Is not possible to determine which folder are the protection keys. NetDevPack.Security.JwtSigningCredentials.Store.FileSystem or NetDevPack.Security.JwtSigningCredentials.Store.EntityFrameworkCore"); } } } return(key); }
public void StoreElement_WithValidFriendlyName_UsesFriendlyName() { WithUniqueTempDirectory(dirInfo => { // Arrange var element = XElement.Parse("<element1 />"); var repository = new FileSystemXmlRepository(dirInfo, NullLoggerFactory.Instance); // Act repository.StoreElement(element, "valid-friendly-name"); // Assert var fileInfos = dirInfo.GetFiles(); var fileInfo = fileInfos.Single(); // only one file should've been created // filename should be "valid-friendly-name.xml" Assert.Equal("valid-friendly-name.xml", fileInfo.Name, StringComparer.OrdinalIgnoreCase); // file contents should be "<element1 />" var parsedElement = XElement.Parse(File.ReadAllText(fileInfo.FullName)); XmlAssert.Equal("<element1 />", parsedElement); }); }
internal KeyValuePair <IXmlRepository, IXmlEncryptor> GetFallbackKeyRepositoryEncryptorPair() { IXmlRepository repository = null; IXmlEncryptor encryptor = null; // If we're running in Azure Web Sites, the key repository goes in the %HOME% directory. var azureWebSitesKeysFolder = _keyStorageDirectories.GetKeyStorageDirectoryForAzureWebSites(); if (azureWebSitesKeysFolder != null) { _logger.UsingAzureAsKeyRepository(azureWebSitesKeysFolder.FullName); // Cloud DPAPI isn't yet available, so we don't encrypt keys at rest. // This isn't all that different than what Azure Web Sites does today, and we can always add this later. repository = new FileSystemXmlRepository(azureWebSitesKeysFolder, _loggerFactory); } else { // If the user profile is available, store keys in the user profile directory. var localAppDataKeysFolder = _keyStorageDirectories.GetKeyStorageDirectory(); if (localAppDataKeysFolder != null) { if (OSVersionUtil.IsWindows()) { Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); // Hint for the platform compatibility analyzer. // If the user profile is available, we can protect using DPAPI. // Probe to see if protecting to local user is available, and use it as the default if so. encryptor = new DpapiXmlEncryptor( protectToLocalMachine: !DpapiSecretSerializerHelper.CanProtectToCurrentUserAccount(), loggerFactory: _loggerFactory); } repository = new FileSystemXmlRepository(localAppDataKeysFolder, _loggerFactory); if (encryptor != null) { _logger.UsingProfileAsKeyRepositoryWithDPAPI(localAppDataKeysFolder.FullName); } else { _logger.UsingProfileAsKeyRepository(localAppDataKeysFolder.FullName); } } else { // Use profile isn't available - can we use the HKLM registry? RegistryKey regKeyStorageKey = null; if (OSVersionUtil.IsWindows()) { Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); // Hint for the platform compatibility analyzer. regKeyStorageKey = RegistryXmlRepository.DefaultRegistryKey; } if (regKeyStorageKey != null) { Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); // Hint for the platform compatibility analyzer. regKeyStorageKey = RegistryXmlRepository.DefaultRegistryKey; // If the user profile isn't available, we can protect using DPAPI (to machine). encryptor = new DpapiXmlEncryptor(protectToLocalMachine: true, loggerFactory: _loggerFactory); repository = new RegistryXmlRepository(regKeyStorageKey, _loggerFactory); _logger.UsingRegistryAsKeyRepositoryWithDPAPI(regKeyStorageKey.Name); } else { // Final fallback - use an ephemeral repository since we don't know where else to go. // This can only be used for development scenarios. repository = new EphemeralXmlRepository(_loggerFactory); _logger.UsingEphemeralKeyRepository(); } } } return(new KeyValuePair <IXmlRepository, IXmlEncryptor>(repository, encryptor)); }
/// <summary> /// Returns a collection of default <see cref="ServiceDescriptor"/> instances that can be /// used to bootstrap the Data Protection system. /// </summary> public static IEnumerable <ServiceDescriptor> GetDefaultServices() { // The default key services are a strange beast. We don't want to return // IXmlEncryptor and IXmlRepository as-is because they almost always have to be // set as a matched pair. Instead, our built-in key manager will use a meta-service // which represents the default pairing (logic based on hosting environment as // demonstrated below), and if the developer explicitly specifies one or the other // we'll not use the fallback at all. yield return(ServiceDescriptor.Singleton <IDefaultKeyServices>(services => { var log = services.GetLogger(typeof(DataProtectionServices)); ServiceDescriptor keyEncryptorDescriptor = null; ServiceDescriptor keyRepositoryDescriptor = null; // If we're running in Azure Web Sites, the key repository goes in the %HOME% directory. var azureWebSitesKeysFolder = FileSystemXmlRepository.GetKeyStorageDirectoryForAzureWebSites(); if (azureWebSitesKeysFolder != null) { log?.UsingAzureAsKeyRepository(azureWebSitesKeysFolder.FullName); // Cloud DPAPI isn't yet available, so we don't encrypt keys at rest. // This isn't all that different than what Azure Web Sites does today, and we can always add this later. keyRepositoryDescriptor = DataProtectionServiceDescriptors.IXmlRepository_FileSystem(azureWebSitesKeysFolder); } else { // If the user profile is available, store keys in the user profile directory. var localAppDataKeysFolder = FileSystemXmlRepository.DefaultKeyStorageDirectory; if (localAppDataKeysFolder != null) { if (OSVersionUtil.IsWindows()) { // If the user profile is available, we can protect using DPAPI. // Probe to see if protecting to local user is available, and use it as the default if so. keyEncryptorDescriptor = DataProtectionServiceDescriptors.IXmlEncryptor_Dpapi(protectToMachine: !DpapiSecretSerializerHelper.CanProtectToCurrentUserAccount()); } keyRepositoryDescriptor = DataProtectionServiceDescriptors.IXmlRepository_FileSystem(localAppDataKeysFolder); if (keyEncryptorDescriptor != null) { log?.UsingProfileAsKeyRepositoryWithDPAPI(localAppDataKeysFolder.FullName); } else { log?.UsingProfileAsKeyRepository(localAppDataKeysFolder.FullName); } } else { // Use profile isn't available - can we use the HKLM registry? RegistryKey regKeyStorageKey = null; if (OSVersionUtil.IsWindows()) { regKeyStorageKey = RegistryXmlRepository.DefaultRegistryKey; } if (regKeyStorageKey != null) { // If the user profile isn't available, we can protect using DPAPI (to machine). keyEncryptorDescriptor = DataProtectionServiceDescriptors.IXmlEncryptor_Dpapi(protectToMachine: true); keyRepositoryDescriptor = DataProtectionServiceDescriptors.IXmlRepository_Registry(regKeyStorageKey); log?.UsingRegistryAsKeyRepositoryWithDPAPI(regKeyStorageKey.Name); } else { // Final fallback - use an ephemeral repository since we don't know where else to go. // This can only be used for development scenarios. keyRepositoryDescriptor = ServiceDescriptor.Singleton <IXmlRepository>( s => new EphemeralXmlRepository(s)); log?.UsingEphemeralKeyRepository(); } } } return new DefaultKeyServices( services: services, keyEncryptorDescriptor: keyEncryptorDescriptor, keyRepositoryDescriptor: keyRepositoryDescriptor); })); // Provide root key management and data protection services yield return(ServiceDescriptor.Singleton <IKeyManager>(services => new XmlKeyManager(services))); yield return(ServiceDescriptor.Singleton <IDataProtectionProvider>( services => DataProtectionProviderFactory.GetProviderFromServices( options: services.GetRequiredService <IOptions <DataProtectionOptions> >().Value, services: services, mustCreateImmediately: true /* this is the ultimate fallback */))); // Provide services required for XML encryption #if !NETSTANDARD1_3 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml yield return(DataProtectionServiceDescriptors.ICertificateResolver_Default()); #endif // Hook up the logic which allows populating default options yield return(ServiceDescriptor.Transient <IConfigureOptions <DataProtectionOptions> >(services => { return new ConfigureOptions <DataProtectionOptions>(options => { options.ApplicationDiscriminator = services.GetApplicationUniqueIdentifier(); }); })); // Read and apply policy from the registry, overriding any other defaults. var encryptorConfigurationReadFromRegistry = false; if (OSVersionUtil.IsWindows()) { foreach (var descriptor in RegistryPolicyResolver.ResolveDefaultPolicy()) { yield return(descriptor); if (descriptor.ServiceType == typeof(IAuthenticatedEncryptorConfiguration)) { encryptorConfigurationReadFromRegistry = true; } } } // Finally, provide a fallback encryptor configuration if one wasn't already specified. if (!encryptorConfigurationReadFromRegistry) { yield return(DataProtectionServiceDescriptors.IAuthenticatedEncryptorConfiguration_FromSettings( new AuthenticatedEncryptionSettings()));; } }
/// <summary> /// Returns a collection of default <see cref="ServiceDescriptor"/> instances that can be /// used to bootstrap the Data Protection system. /// </summary> public static IEnumerable <ServiceDescriptor> GetDefaultServices() { // The default key services are a strange beast. We don't want to return // IXmlEncryptor and IXmlRepository as-is because they almost always have to be // set as a matched pair. Instead, our built-in key manager will use a meta-service // which represents the default pairing (logic based on hosting environment as // demonstrated below), and if the developer explicitly specifies one or the other // we'll not use the fallback at all. yield return(ServiceDescriptor.Singleton <IDefaultKeyServices>(services => { ILogger log = services.GetLogger(typeof(DataProtectionServices)); ServiceDescriptor keyEncryptorDescriptor = null; ServiceDescriptor keyRepositoryDescriptor = null; // If we're running in Azure Web Sites, the key repository goes in the %HOME% directory. var azureWebSitesKeysFolder = FileSystemXmlRepository.GetKeyStorageDirectoryForAzureWebSites(); if (azureWebSitesKeysFolder != null) { if (log.IsInformationLevelEnabled()) { log.LogInformationF($"Azure Web Sites environment detected. Using '{azureWebSitesKeysFolder.FullName}' as key repository; keys will not be encrypted at rest."); } // Cloud DPAPI isn't yet available, so we don't encrypt keys at rest. // This isn't all that different than what Azure Web Sites does today, and we can always add this later. keyRepositoryDescriptor = DataProtectionServiceDescriptors.IXmlRepository_FileSystem(azureWebSitesKeysFolder); } else { // If the user profile is available, store keys in the user profile directory. var localAppDataKeysFolder = FileSystemXmlRepository.DefaultKeyStorageDirectory; if (localAppDataKeysFolder != null) { if (OSVersionUtil.IsWindows()) { // If the user profile is available, we can protect using DPAPI. // Probe to see if protecting to local user is available, and use it as the default if so. keyEncryptorDescriptor = DataProtectionServiceDescriptors.IXmlEncryptor_Dpapi(protectToMachine: !DpapiSecretSerializerHelper.CanProtectToCurrentUserAccount()); } keyRepositoryDescriptor = DataProtectionServiceDescriptors.IXmlRepository_FileSystem(localAppDataKeysFolder); if (log.IsInformationLevelEnabled()) { if (keyEncryptorDescriptor != null) { log.LogInformationF($"User profile is available. Using '{localAppDataKeysFolder.FullName}' as key repository and Windows DPAPI to encrypt keys at rest."); } else { log.LogInformationF($"User profile is available. Using '{localAppDataKeysFolder.FullName}' as key repository; keys will not be encrypted at rest."); } } } else { // Use profile isn't available - can we use the HKLM registry? RegistryKey regKeyStorageKey = null; if (OSVersionUtil.IsWindows()) { regKeyStorageKey = RegistryXmlRepository.DefaultRegistryKey; } if (regKeyStorageKey != null) { // If the user profile isn't available, we can protect using DPAPI (to machine). keyEncryptorDescriptor = DataProtectionServiceDescriptors.IXmlEncryptor_Dpapi(protectToMachine: true); keyRepositoryDescriptor = DataProtectionServiceDescriptors.IXmlRepository_Registry(regKeyStorageKey); if (log.IsInformationLevelEnabled()) { log.LogInformationF($"User profile not available. Using '{regKeyStorageKey.Name}' as key repository and Windows DPAPI to encrypt keys at rest."); } } else { // Final fallback - use an ephemeral repository since we don't know where else to go. // This can only be used for development scenarios. keyRepositoryDescriptor = DataProtectionServiceDescriptors.IXmlRepository_InMemory(); if (log.IsWarningLevelEnabled()) { log.LogWarning("Neither user profile nor HKLM registry available. Using an ephemeral key repository. Protected data will be unavailable when application exits."); } } } } return new DefaultKeyServices( services: services, keyEncryptorDescriptor: keyEncryptorDescriptor, keyRepositoryDescriptor: keyRepositoryDescriptor); })); // Provide root key management and data protection services yield return(DataProtectionServiceDescriptors.IKeyManager_Default()); yield return(DataProtectionServiceDescriptors.IDataProtectionProvider_Default()); // Provide services required for XML encryption #if !DOTNET5_4 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml yield return(DataProtectionServiceDescriptors.ICertificateResolver_Default()); #endif // Hook up the logic which allows populating default options yield return(DataProtectionServiceDescriptors.ConfigureOptions_DataProtectionOptions()); // Read and apply policy from the registry, overriding any other defaults. bool encryptorConfigurationReadFromRegistry = false; if (OSVersionUtil.IsWindows()) { foreach (var descriptor in RegistryPolicyResolver.ResolveDefaultPolicy()) { yield return(descriptor); if (descriptor.ServiceType == typeof(IAuthenticatedEncryptorConfiguration)) { encryptorConfigurationReadFromRegistry = true; } } } // Finally, provide a fallback encryptor configuration if one wasn't already specified. if (!encryptorConfigurationReadFromRegistry) { yield return(DataProtectionServiceDescriptors.IAuthenticatedEncryptorConfiguration_Default()); } }