Encrypting Credentials in a Wpf Application

Encrypting credentials in a WPF application

Here's a summary of my blog post: How to store a password on Windows?

You can use the Data Protection API and its .NET implementation (ProtectedData) to encrypt the password. Here's an example:

public static string Protect(string str)
{
byte[] entropy = Encoding.ASCII.GetBytes(Assembly.GetExecutingAssembly().FullName);
byte[] data = Encoding.ASCII.GetBytes(str);
string protectedData = Convert.ToBase64String(ProtectedData.Protect(data, entropy, DataProtectionScope.CurrentUser));
return protectedData;
}

public static string Unprotect(string str)
{
byte[] protectedData = Convert.FromBase64String(str);
byte[] entropy = Encoding.ASCII.GetBytes(Assembly.GetExecutingAssembly().FullName);
string data = Encoding.ASCII.GetString(ProtectedData.Unprotect(protectedData, entropy, DataProtectionScope.CurrentUser));
return data;
}

Or you can use the Windows Credential Manager (This is the way I prefer because it allows users to backup/restore/edit their credentials even if your application has no such functionality). I've created a NuGet package Meziantou.Framework.Win32.CredentialManager. How to use it:

CredentialManager.WriteCredential("ApplicationName", "username", "Pa$$w0rd", CredentialPersistence.Session);

var cred = CredentialManager.ReadCredential("ApplicationName");
Assert.AreEqual("username", cred.UserName);
Assert.AreEqual("Pa$$w0rd", cred.Password);

CredentialManager.DeleteCredential("ApplicationName");

Original answer with the native API wrapper (A more recent version of this is available on GitHub):

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.Text;
using System.ComponentModel;

public static class CredentialManager
{
public static Credential ReadCredential(string applicationName)
{
IntPtr nCredPtr;
bool read = CredRead(applicationName, CredentialType.Generic, 0, out nCredPtr);
if (read)
{
using (CriticalCredentialHandle critCred = new CriticalCredentialHandle(nCredPtr))
{
CREDENTIAL cred = critCred.GetCredential();
return ReadCredential(cred);
}
}

return null;
}

private static Credential ReadCredential(CREDENTIAL credential)
{
string applicationName = Marshal.PtrToStringUni(credential.TargetName);
string userName = Marshal.PtrToStringUni(credential.UserName);
string secret = null;
if (credential.CredentialBlob != IntPtr.Zero)
{
secret = Marshal.PtrToStringUni(credential.CredentialBlob, (int)credential.CredentialBlobSize / 2);
}

return new Credential(credential.Type, applicationName, userName, secret);
}

public static int WriteCredential(string applicationName, string userName, string secret)
{
byte[] byteArray = Encoding.Unicode.GetBytes(secret);
if (byteArray.Length > 512)
throw new ArgumentOutOfRangeException("secret", "The secret message has exceeded 512 bytes.");

CREDENTIAL credential = new CREDENTIAL();
credential.AttributeCount = 0;
credential.Attributes = IntPtr.Zero;
credential.Comment = IntPtr.Zero;
credential.TargetAlias = IntPtr.Zero;
credential.Type = CredentialType.Generic;
credential.Persist = (UInt32)CredentialPersistence.Session;
credential.CredentialBlobSize = (UInt32)Encoding.Unicode.GetBytes(secret).Length;
credential.TargetName = Marshal.StringToCoTaskMemUni(applicationName);
credential.CredentialBlob = Marshal.StringToCoTaskMemUni(secret);
credential.UserName = Marshal.StringToCoTaskMemUni(userName ?? Environment.UserName);

bool written = CredWrite(ref credential, 0);
int lastError = Marshal.GetLastWin32Error();

Marshal.FreeCoTaskMem(credential.TargetName);
Marshal.FreeCoTaskMem(credential.CredentialBlob);
Marshal.FreeCoTaskMem(credential.UserName);

if (written)
return 0;

throw new Exception(string.Format("CredWrite failed with the error code {0}.", lastError));
}

public static IReadOnlyList<Credential> EnumerateCrendentials()
{
List<Credential> result = new List<Credential>();

int count;
IntPtr pCredentials;
bool ret = CredEnumerate(null, 0, out count, out pCredentials);
if (ret)
{
for (int n = 0; n < count; n++)
{
IntPtr credential = Marshal.ReadIntPtr(pCredentials, n * Marshal.SizeOf(typeof(IntPtr)));
result.Add(ReadCredential((CREDENTIAL)Marshal.PtrToStructure(credential, typeof(CREDENTIAL))));
}
}
else
{
int lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(lastError);
}

return result;
}

[DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool CredRead(string target, CredentialType type, int reservedFlag, out IntPtr credentialPtr);

[DllImport("Advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool CredWrite([In] ref CREDENTIAL userCredential, [In] UInt32 flags);

[DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool CredEnumerate(string filter, int flag, out int count, out IntPtr pCredentials);

[DllImport("Advapi32.dll", EntryPoint = "CredFree", SetLastError = true)]
static extern bool CredFree([In] IntPtr cred);

private enum CredentialPersistence : uint
{
Session = 1,
LocalMachine,
Enterprise
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct CREDENTIAL
{
public UInt32 Flags;
public CredentialType Type;
public IntPtr TargetName;
public IntPtr Comment;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
public UInt32 CredentialBlobSize;
public IntPtr CredentialBlob;
public UInt32 Persist;
public UInt32 AttributeCount;
public IntPtr Attributes;
public IntPtr TargetAlias;
public IntPtr UserName;
}

sealed class CriticalCredentialHandle : CriticalHandleZeroOrMinusOneIsInvalid
{
public CriticalCredentialHandle(IntPtr preexistingHandle)
{
SetHandle(preexistingHandle);
}

public CREDENTIAL GetCredential()
{
if (!IsInvalid)
{
CREDENTIAL credential = (CREDENTIAL)Marshal.PtrToStructure(handle, typeof(CREDENTIAL));
return credential;
}

throw new InvalidOperationException("Invalid CriticalHandle!");
}

protected override bool ReleaseHandle()
{
if (!IsInvalid)
{
CredFree(handle);
SetHandleAsInvalid();
return true;
}

return false;
}
}
}

public enum CredentialType
{
Generic = 1,
DomainPassword,
DomainCertificate,
DomainVisiblePassword,
GenericCertificate,
DomainExtended,
Maximum,
MaximumEx = Maximum + 1000,
}

public class Credential
{
private readonly string _applicationName;
private readonly string _userName;
private readonly string _password;
private readonly CredentialType _credentialType;

public CredentialType CredentialType
{
get { return _credentialType; }
}

public string ApplicationName
{
get { return _applicationName; }
}

public string UserName
{
get { return _userName; }
}

public string Password
{
get { return _password; }
}

public Credential(CredentialType credentialType, string applicationName, string userName, string password)
{
_applicationName = applicationName;
_userName = userName;
_password = password;
_credentialType = credentialType;
}

public override string ToString()
{
return string.Format("CredentialType: {0}, ApplicationName: {1}, UserName: {2}, Password: {3}", CredentialType, ApplicationName, UserName, Password);
}
}

Usage:

WriteCredential("ApplicationName", "Meziantou", "Passw0rd");
Console.WriteLine(ReadCredential("Demo"));

C# WPF Encryption

Do not try to create your own encryption algorithm rather use the cryptography classes provided in the .NET Framework through System.Security.Cryptography.

For passwords a good solution is to use a oneway encryption like a MD5 hash or SHA1. And when the user enters his/her password you compute the hash and compare it to the stored hash. The advantage of this is that you do not need to worry about how to securely store the key used to encrypt the passwords.

To increase the security of using a one way hash you can apply a salt, this help restrict the effectiveness of certain types of attackes like a dictionary attack etc. I have not read the wiki entry, but I am sure this will provide more detail.

Storing credentials in a c# application

You can't both give the user the password and prevent them from having the password. But every computing problem except for one, can be solved by adding an extra layer of indirection. Create something that doesn't run on the users machine, that acts as a middleman between the file share and the user. I would suggest using the WebDAV protocol.

How to securely save username/password (local)?

If you are just going to verify/validate the entered user name and password, use the Rfc2898DerivedBytes class (also known as Password Based Key Derivation Function 2 or PBKDF2). This is more secure than using encryption like Triple DES or AES because there is no practical way to go from the result of RFC2898DerivedBytes back to the password. You can only go from a password to the result. See Is it ok to use SHA1 hash of password as a salt when deriving encryption key and IV from password string? for an example and discussion for .Net or String encrypt / decrypt with password c# Metro Style for WinRT/Metro.

If you are storing the password for reuse, such as supplying it to a third party, use the Windows Data Protection API (DPAPI). This uses operating system generated and protected keys and the Triple DES encryption algorithm to encrypt and decrypt information. This means your application does not have to worry about generating and protecting the encryption keys, a major concern when using cryptography.

In C#, use the System.Security.Cryptography.ProtectedData class. For example, to encrypt a piece of data, use ProtectedData.Protect():

// Data to protect. Convert a string to a byte[] using Encoding.UTF8.GetBytes().
byte[] plaintext;

// Generate additional entropy (will be used as the Initialization vector)
byte[] entropy = new byte[20];
using(RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(entropy);
}

byte[] ciphertext = ProtectedData.Protect(plaintext, entropy,
DataProtectionScope.CurrentUser);

Store the entropy and ciphertext securely, such as in a file or registry key with permissions set so only the current user can read it. To get access to the original data, use ProtectedData.Unprotect():

byte[] plaintext= ProtectedData.Unprotect(ciphertext, entropy,
DataProtectionScope.CurrentUser);

Note that there are additional security considerations. For example, avoid storing secrets like passwords as a string. Strings are immutable, being they cannot be notified in memory so someone looking at the application's memory or a memory dump may see the password. Use SecureString or a byte[] instead and remember to dispose or zero them as soon as the password is no longer needed.

How to store FTP credentials securely in C# application?

You cannot allow a user access to a program that itself knows credentials which you don't want the user to know. The user will always be able to eventually extract the credentials from the program. You may be able to slow them down, but the usual copy-protection calculus applies:

  • If the information is valuable enough for you to think you need to protect it, it is valuable enough for someone else to bypass your protection.
  • If you invest enough effort into protection to ensure it is not worthwhile to someone else to bypass your protection, then your investment itself exceeded the value of the information (i.e. you spent too much).

Depending on your relationship to the user and the computer they are using, you have some alternatives:

  • Give them the credentials, to be entered in the program configuration after it's installed. Do this only if you trust them of course.
  • Implement the FTP connection code as a Windows service. Give the credentials to someone else who will install the program, which will in turn install the service, configured with the credentials. The user will have access only to a client program that will communicate with the service, which in turn will handle the FTP communications on behalf of the user. Again, do this only if you trust the person installing the program, and the untrusted user does not have admin rights on the machine.
  • Issue per-user credentials. Frankly, this is IMHO the best approach. Advantages include:
    • The user doesn't know anything that would affect the security of anyone else's resources,
    • You don't have to try to hide anything from the user,
    • You can monitor access to the FTP resource on a per-user basis, and
    • You can revoke the credentials for a specific user without affecting your other users.

How can I encrypt user settings (such as passwords) in my application?

David, I thought your answer was nifty, but I thought those would be niftier as extension methods. That would allow such syntax as:

string cypherText;
string clearText;

using (var secureString = "Some string to encrypt".ToSecureString())
{
cypherText = secureString.EncryptString();
}

using (var secureString = cypherText.DecryptString())
{
clearText = secureString.ToInsecureString();
}

Here's the updated code:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Text;

public static class SecureIt
{
private static readonly byte[] entropy = Encoding.Unicode.GetBytes("Salt Is Not A Password");

public static string EncryptString(this SecureString input)
{
if (input == null)
{
return null;
}

var encryptedData = ProtectedData.Protect(
Encoding.Unicode.GetBytes(input.ToInsecureString()),
entropy,
DataProtectionScope.CurrentUser);

return Convert.ToBase64String(encryptedData);
}

public static SecureString DecryptString(this string encryptedData)
{
if (encryptedData == null)
{
return null;
}

try
{
var decryptedData = ProtectedData.Unprotect(
Convert.FromBase64String(encryptedData),
entropy,
DataProtectionScope.CurrentUser);

return Encoding.Unicode.GetString(decryptedData).ToSecureString();
}
catch
{
return new SecureString();
}
}

public static SecureString ToSecureString(this IEnumerable<char> input)
{
if (input == null)
{
return null;
}

var secure = new SecureString();

foreach (var c in input)
{
secure.AppendChar(c);
}

secure.MakeReadOnly();
return secure;
}

public static string ToInsecureString(this SecureString input)
{
if (input == null)
{
return null;
}

var ptr = Marshal.SecureStringToBSTR(input);

try
{
return Marshal.PtrToStringBSTR(ptr);
}
finally
{
Marshal.ZeroFreeBSTR(ptr);
}
}
}

Least worst option to secure database connection string for distributed wpf application to users with local admin rights

You are right: you cannot prevent a determined local admin to get access to this database.

Here's what I -- as a determined user -- would do (I have done it in the past with other programs): I would attach a debugger to the running process and look at the memory to find the plain connection string (of course I am simplifying -- but it works).

Another option would be to use the debugger to hijack your IDbCommand objects to execute arbitrary SQL code, no need for me to find the connection credentials, your application manages the connection for me.

So it really comes down to: how competent and determined are your users? how important is your database? how determined are you?
It's the classical hacker trade-off: how much effort do you want to put into this to discourage your users from cracking your app, knowing that in the end it's always technically possible to crack it?

The bare minimum I would do:

  1. Encrypt the raw connection string. You have covered this part.

  2. Maybe do the encryption in code rather than rely on the built-in app.config section encryption. The problem with the built-in one is that it's self-documenting. People with .net knowledge will know just by reading it how it's encrypted. The security is provided by securing the keys, but they can't really be hidden from a local administrator. There's even a command-line tool to decrypt the section (if you pass the correct keys)! Performing the decryption in code makes it less obvious which algorithm you're using and needs more work to actually decrypt it.
    NOTE: you should still use a strong cryptography provider such as those you've mentionned. I am not saying that you should invent your own cryptographic cipher, rather than you may hide the cipher you use in the program itself rather than the config file.

  3. Obfuscate the compiled assembly. Otherwise it's just to easy to open it up in a tool such as IlSpy, and even modify it (e.g. to dump the plain connection string).

  4. Add some debugger traps to prevent attaching a debugger to your process.

Note that both 3. and 4. would be more efficient with a mixed mode assembly (one that contains native code as well as managed code).

This is a start. Maybe other people will have more ideas.


Of course this was just the application side. The embedded database should be appropriately protected as well. It should be encrypted to start with.



Related Topics



Leave a reply



Submit