Dotnet core and data protection encryption

Written by Luke Arntz on November 20, 2019
[ dotnet ] [ dependency injection ] [ data protection ] [ encryption ]

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:

  1. Create a project directory, and cd into the directory.
  2. Run dotnet new console to create a new console project.
  3. 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;
        }
    }
}

References

Related Articles

Top