Partager via


DPAPI and SharePoint

DPAPI stands for Data Protection API. It is a pair of function calls that provide OS-level data protection services to user and system processes. https://msdn.microsoft.com/en-us/library/ms995355.aspx. Simply put, if your OS is Windows 2000 or later, you can use DPAPI to encrypt and decrypt data without having to manage (and worry about) encryption keys. DPAPI works on both client and server OS's, with or without an AD domain.

You can use DPAPI with SharePoint to manage secrete data. However, there is a catch: with DPAPI, you can use either machine account or domain account to manage the secrete keys. Since machine account is different from machine to machine, it is not a good idea to use it with a multi-server SharePoint farm because then you would not be able to decrypt data on one server while the data is encrypted on other.

Even with domain account, you need to use code to tell SharePoint that you want to use the application pool account to make the DPAPI calls. Otherwise, SharePoint would use current session’s user account to do the work, so data that one user encrypt would not be able to be decrypted by other users, unless, of cause, you want it be that way.

Here is code snippet to revert a SharePoint session back to use its AppPool account:

object lockForImpersonation = new object();

lock (lockForImpersonation)

{

  WindowsImpersonationContext impersonationCtx = null;

  try

  {

    impersonationCtx = WindowsIdentity.Impersonate(System.IntPtr.Zero);

 

        // Make DPAPI calls…

  }

finally

{

  if (impersonationCtx != null)

    impersonationCtx.Undo();

}

Here is my implementation of extension for using DPAPI with SharePoint for Enterprise Library’s Cryptography Application Block:

        /// <summary>

        /// Encrypts a secret using a specified symmetric cryptography provider.

        /// </summary>

        /// <param name="symmetricInstance">A symmetric instance from configuration.</param>

        /// <param name="plaintext">The input as a base64 encoded string for which you want to encrypt.</param>

        /// <param name="revertToSelf">true for reverting the WindowsIdentity to the original user while while performing cryptography opreations.</param>

        /// <returns>The resulting cipher text as a base64 encoded string.</returns>

        public static string EncryptSymmetric(string symmetricInstance, string plaintext, bool revertToSelf)

        {

            if (revertToSelf)

            {

                string stringValue;

                object lockForImpersonation = new object();

                lock (lockForImpersonation)

       {

                    WindowsImpersonationContext impersonationCtx = null;

                    try

                    {

                        impersonationCtx = WindowsIdentity.Impersonate(System.IntPtr.Zero);

                        stringValue = EncryptSymmetric(symmetricInstance, plaintext);

                    }

                    finally

                    {

                        if (impersonationCtx != null)

                            impersonationCtx.Undo();

                    }

                }

                return stringValue;

            }

            else

            {

                return Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.Cryptographer.EncryptSymmetric(symmetricInstance, plaintext);

            }

        }

        /// <summary>

        /// Decrypts a cipher text using a specified symmetric cryptography provider.

        /// </summary>

        /// <param name="symmetricInstance">A symmetric instance from configuration.</param>

        /// <param name="ciphertextBase64">The cipher text as a base64 encoded string for which you want to decrypt.</param>

        /// <param name="revertToSelf">true for reverting the WindowsIdentity to the original user while while performing cryptography opreations.</param>

        /// <returns>The resulting plain text as a string.</returns>

        public static string DecryptSymmetric(string symmetricInstance, string ciphertextBase64, bool revertToSelf)

        {

       if (revertToSelf)

            {

                string stringValue;

                object lockForImpersonation = new object();

                lock (lockForImpersonation)

                {

                    WindowsImpersonationContext impersonationCtx = null;

                    try

                    {

                        impersonationCtx = WindowsIdentity.Impersonate(System.IntPtr.Zero);

                        stringValue = DecryptSymmetric(symmetricInstance, ciphertextBase64);

                    }

                    finally

                    {

                        if (impersonationCtx != null)

                            impersonationCtx.Undo();

                    }

                    return stringValue;

             }

            }

            else

            {

                return Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.Cryptographer.DecryptSymmetric(symmetricInstance, ciphertextBase64);

            }

        }

Zewei Song, Ph.D.

MCPD, MCTS: .NET 3.5, MOSS AppDev, Configuration

Enterprise Services, Microsoft Corporation

Comments

  • Anonymous
    April 20, 2011
    Hi, I tried your code to encrypt /decrypt the text, everything work fine. Untill i did the IISRESET, it throw System.Security.Cryptography.CryptographicException: Key not valid for use in specified state. when i decrypt the message again. any idea?

  • Anonymous
    April 26, 2011
    Are you using domain account for managing keys? Do you have a multi-server farm? Do they all use the same account as AppPool account?