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

I was recently assisting with a SharePoint 2010 BCS / WCF issue where we wanted the BCS logic to run as the Application Pool Identity. Everything worked fine if they had the External Content Type (ECT) configured to use claims based authentication and the user’s identity. But once they opened the ECT in SharePoint Designer, opened the External System, and unchecked the box to "Use claims based authentication" in the "Connection Properties" for their production server they would then get the error below in the ULS logs each time they tried to access their External List in SharePoint. The same change worked fine for them on two other test systems.

03/16/2012 11:01:57.50    w3wp.exe (0x1548)    0x1858    Business Connectivity Services    Business Data    f6pq    Unexpected

CommunicationException exception executing : System.ServiceModel.Security.SecurityNegotiationException: SOAP security negotiation failed. See inner exception for more details. ---> System.Security.Cryptography.CryptographicException: The system cannot find the file specified.     
at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)   
at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle)   
at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()   
at System.IdentityModel.Tokens.RsaSecurityToken..ctor(RSACryptoServiceProvider rsa, Boolean ownsRsa)   
at System.IdentityModel.Tokens.RsaSecurityToken.CreateSafeRsaSecurityToken(Int32 keySize)   
at System.ServiceModel.Security.Tokens.IssuedSecurityTokenProvider.CoreFederatedTokenProvider.CreateAndCacheRsaSecurityToken()   
at System.ServiceModel.Security.Tokens.IssuedSecurityTokenProvider.CoreFederatedTokenProvider.CreateNegotiationState(EndpointAddress target, Uri via, TimeSpan timeout)   
at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout)     -
-- End of inner exception stack trace ---    Server stack trace:    
at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout)   
at System.ServiceModel.Security.IssuanceTokenProviderBase`1.GetTokenCore(TimeSpan timeout)   
at System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(TimeSpan timeout)   
at System.ServiceModel.Security.Tokens.IssuedSecurityTokenProvider.GetTokenCore(TimeSpan timeout)   
at System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(TimeSpan timeout)   
at System.ServiceModel.Security.SecurityProtocol.TryGetSupportingTokens(SecurityProtocolFactory factory, EndpointAddress target, Uri via, Message message, TimeSpan timeout, Boolean isBlockingCall, IList`1& supportingTokens)   
at System.ServiceModel.Security.TransportSecurityProtocol.SecureOutgoingMessageAtInitiator(Message& message, String actor, TimeSpan timeout)   
at System.ServiceModel.Security.TransportSecurityProtocol.SecureOutgoingMessage(Message& message, TimeSpan timeout)   
at System.ServiceModel.Security.SecurityProtocol.SecureOutgoingMessage(Message& message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState)   
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)   
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)   
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)   
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)   
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)    Exception rethrown
at [0]:    
at Microsoft.SharePoint.BusinessData.SystemSpecific.Wcf.WcfSystemUtility.Execute(Object[] args)

 

I did some debugging and found that a call to lookup the user storage location was setting the error: "The system cannot find the file specified" as a result of not being able to open the profile registry keys for the user.  The code made 3 attempts looking for these 3 keys:

  1. HKLM\Software\Microsoft\Windows NT\CurrentVersion\ProfileList\S-1-5-17
  2. HKLM\Software\Microsoft\Windows NT\CurrentVersion\ProfileList\S-1-5-17.bak
  3. HKCU\S-1-5-17

NOTE: S-1-5-17 is the SID for the NT AUTHORITY\IUSR account.

I had already collected a Process Monitor log so I used it to easily see the 3 registry requests resulting in NAME NOT FOUND:

image

NOTE: Creating a filter in Process Monitor where "Path contains \Windows NT\CurrentVersion\P" will allow you to easily find this.

 

In the working test environments we were looking for and successfully finding the ProfileList registry key for the SID of the user configured as the AppPool identity.

For example:

HKLM\Software\Microsoft\Windows NT\CurrentVersion\ProfileList\S-1-5-21-123456789-123456789-123456789-123456

We would then use information from the registry to build a path to the RSA directory for the user:

"C:\Users\<AppPoolUserID>\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-123456789-123456789-123456789-123456\"

 

So, the issue here was that on the production server we were not actually running as the AppPool Identity, we were running as the IUSR account and there is no Windows profile that exists for the IUSR account.  This brings up the next key question which is why is one server running as IUSR while the other working one is running as the Application Pool Identity?

The answer to this lies in a hotfix/update that is applied on the production server that isn't on the test servers.

----------------------------------------------------------------------------------------------------------------

SharePoint impersonates the IUSR account and is denied access to resources states:

This hotfix makes a new application setting available in ASP.NET 2.0. The new application setting is aspnet:AllowAnonymousImpersonation. You can enable this setting by adding the following section to the Web.config file:

    <appSettings>

        <add key="aspnet:AllowAnonymousImpersonation" value="true" />

    </appSettings>

To enable this setting, you must have IIS 7 or IIS 7.5 running in Integrated mode. When this setting is enabled, the application runs under the security context of the IUSR identity.

----------------------------------------------------------------------------------------------------------------

Article 979917 shows that the new setting of running as IUSR was introduced in System.web.dll version 2.0.50727.5007

Our working test servers had an older build and our failing production server had a newer build.

By setting the value to "false" SharePoint will use the older less restrictive security setting that allows the code to run as the AppPool identity.