Friday, September 28, 2007

Certificate Access Error in a IIS hosted WCF Service

The problem appears when a WCF Service hosted in an IIS tries to load a certificate from the Windows Certificates Store with the account of the Application Pool where the service runs, and the account’s profile is not previously loaded. When a user logs on interactively, the system automatically loads the user's profile. If a service or an application impersonates a user, the system does not load the user's profile. Therefore, the service or application should load the user's profile with LoadUserProfile.

When this happens the operation throws the following exception:

System.Security.Cryptography.CryptographicException: The system cannot find the file specified.

See Microsoft Knowledge Base Article for detailed information.

A workaround to this problem is to load the Application Pool Identity Account´s profile before the service call is executed. Placing the code in the Application_Start() method on the Global.asax of the IIS host will solve the problem (see for detailed information).

Here is the code:

        private ProfileManager.PROFILEINFO profile;


        protected void Application_Start(object sender, EventArgs e)


            bool retVal = false;

            // Need to duplicate the token. LoadUserProfile needs a token with


            const int SecurityImpersonation = 2;

            dupeTokenHandle = DupeToken(WindowsIdentity.GetCurrent().Token, SecurityImpersonation);

            if (IntPtr.Zero == dupeTokenHandle)


                throw new Exception("Unable to duplicate token.");



            // Load the profile.

            profile = new ProfileManager.PROFILEINFO();

            profile.dwSize = 32;


            profile.lpUserName = @"MyDomain\UserName";

            retVal = ProfileManager.LoadUserProfile(dupeTokenHandle, ref profile);


            if (!retVal)


                throw new Exception("Error loading user profile. " + Marshal.GetLastWin32Error());




        protected void Application_End(object sender, EventArgs e)


            ProfileManager.UnloadUserProfile(WindowsIdentity.GetCurrent().Token, profile.hProfile);




        private IntPtr DupeToken(IntPtr token, int Level)


            IntPtr dupeTokenHandle = new IntPtr(0);

            bool retVal = DuplicateToken(token, Level, ref dupeTokenHandle);

            if (false == retVal)


                return IntPtr.Zero;


            return dupeTokenHandle;





    internal class ProfileManager


        [DllImport("Userenv.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]

        internal static extern bool LoadUserProfile(IntPtr hToken, ref PROFILEINFO lpProfileInfo);

        [DllImport("Userenv.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]

        internal static extern bool UnloadUserProfile(IntPtr hToken, IntPtr hProfile);


        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]

        public struct PROFILEINFO


            public int dwSize;

            public int dwFlags;

            public String lpUserName;

            public String lpProfilePath;

            public String lpDefaultPath;

            public String lpServerName;

            public String lpPolicyPath;

            public IntPtr hProfile;






An alternative workaround consists in creating a Windows Service account that loads at system start-up using the Application Pool Service Identity.

Thanks to JavierA for helping me to find the solution.

Wednesday, September 26, 2007

The beginning

Hi, my name is Mariano, I'm from Argentina and I have 25 years.
I have programmed computers since I was 15 years old and I really enjoy it.
I'm in the computer business since year 2000 and now I'm working as developer for Lagash Systems S.A. mainly with Microsoft technology.

This is my first blog, so I hope you find it interesting!! (I will try to keep it updated!!)