Originally posted at: FIM2010: Protect passwords in configuration files @ IS4U Blog

 


Intro

One of the great features of FIM is that it is relatively easy to plug in custom functionality. You can extend the synchronization engine by developing rules extensions and add custom workflows to the FIM portal. Rules extensions run under the FIM synchronization service account, workflows under the FIM service account. This article describes an approach to enable communication with external systems (eg Exchange).  Since you would rarely grant a service account rights to Microsoft Exchange, you need 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.

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?
The 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: 01000000d08c9ddf0115d1118c7a00c04fc297eb01000000c6c2de88df8670438e7ac64054c971490000000002000000000003660000c000000010000000103f2b826467c9fdaff555bc064b4da50000000004800000a00000001000000006673bf0a8cd2463ec9f9fd1a911dfc30800000076d2a3f9c1102b6e1e0000006452d271a75a5df3a600f0f7cb45c18df98d3aae

 


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) .

 

The following code uses 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 = "01000000d08c9ddf0115d1118c7a00c04fc297eb01000000c6c2de88df8670438e7ac64054c971490000000002000000000003660000c000000010000000103f2b826467c9fdaff555bc064b4da50000000004800000a00000001000000006673bf0a8cd2463ec9f9fd1a911dfc30800000076d2a3f9c1102b6e1e0000006452d271a75a5df3a600f0f7cb45c18df98d3aae";
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 the 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.  Just 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