tags:
Contents
Data Protection and Dependency Injection
Dependency injection is the process of passing an object to another object that depends on the first object. In most examples using data protection you’ll find that they use dependency injection. I am using dotnet core to automate virtual machine deployments and was looking for a safe way to store credentials. PowerShell provides the Import-CliXml/Export-CliXml
cmdlets that can be used for this purpose, and here is how I recreated that functionality for use in my c# programs.
This is also a nice way to keep thing simple while testing and learning about data protection in AspNetCore.
Project Setup
When you’re a beginner project setup can often be the trickiest part to getting started. For the purposes of this article we are going to be using the Microsoft.AspNetCore.DataProtection.Extensions
package, because this “provides a concrete type, DataProtectionProvider, which offers a simple way to use Data Protection without relying on DI”.
For this example we need to create a new console application and add the Microsoft.AspNetCore.DataProtection.Extensions
package.
Steps:
- Create a project directory, and
cd
into the directory. - Run
dotnet new console
to create a new console project. - Run
dotnet add package Microsoft.AspNetCore.DataProtection.Extensions
to add the package to the project.
Once complete your csproj file should look like the one below.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Extensions" Version="2.2.0" />
</ItemGroup>
</Project>
The project should run and print Hello, World
to the console. If not, you’ve done something has gone tremendously wrong.
Full Source Code
CredentialManager.cs
using System;
using System.IO;
using Microsoft.AspNetCore.DataProtection;
namespace api_credentialmanager
{
class CredentialManager
{
private readonly string destFolder = Path.Combine(
System.Environment.GetEnvironmentVariable("LOCALAPPDATA"),
"api_secrets");
private IDataProtectionProvider _provider;
private IDataProtector _protector;
public CredentialManager()
{
try
{
_provider = DataProtectionProvider.Create(new DirectoryInfo(destFolder),
configuration =>
{
configuration.ProtectKeysWithDpapi();
}
);
_protector = _provider.CreateProtector("api");
}
catch (Exception e)
{
Console.WriteLine("Exception Occurred: {0}", e.Message);
Console.WriteLine(e.StackTrace);
}
}
public bool SaveSecret(string secret, string name)
{
try
{
File.WriteAllText(destFolder + "\\" + name, _protector.Protect(secret));
}
catch (Exception e)
{
Console.WriteLine("Exception Occurred: {0}", e.Message);
Console.WriteLine(e.StackTrace);
return false;
}
return true;
}
public string GetSecret(string name)
{
try
{
return _protector.Unprotect(File.ReadAllText(destFolder + "\\" + name));
}
catch (Exception e)
{
Console.WriteLine("Exception Occurred: {0}", e.Message);
Console.WriteLine(e.StackTrace);
return null;
}
}
}
}
Program.cs
using System;
namespace api_credentialmanager
{
class Program
{
static void Main(string[] args)
{
string name = null;
if (args.Length == 0)
{
Console.WriteLine("No credential name specified.");
Environment.Exit(-1);
}
else
{ name = args[0]; }
Console.WriteLine("\nEnter {0} credentials.", name);
Console.Write("User: ");
string user = Console.ReadLine();
Console.Write("Password for {0}: ", user);
string password = GetPassword();
CredentialManager credentialManager = new CredentialManager();
credentialManager.SaveSecret(user + ":" + password, name);
}
static string GetPassword() // https://stackoverflow.com/questions/3404421/password-masking-console-application
{
string pw = null;
ConsoleKeyInfo keyInfo;
do
{
keyInfo = Console.ReadKey(true);
// Skip if Backspace or Enter is Pressed
if (keyInfo.Key != ConsoleKey.Backspace && keyInfo.Key != ConsoleKey.Enter)
{
pw += keyInfo.KeyChar;
Console.Write("*");
}
else
{
if (keyInfo.Key == ConsoleKey.Backspace && pw.Length > 0)
{
// Remove last charcter if Backspace is Pressed
pw = pw.Substring(0, (pw.Length - 1));
Console.Write("\b \b");
}
}
}
// Stops Getting Password Once Enter is Pressed
while (keyInfo.Key != ConsoleKey.Enter);
return pw;
}
}
}