Add UnprotectKeysWithAnyCertificate

This commit is contained in:
Nate McMaster 2018-02-20 15:34:56 -08:00
parent 3d30ea8249
commit 70dcbf6ed6
No known key found for this signature in database
GPG Key ID: A778D9601BD78810
2 changed files with 67 additions and 6 deletions

View File

@ -320,6 +320,33 @@ namespace Microsoft.AspNetCore.DataProtection
return builder;
}
/// <summary>
/// Configures certificates which can be used to decrypt keys loaded from storage.
/// </summary>
/// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
/// <param name="certificates">Certificates that can be used to decrypt key data.</param>
/// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
public static IDataProtectionBuilder UnprotectKeysWithAnyCertificate(this IDataProtectionBuilder builder, params X509Certificate2[] certificates)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.Services.Configure<XmlKeyDecryptionOptions>(o =>
{
if (certificates != null)
{
foreach (var certificate in certificates)
{
o.AddKeyDecryptionCertificate(certificate);
}
}
});
return builder;
}
/// <summary>
/// Configures keys to be encrypted with Windows DPAPI before being persisted to
/// storage. The encrypted key will only be decryptable by the current Windows user account.

View File

@ -171,16 +171,16 @@ namespace Microsoft.AspNetCore.DataProtection
WithUniqueTempDirectory(directory =>
{
// Step 1: directory should be completely empty
directory.Create();
// Step 1: directory should be completely empty
directory.Create();
Assert.Empty(directory.GetFiles());
// Step 2: instantiate the system and round-trip a payload
var protector = DataProtectionProvider.Create(directory, certificate).CreateProtector("purpose");
// Step 2: instantiate the system and round-trip a payload
var protector = DataProtectionProvider.Create(directory, certificate).CreateProtector("purpose");
Assert.Equal("payload", protector.Unprotect(protector.Protect("payload")));
// Step 3: validate that there's now a single key in the directory and that it's is protected using the certificate
var allFiles = directory.GetFiles();
// Step 3: validate that there's now a single key in the directory and that it's is protected using the certificate
var allFiles = directory.GetFiles();
Assert.Single(allFiles);
Assert.StartsWith("key-", allFiles[0].Name, StringComparison.OrdinalIgnoreCase);
string fileText = File.ReadAllText(allFiles[0].FullName);
@ -189,6 +189,40 @@ namespace Microsoft.AspNetCore.DataProtection
});
}
[Fact]
public void System_CanUnprotectWithCert()
{
var filePath = Path.Combine(GetTestFilesPath(), "TestCert2.pfx");
var certificate = new X509Certificate2(filePath, "password");
WithUniqueTempDirectory(directory =>
{
// Step 1: directory should be completely empty
directory.Create();
Assert.Empty(directory.GetFiles());
// Step 2: instantiate the system and create some data
var protector = DataProtectionProvider
.Create(directory, certificate)
.CreateProtector("purpose");
var data = protector.Protect("payload");
// Step 3: validate that there's now a single key in the directory and that it's is protected using the certificate
var allFiles = directory.GetFiles();
Assert.Single(allFiles);
Assert.StartsWith("key-", allFiles[0].Name, StringComparison.OrdinalIgnoreCase);
string fileText = File.ReadAllText(allFiles[0].FullName);
Assert.DoesNotContain("Warning: the key below is in an unencrypted form.", fileText, StringComparison.Ordinal);
Assert.Contains("X509Certificate", fileText, StringComparison.Ordinal);
// Step 4: setup a second system and validate it can decrypt keys and unprotect data
var unprotector = DataProtectionProvider.Create(directory,
b => b.UnprotectKeysWithAnyCertificate(certificate));
Assert.Equal("payload", unprotector.CreateProtector("purpose").Unprotect(data));
});
}
/// <summary>
/// Runs a test and cleans up the temp directory afterward.
/// </summary>