Problems using .Net RSACryptoServiceProvider for XML Digital Signatures in a Shared Web Hosting Environment
Written by John Cashimer Sunday, 15 March 2009
If you are a software developer looking for a free and fairly easy way to license your software for trials and purchasing then this article is for you.
A while ago I found this very useful article about digitally signing xml files:
Using XML Digital Signatures for Application Licensing
These code samples are for the .Net environment and therefore require a Windows host to run on. The link above has all the information you need to create your own encryption keys and use them for adding a ‘signature’ to your xml license files. When your application reads the license file on start up to check and see if the ‘expired’ date has passed, for example, you can also be sure that the license file has not been tampered with by verifying its digital signature.
The code worked great on the home development sever, but when the digital signing was moved to the shared server environment, it ran into some obstacles that are apparently by design in the shared server environment: namely the use of encryption is tightly controlled for security reasons.
The first error encountered said the code needed more ‘permission’ to access the ‘Key Container’, (see below, Error 1). Arvixe support graciously accommodated me with the required permission to get passed this first hurdle. But then a second error was encountered, (see below, Error 2). Not being fluent in .Net server admin or encryption, it was by no means obvious what the remedy for this second error was, or even if there was one; and the results Google returned including Microsoft’s own information didn’t shed much light.
The first error seemed reasonable and was quickly resolved by Arvixe support, but the second error, ‘cannot find the file specified’, made less sense as the code was not accessing any files. The ‘file’ being referred to in the error message is apparently the ‘machine key store’ and every attempt to use a private key results in this error; Even using ‘System.Security.Cryptography.RSA.FromXmlString,’ which does not explicitly refer to the machine key store, generates the same error. For the purpose of this article, suffice it to say it’s a restriction related to server security, or at least server efficiency.
The good news is there is available, again at codeproject.com, a simple and easy workaround that requires no extra server permissions and is not dependent on any special server settings. Thanks again to the gurus who provide us great source code samples on the internet, here is the link:
Using RSA Public Key Encryption in a Shared Web Hosting Environment
This link provides source code to completely replace the use of .Net’s System.Security.Cryptography.RSA.FromXmlString, which allows use of cryptography without using the system’s machine key store. (In the code from the first link above, for the part that uses a private key to do the digital signing, replace the use of CspProviderFlags.UseMachineKeyStore with FromXmlString.)
The replacement class, EZRSA, works exactly the same as the .Net version for the FromXmlString method, after you read the comments and make a few tweaks to the source code: i.e. 1.KeySize should return keyLen * 8, 2. inherit from ‘RSA’ rather than ‘AsymmetricAlgorithm’, 3. Add a couple of overridable methods, (see the comments.) In my case I also had to change the default constructor from 512 to 1024 to use the keys I had already been using, e.g. EZRSA csp = new EZRSA(1024);
Somehow the link to this excellent workaround avoided me the first few web searches, maybe it will be a little easier to find now. Thanks again to the all the orignal coders!
[SecurityException: Request for the permission of type 'System.Security.Permissions.KeyContainerPermission, mscorlib, Version=22.214.171.124, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.] System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet) +0 System.Security.CodeAccessPermission.Demand() +58 System.Security.Cryptography.RSACryptoServiceProvider.ImportParameters(RSAParameters parameters) +240 System.Security.Cryptography.RSA.FromXmlString(String xmlString)
The second error from the online version is:
CryptographicException: The system cannot find the file specified. System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer) +7712542 System.Security.Cryptography.RSACryptoServiceProvider.ImportParameters(RSAParameters parameters) +258 System.Security.Cryptography.RSA.FromXmlString(String xmlString) +470 SignXml.SignXml.Page_Load(Object sender, EventArgs e) +417