If you create an instance of the .NET
RSACryptoServiceProvider class or the .NET
DSACryptoServiceProvider class either directly or indirectly through the
SignedXml class, you may receive the following
CryptographicException class exception:
System.Security.Cryptography.CryptographicException:
CryptoAPI
cryptographic service provider (CSP) for
this implementation could not be
acquired.
at System.Security.Cryptography.RSACryptoServiceProvider
..ctor(Int32 dwKeySize, CspParameters parameters,
Boolean
useDefaultKeySize)
at
System.Security.Cryptography.RSACryptoServiceProvider
..ctor(CspParameters
parameters)
You may receive this
CryptographicException error message when you run the .NET code in a Web service, in a COM+
component, or in an Active Server Pages (ASP) page.
Underlying
base,
enhanced, or
strong cryptographic service provider (CSP) implementations create a key
container for storing an RSA public/private asymmetric key pair. Key containers
are stored in user profiles. For performance reasons, the user profile is not
loaded by the system under a scenario where the .NET code runs in a Web
service, ASP page, or COM+. If the user profile is not loaded, a key container
cannot be opened or created. Because a key container is required for
RSACryptoServiceProvider or
DSACryptoServiceProvider, the .NET code (when run from a Web Service, ASP Page, or COM+)
fails by throwing the exception described in the "Symptoms" section of this
article. If the same .NET code is run from the context of the interactive
logged-on user, where the user profile is loaded by Winlogon, a key container
can be created and opened, and the code works under this scenario.
.NET Framework 1.1 and later versions
If the .NET Framework-based application is intended for use together with the .NET Framework 1.1 or a later version, you can use the feature that is documented at the following Microsoft Developer Network Web site to instruct
RSACryptoServiceProvider to use the
machine key store:
In this method, you use the static property as follows:
RSACryptoServiceProvider.UseMachineKeyStore = true;
The
UseMachineKeyStore static property applies to all code in the current application domain.
.NET Framework 1.0
You must instruct
RSACryptoServiceProvider or
DSACryptoServiceProvider to use
machine key store (as in the following sample code) in scenarios such as
a Web service, ASP Page, or COM+, where the user profile is not loaded by the
system for performance reasons. You can use the
CspParameters parameter in the
RSACryptoServiceProvider() constructor, as follows.
CspParameters CSPParam = new CspParameters();
CSPParam.Flags = CspProviderFlags.UseMachineKeyStore;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(CSPParam);
The following C# sample code demonstrates how to use the
machine key store when
RSACryptoServiceProvider is used indirectly by the
SignedXml class while signing an XML file or verifying a signed XML file.
This sample code assumes that the XML file was signed by using RSA keys. The
sample code signs an input XML file. The signed file is stored in
EnvelopingSig.xml, which is then verified.
using System;
using System.Xml;
using System.IO;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
namespace XmlSample
{
public class XmlDigitalSigning
{
public enum SigType
{
Detached = 1,
Enveloping = 2,
Enveloped = 3
}
public SignedXml Sign(String Uri, RSACryptoServiceProvider rsa, SigType type)
{
// Create the SignedXml message.
SignedXml xmlSign = new SignedXml();
// Set the RSA key for signing.
xmlSign.SigningKey = rsa;
// Create a Reference to point to the to-be-signed content.
Reference reference = new Reference();
if (type == SigType.Detached)
{
reference.Uri = Uri;
}
else if (type == SigType.Enveloping)
{
XmlDocument doc = new XmlDocument();
DataObject obj = new DataObject();
doc.Load(Uri);
obj.Data = doc.ChildNodes;
obj.Id = "MyObjectId";
reference.Uri = "#" + "MyObjectId";
xmlSign.AddObject(obj);
}
// Add the Reference to the SignedXml message.
xmlSign.AddReference(reference);
// Add a KeyInfo element to the SignedXml message.
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new RSAKeyValue((RSA)rsa));
xmlSign.KeyInfo = keyInfo;
// Compute the XML Digital Signature.
xmlSign.ComputeSignature();
return xmlSign;
}
public bool Verify(String signedxmlFile)
{
bool bResult = false;
FileStream inputStream = new FileStream(signedxmlFile, FileMode.Open);
// Load the XML.
XmlDocument doc = new XmlDocument();
XmlTextReader xtr = new XmlTextReader(inputStream);
doc.Load(xtr);
// Get RSAKeyValue Element from the signed XML.
SignedXml signedXml = new SignedXml();
XmlNodeList rsaKeyValueNodes = doc.GetElementsByTagName("RSAKeyValue");
XmlElement rsaKeyValueElement = (XmlElement) rsaKeyValueNodes.Item(0);
String rsaKeyValueString = rsaKeyValueElement.OuterXml;
// Remove the KeyInfo node from the SignedXml signature.
XmlNodeList KeyInfoNode = doc.GetElementsByTagName("KeyInfo");
XmlElement keyInfoElement = (XmlElement) KeyInfoNode.Item(0);
XmlElement signatureElement = (XmlElement) keyInfoElement.ParentNode;
// Console.WriteLine("****");
// Console.WriteLine(keyInfoElement.OuterXml);
// Console.WriteLine("****");
// Console.WriteLine(signatureElement.OuterXml);
// Console.WriteLine("****");
signatureElement.RemoveChild(keyInfoElement);
// Console.WriteLine(doc.DocumentElement.OuterXml);
// Console.WriteLine("****");
// Use machine key store.
CspParameters cspParams = new CspParameters();
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
rsaKey.FromXmlString(rsaKeyValueString);
// Set the keyInfo.
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new RSAKeyValue(rsaKey));
signedXml.KeyInfo = keyInfo;
// Load the signature.
signedXml.LoadXml(doc.DocumentElement);
AsymmetricAlgorithm key;
if (signedXml.CheckSignatureReturningKey(out key))
{
bResult = true;
}
else
{
bResult = false;
}
return bResult;
}
}
class XmlSampleCode
{
static void Main(string[] args)
{
int nArgs;
// args[1] - XML file to be signed
nArgs = args.Length;
if (nArgs < 1)
{
Environment.Exit(-1);
}
for (int i = 0; i < nArgs; i++)
{
Console.WriteLine("Args[{0}] = {1}", i, args[i]);
}
try
{
CspParameters cspParams = new CspParameters();
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cspParams);
XmlDocument xmlOutput = new XmlDocument();
XmlDigitalSigning digSign = new XmlDigitalSigning();
SignedXml xmlSign;
xmlSign = digSign.Sign(args[0], rsa, XmlDigitalSigning.SigType.Enveloping);
XmlTextWriter xmltw = new XmlTextWriter("EnvelopingSig.xml", new UTF8Encoding(false));
xmltw.Formatting = Formatting.Indented;
xmlOutput.InnerXml = xmlSign.GetXml().OuterXml;
xmlOutput.WriteTo(xmltw);
xmltw.Flush();
xmltw.Close();
if (digSign.Verify("EnvelopingSig.xml"))
{
Console.WriteLine("Signature has been verified successfully");
}
else
{
Console.WriteLine("Signature verification failed");
}
}
catch (Exception e)
{
Console.WriteLine("\nException Information : \n\n{0}", e.ToString());
}
return;
}
}
}
This
behavior is by design.
The exception described in the "Symptoms" section of this
article also occurs if the application specifies the name of a
machine key store in
CspParameters, and the calling security context does not have the permissions
to open it.
If the caller is running under the SYSTEM security
context, CSP implementations automatically redirect to the
machine key container.