I'm getting an Access denied error when I attempt to make a web service request. The web service requires an asymmetric security binding. The call is made via a webpage that references a .NET dll that references the web service. All of this works fine on our server but we loaded it up at a web hosting company we got hit with this error.
My theory is that whatever the GetKeyPairHelper
method does it requires the ability to write a file to do it while we don't have access to the location it tries to write to. The support people at this company ran a trace for us and these were the last two lines:
06:03.1 w3wp.exe 5860 CreateFile C:\ProgramData NAME COLLISION >Desired Access: Read Data/List Directory, Synchronize, Disposition: Create, Options: >Directory, Synchronous IO Non-Alert, Open Reparse Point, Attributes: N, ShareMode: Read, >Write, AllocationSize: 0
06:03.1 w3wp.exe 5860 CreateFile C:\ProgramData ACCESS DENIED >Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, >Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a
Looks like something trying to write to C:\ProgramData
but what? And why? And how do I get around it? I'm hoping there is some way to configure this so it doesn't have to write a file. Perhaps this job can be performed in memory or directed to an area we have access to.
Stacktrace:
Error: Access is denied. : Server stack trace: 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.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize) at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey() at System.IdentityModel.Tokens.X509AsymmetricSecurityKey.get_PrivateKey() at System.IdentityModel.Tokens.X509AsymmetricSecurityKey.GetSignatureFormatter(String algorithm) at System.IdentityModel.SignedXml.ComputeSignature(SecurityKey signingKey) at System.ServiceModel.Security.WSSecurityOneDotZeroSendSecurityHeader.CompletePrimarySignatureCore(SendSecurityHeaderElement[] signatureConfirmations, SecurityToken[] signedEndorsingTokens, SecurityToken[] signedTokens, SendSecurityHeaderElement[] basicTokens) at System.ServiceModel.Security.SendSecurityHeader.CompleteSignature() at System.ServiceModel.Security.SendSecurityHeader.CompleteSecurityApplication() at System.ServiceModel.Security.SecurityAppliedMessage.OnWriteMessage(XmlDictionaryWriter writer) at System.ServiceModel.Channels.Message.WriteMessage(XmlDictionaryWriter writer) at System.ServiceModel.Channels.Message.WriteMessage(XmlWriter writer) at CustomMessageEncoder.CustomTextMessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset) in C:\xxxxx\CustomEncoders\CustomTextMessageEncoder.cs:line 86
Code:
public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
{
MemoryStream stream = new MemoryStream();
XmlWriter writer = XmlWriter.Create(stream, this.writerSettings);
message.WriteMessage(writer); //'line 86' in stacktrace.
writer.Close();
byte[] messageBytes = stream.GetBuffer();
int messageLength = (int)stream.Position;
stream.Close();
int totalLength = messageLength + messageOffset;
byte[] totalBytes = bufferManager.TakeBuffer(totalLength);
Array.Copy(messageBytes, 0, totalBytes, messageOffset, messageLength);
//maybe parse message around here...
ArraySegment<byte> byteArray = new ArraySegment<byte>(totalBytes, messageOffset, messageLength);
return byteArray;
}
What happened here is that the hosting company was hosting our site on a shared server and so rightfully denied access to the C:\ProgramData folder which, further down the directory contains C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys which is the folder you need access to in order to use certificates based security. Their solution for us was to allow access to this folder for our app pool only. Works like a charm.