.NET Core: How to access Windows Credential Manager if running on Windows (otherwise ignore)?

Viewed 2856

So far, to store and retrieve secrets (like credentials) in .NET applications, I successfully used the CredentialManagement package on Windows. Now I'd like to go cross-platform.

So I need to access the Windows Credential Manager from a .NET Core cross-platform application. If it's running on Windows - use the Credential Manager. If it's running on Linux - don't crash (use key chain or whatever, that is the next step).

How would this be done?

(Note: I'm open to alternatives to the Windows Credential Manager but they should provide an equal level of protection.)

2 Answers

I ended up using the Data Protection API for Windows (DPAPI) to store secrets in a file that is encrypted within the scope of the logged in user.

Encrypted storage of a password:

try
{
    var passwordBytes = Encoding.UTF8.GetBytes(password);
    var protectedPasswordBytes = ProtectedData.Protect(passwordBytes, null, DataProtectionScope.CurrentUser);
    var protectedPasswordBytesBase64 = Convert.ToBase64String(protectedPasswordBytes);
    File.WriteAllText(passwordFilePath, protectedPasswordBytesBase64);
} catch (PlatformNotSupportedException)
{
    Debug.WriteLine("Could not store credentials");
}

Decryption:

if (File.Exists(passwordFilePath))
{
    var protectedPasswordBytesBase64 = File.ReadAllText(passwordFilePath);
    var protectedPasswordBytes = Convert.FromBase64String(protectedPasswordBytesBase64);
    var passwordBytes = ProtectedData.Unprotect(protectedPasswordBytes, null, DataProtectionScope.CurrentUser);
    password = Encoding.UTF8.GetString(passwordBytes);
}

(Only works on Windows, for other OSes I'll use other solutions.)

To determine the operating system on which your application is running. This can help, for reference

  1. RuntimeInformation.IsOSPlatform(OSPlatform) Method
  2. OSPlatform.Windows Property

A complete example for windows(CredentialManagement + Detect Operating System),

using CredentialManagement;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace DetectOSCredentialManagement
{
    class Program
    {
        static void Main(string[] args)
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                Console.WriteLine("Hello Beauty!");
                Program.SetCredentials("FOO", "friday", "fr!d@y0", PersistanceType.LocalComputer);
                var userpass = Program.GetCredential("FOO");
                Console.WriteLine($"User: {userpass.Username} Password: {userpass.Password}");
                Program.RemoveCredentials("FOO");
                Debug.Assert(Program.GetCredential("FOO") == null);
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                Console.WriteLine("Hello Cutie!");
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            {
                Console.WriteLine("Too Costly!");
            }
        }

        public static UserPass GetCredential(string target)
        {
            var cm = new Credential { Target = target };
            if (!cm.Load())
            {
                return null;
            }

            // UserPass is just a class with two string properties for user and pass
            return new UserPass(cm.Username, cm.Password);
        }

        public static bool SetCredentials(
             string target, string username, string password, PersistanceType persistenceType)
        {
            return new Credential
            {
                Target = target,
                Username = username,
                Password = password,
                PersistanceType = persistenceType
            }.Save();
        }

        public static bool RemoveCredentials(string target)
        {
            return new Credential { Target = target }.Delete();
        }
    }
    public class UserPass
    {
        public string Username { get; set; }
        public string Password { get; set; }

        public UserPass(string username, string password)
        {
            Username = username;
            Password = password;
        }
    }
}

System.Security.Permissions -- This dll is also need to run above application.

Related