Friday, 13 March 2015

FIM2010: Protect passwords in configuration files

Intro

One of the great features of FIM is that it is relatively easy to plugin custom functionality. You can extend the synchronization engine by developing rules extension and you can add custom workflows to the FIM portal. Rules extensions run under the FIM synchronization service account, workflows under the FIM service service account. This article describes an approach to enable communication to external systems (eg Exchange). Because you typically do not grant a service account rights to Microsoft Exchange, you need the ability to run part of your code using different credentials.

Encrypt password

You do not want to have passwords in clear text in configuration files or source code. That is where encryption comes into play. Encryption can be handled in a myriad of different ways. The method described here uses powershell cmdlets, which keeps it quite simple and understandable. So, how do we convert plain text to something more secure that cannot be read by anyone who happens to have read access to the files? Following two powershell commands are the answer:
$secureString = ConvertTo-SecureString -AsPlainText 
   -Force -String $pwd
$text = ConvertFrom-SecureString $secureString
The cmdlet ConvertTo-SecureString creates a secure string object from the password stored in the variable $pwd. The $text variable is a textual representation of the secure string and looks something like this: 01000000d08c9ddf0115d1118c7a00 c04fc297eb01000000c6c2de88df86 70438e7ac64054c971490000000002 000000000003660000c00000001000 0000103f2b826467c9fdaff555bc06 4b4da50000000004800000a0000000 1000000006673bf0a8cd2463ec9f9f d1a911dfc30800000076d2a3f9c110 2b6e1e0000006452d271a75a5df3a6 00f0f7cb45c18df98d3aae

Decrypt password

PowerShell

To decrypt the password in powershell, use the ConvertTo-SecureString cmdlet:
ConvertTo-SecureString $text
The output of this cmdlet is a secure string object which can be used to build a PSCredential object.

C#

To perform decryption in C# you need to add a reference to System.Management.Automation in your project. To export the dll to the current directory execute following powershell cmd:
Copy ([PSObject].Assembly.Location) .
Following code shows how to use the PowerShell library to construct a PSCredential object. The PSCredential object can then be used to perform management operations on Exchange.
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Security;
 
private string PWD = "01000000d08c9ddf0115d1118c7
a00c04fc297eb01000000c6c2de88df8670438e7ac64054c9
71490000000002000000000003660000c0000000100000001
03f2b826467c9fdaff555bc064b4da50000000004800000a0
0000001000000006673bf0a8cd2463ec9f9fd1a911dfc3080
0000076d2a3f9c1102b6e1e0000006452d271a75a5df3a600
f0f7cb45c18df98d3aae";
private string USER = @"is4u\sa-ms-exch";
 
private PSCredential getPowershellCredential()
{
  PSCredential powershellCredential;
 string powershellUsername = USER;
 SecureString pwd = getPowershellPassword(PWD);
 if (pwd != null)
 {
  powershellCredential = new 
       PSCredential(powershellUsername, pwd);
 }
 else
 {
  throw new Exception("Password is invalid");
 }
  return powershellCredential;
}
 
private SecureString getPowershellPassword(string encryptedPwd)
{
 SecureString pwd = null;
 using (PowerShell powershell = PowerShell.Create())
 {
  powershell.AddCommand("ConvertTo-SecureString");
  powershell.AddParameter("String", encryptedPwd);
  Collection<PSObject> results = powershell.Invoke();
  if (results.Count > 0)
  {
   PSObject result = results[0];
   pwd = (SecureString)result.BaseObject;
  }
 }
 return pwd;
}

NetworkCredential

If your operation requires you to connect using a network credential instead of a PSCredential object, this is very easy. You can get the corresponding NetworkCredential object from the PSCredential.
using System.Net;
 
private NetworkCredential getNetworkCredential()
{
  NetworkCredential cred = getPowershellCredential().GetNetworkCredential();
  return cred;
}

Security aspects

Only the user account that encrypted the password can decrypt it (because Kerberos keys are used under the hood). This is illustrated in following screenshot. If you look carefully, you can check I did not cheat on the parameter. secureString

Conclusion

The approach described here is simple, quick and secure. You need to run a few PowerShell commands and you can store passwords securely in your configuration files. Make sure to encrypt the required password using the service account that will be performing the decryption. Note that this technique is not limited to use in FIM deployments. You can use this technique in any .Net/Windows context.

References

Scripting Guy - Decrypt PowerShell Secure String Password