Using Amazon S3 and Cloudfront to distribute your private files Part 2 - Shared hosting
So, you want to use the solution I posted Here in your shared hosting environment? You're out of luck and you will start seeing this error below pop up every time you try to sign your url's (mostly... unless your with a shared hosting provider that likes to bend the rules and will change you to full trust in your IIS settings) System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed. Why does this happen? Shared hosting providers (rightfully so) block IIS access from machine keys... and when the RSA .net library attempts to go about its business, it causes a security exception. How do I fix this? In my searching... there seems to be no workaround at all using the provided .net RSA library (unless you can afford a VPS and can give your IIS app full trust access... ) Except for this library, which is a compiled code replacement for the RSACryptoServiceProvider
Service and works perfectly for what we are after: http://www.codeproject.com/KB/security/EZRSA.aspx I effectively got the Amazon Cloudfront URL signing working using this library, and no longer get the security exception in my live environment. Enough rambling, gimme the code!
Step 1: convert your PEM file to an XML string.
I just setup a test page, called the GetPreSignedURLWithPEMFile function and put my break line inside the GetPreSignedURLWithPEMKey function after JavaScience.opensslkey.DecodePEMKey(keyPEM); is called. This is your pem file converted to an xml string. Copy that xml and create a new string at the top of your SignedURL class (well really you can save it anywhere i just did it there so its 1 place to update if i need to change it).
Step 2: Download and reference the EZRSA library.
http://www.codeproject.com/KB/security/EZRSA/ezrsa_demo.zip
Step 3:Call the (edited) GetPreSignedURLWithXMLKey Function instead.
New edited GetPreSignedURLWithXMLKey function that uses the EZRSA class:
private const string xmlKey ="<RSAKeyValue>" +" <Modulus>eChle22XFSVa5LZcZrasdfasdfkFkVO+dDryMZyYB+xACFbq0/ZD2uByLQAKw==</Modulus>" +" <Exponent>fffffAQAB</Exponent>" +" <P>/l4Qiqfffve4fWyyWPpCn1BPlMkE3Qy02ieVuCrznQmfZM=</P>" +" <Q>62DmmOSvPdde9PsmEweo1R8IdB8c8d0B58f5boICyt0gk=</Q>" +" <DP>1fiYn53uUlOVPrWtviYZMO1NRpQTgSTbNSevPm8URcM=</DP>" +" <DQ>Ene54AkhTssS2BhLmENeBtFOIcwaDGk8qCYC3mb6nrLE=</DQ>" +" <InverseQ>ZKMeZfYfDw2pVD3bKKf3GMtPMMJyBm4i7pBNwg9wU2HY=</InverseQ>" +" <D>jdTsKUA/lz60XggshvlbWU87G/LsEwbU2kV6eAOLxyy5i/CsDw4pCUCku8SfvvEumDyVUQETGCenKrX+ocE9JUAQ==</D>" +<RSAKeyValue>";public static string GetPreSignedURLWithXMLKey(string resourceURL, DateTime expiryTime, string keyXML, string keypairId, bool urlEncode){long expiry = (long)(expiryTime.ToUniversalTime() - new DateTime(1970, 1, 1)).TotalSeconds;string policy = String.Format(@"{{""Statement"":[{{""Resource"":""{0}"",""Condition"":{{""DateLessThan"":{{""AWS:EpochTime"":{1}}}}}}}]}}",resourceURL, expiry);if (string.IsNullOrEmpty(keyXML))keyXML = xmlKey;EZRSA ezrsa = new EZRSA(1024); //Amazon Cloudfront uses 1024 bit encrypted keysezrsa.FromXmlString(keyXML);string signature = UrlSafe(ezrsa.SignData(Encoding.UTF8.GetBytes(policy), new SHA1CryptoServiceProvider()));string formatStr = urlEncode ?"{0}%3fExpires%3d{1}%26Signature%3d{2}%26Key-Pair-Id%3d{3}" :"{0}?Expires={1}&Signature={2}&Key-Pair-Id={3}";return string.Format(formatStr, resourceURL, expiry, signature, keypairId);}
Now all you need to do is call:
DateTime expires = DateTime.UtcNow.AddSeconds(30);string formattedUrl = PMI.AWS.CloudFront.SignedURL.GetPreSignedURLWithXMLKey(cloudfrontUrl, expires, string.Empty, "KeyID", false);
And you're set! the url will be signed like before, but you will have worked around the exception. If you have anymore tips or questions feel free to comment! Anthony 1 - Shared hosting 0