Tuesday, September 25, 2012

C# - Security - Cryptographic Agility

When developing any software it is really important to try and make your software as flexible as possible - loosely coupled designs with highly configurable components results in a much more sustainable and less fragile application.  Of particular concern in modern software is security; the most secure algorithms used in today's software are not the most secure algorithms in tomorrow's software.  In an effort to reduce hardcoding the cryptographic algorithms utilized within an application should be configurable leading to a concept known as Cryptographic Agility.  In the .Net world this is accomplished using the machine.config file stored within the .Net installation.  There is a great article that discusses how this is implemented and how to use it here, so I will not rehash it in this post.  There is no doubt that this is the preferred mechanism for implementing cryptographic agility in windows .Net applications as the machine.config file is managed by the administrators for the machine and are not generally accessible to all users on the machine (thus preventing just anyone from changing the algorithm to a less secure algorithm).  However, not all all deployment scenarios are necessarily able to accommodate these changes (maybe b/c it is a shared hosting site).  Thus, in this post I want to provide one alternative that allows you to take advantage of the machine.config file, but still offer an alternative for those times when the machine.config file is out of your control (or for those rare use cases where you need to override the machine.config).

One option is to use the application's properties to define the default algorithms if that machine.config file does not contain them. One possible scenario, and the one I will use here, is that all of your base crypto files/utils can extend from a base class that uses a static constructor to ensure that either the machine.config values exist OR that they are added to the CryptoConfig configuration from a property file.  There are other ways to initialize the CryptoConfig, of course, but this one is easy and works well.  However, it is important to note, that if an exception is thrown within the static constructor, that the application will NOT terminate and the class file will be left in an unusable state, so please make sure you code for this.  Some might consider this a bad decision (to use this static constructor), but I think it depends on your situation - in my case, if this fails that application can't be used, thus I check for this and fail out gracefully.

Now on to the example.  First create a base class from which your others can extend:

public abstract class Cryptography
{
     static Cryptography()
     {
     }
}

Before we fill in the static constructor, let's add a couple methods that we will be using within the constructor.  The first method will be the one used to register with CryptoConfig the algorithm defined in the properties file.  This will be used when the machine.config file is missing the mapping or when there is a desire to override it (more on this option shortly).

private static void SetAlgorithm(
  string name, string cryptoType)
{
 CryptoConfig config = new CryptoConfig();
 Type type = Type.GetType(cryptoType);
 CryptoConfig.AddAlgorithm(type, name);
}

The next method is the main method to check if the value is in the machine.config file, it it is not (the call to CreateFromName will return null), then it will call SetAlgorithm to set the type based on what is in the configuration file.  You must ensure there is a value in the configuration file as this class doesn't handle the situation where it is not (or add your own defaults to this class).

private static void SetConfiguration(
 string name, string type)
{
 object algorithm = CryptoConfig.CreateFromName(type);
 if (algorithm == null)
 {
  SetAlgorithm(name, type);
 }
}

Finally, back to the static constructor.  In order to allow overrides, we use a property to indicate that we should use the local settings rather than machine.config values.  In order to reduce code, I am using an action delegate to define that the method "SetAlgorithm" should be used when the UseLocalSettings property is set to true; thus, when this property is true, even if the algorithm is configured in machine.config, the local setting will be used instead.

static Cryptography()
{
 Action action = SetConfiguration;
 if(Properties.Settings.Default.UseLocalSettings)
 {
  action = SetAlgorithm;
 }
 action("Hash256", Properties.Settings.Default.Hash256);
 action("Hash512", Properties.Settings.Default.Hash512);
 action("Symmetric", Properties.Settings.Default.Symmetric);
 action("Asymmetric", Properties.Settings.Default.Asymmetric);
}

Edit: Thanks to a post from a google+ follower (Chris Eargle) I realized there was one omission (the generics on my Action) and a more elegant way to set the action method (ternary expression).

Action<string, string> action = 
     Properties.Settings.Default.UseLocalSettings ? 
           SetAlgorithm : SetConfiguration;

Finally, you just need to set these properties in your app.config file.  Each of the hash algorithms are strings and the UseLocalSettings property is a bool.  Example settings for the algorithm's values are as follows (for .Net 4.0):

Hash256=System.Security.Cryptography.SHA256CryptoServiceProvider, 
 System.Core, Version=4.0.0.0, Culture=neutral, 
 PublicKeyToken=b77a5c561934e089
Hash512=System.Security.Cryptography.SHA512CryptoServiceProvider, 
 System.Core, Version=4.0.0.0, Culture=neutral, 
 PublicKeyToken=b77a5c561934e089
Symmetric=System.Security.Cryptography.AesCryptoServiceProvider, 
 System.Core, Version=4.0.0.0, Culture=neutral, 
 PublicKeyToken=b77a5c561934e089
Asymmetric=System.Security.Cryptography.RSACryptoServiceProvider, 
 mscorlib, Version=4.0.0.0, Culture=neutral, 
 PublicKeyToken=b77a5c561934e089

I hope someone finds this useful... Feel free to comment if you have suggestions on how this can be improved or if you have questions about the content.

25 comments:

  1. Hellp Paul,
    I have been working on registering the SHA256 algorithm in my Sharepoint Webpart, while I am able to do this in my windows forms application on .Net 4.0,I am unable to get this working in my Sharepoint which is built onin .Net 3.5 . Can you tell me if there is any workaround for this. Iam actually following this http://social.msdn.microsoft.com/Forums/en-SG/netfxbcl/thread/6438011b-92fb-4123-a22f-ad071efddf85 which is alomost similar to what yoiu have suggested in your article. Let me know if this can made working in .Net 3.5?? Appreciate ur early response.

    ReplyDelete
  2. muneeb - I need a little more detail about what you are doing. Are you trying to use the machine.config or are you registering algorithms at run time the way I am showing in this post? As far as I know, this is all supported in .Net 3.5

    http://msdn.microsoft.com/en-us/library/693aff9y%28v=vs.90%29.aspx --> set the version to 3.5 - from this, I would say what you are trying to do is supported. Please feel free to post back with more details and I will try to help.

    Also, what specific SHA256 algorithm are you trying to use? It appears both (CNG and CryptoServiceProvider) are supported in .Net 3.5
    http://msdn.microsoft.com/en-us/library/system.security.cryptography.sha256.aspx

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Thanks Paul, I am trying to register it at runtime . As I said I am actually following this
    http://social.msdn.microsoft.com/Forums/en-SG/netfxbcl/thread/6438011b-92fb-4123-a22f-ad071efddf85 which is almost similar to what you have suggested(my guessing??). I have class with the RSA SHA256 signature description in the properties instead of the appconfig and I call this method 'AddAlgorithm' of CryptoConfig.AddAlgorithm(typeof(Security.Cryptography.RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"); to register it in config at runtime . This method is available in .Net 4.0 but not int 3.5. The intellisense does not list it and I am unable to add the 'mscorelib' from 4.0 to the solution of 3.5 so that I have this method available. Let me know how can acheive this in 3.5 , Is there any other way I need to adapt. And you metnion .Net 4.0 at the end , I need the values for 3.5 if possible. Thanks and appreciate your earliest response

    ReplyDelete
  6. Ok, I see your problem... you want to call "AddAlgorithm", but that was not added until .Net 4.0 - I believe the solution you are looking for is provided by CLR Security. I have not utilized this, but a quick look at the source code suggests it is exactly what you need.

    http://clrsecurity.codeplex.com/

    I hope this helped... good luck!

    ReplyDelete
  7. Thanks Paul, that is where I am at now. I hope just adding the Security.Cryptography.dll through the gacutil should do it for me??

    ReplyDelete
  8. My pleasure. Please, post back and let me know if this worked out for you. These comments might help someone else out!

    ReplyDelete
  9. Paul, I am unable to get past this . I have the AddAlgorithm method available now but I am not able to add in the algorithm as it says " Alias 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' already exists in the CryptoConfig2 map ". It seems it already exists. I tried Createfromname metthod and not sure what exactly to pass as parameter?

    Another crtical point is I am signing an xmldocument with SignedXml class of System.Security.Cryptography.Xml by calling its ComputerSignature() method. Is is compatible with this CryptoConfig2 or do I have to use any other class from the installed dll. Request you to throw some light. Also please refer to the signature definiton class to get a better idea whats in ther.. Appreciate your help Thanks

    ReplyDelete
  10. It sounds like you don't need to register that algorithm... remove your call to AddAlgorithm and when you call CreateFromName pass in RSA-SHA256 - so 'CreateFromName("RSA-SHA256") as SignatureDescription;'

    Based on what I saw online, RSAPKCS1SHA256SignatureDescription became available in .Net 4.5 - I am not sure if maybe CryptoConfig2 is providing this library or what is going on. Please see this post too: http://clrsecurity.codeplex.com/wikipage?title=Security.Cryptography.RSAPKCS1SHA256SignatureDescription

    I am not sure about your second question. I have never used CryptoConfig2 - I changed from .Net 3.5 to 4.0 in order to take advantage of the code I wrote about in the blog post.

    ReplyDelete
  11. Paul, I have this working in .Net 4.0. Its with 3.5 I have the problem with as Sharepoint is bound to 3.5 only.


    My Second question was as it seems that I aready have the algorithm available now, how do i use it to sign an xml document. I am signing it using the SignedXml class available in System.Secuirty.Cryptography.Xml namespace. Is this the correct way or do I have to use a class from Security.Cryptography namesapce??


    And One more thing is that is it necessary for me to make any changes to the machine config as discussed here while using Cryptoconfig2 as it mentions here
    http://clrsecurity.codeplex.com/discussions/243156 ?? I guess I am registering the algorithm in config from the code itself by following Addalgoirth method. or it is already available with the new dlls added. ?? Really appreciate your help and analysis into this..
    Thanks

    ReplyDelete
    Replies
    1. No, I do not believe that you need to register it with machine.config if you call CryptoConfig2.AddAlgorithm().

      From your first link, it looks like you want something like this: CryptoConfig2.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");

      I know earlier you mentioned that you got an error that it was already present. I think the reason for that is that CryptoConfig2 has their own impl of this and it might match what you want (I am not sure what you defined in your RSAPKCS1SHA256SignatureDescription object. Take a look at this http://clrsecurity.codeplex.com/SourceControl/changeset/view/47833#269110 to see if that is what you are trying to define.

      As far as signing the xml document, I would think the code in the first link you posted should work. I think that the SignedXml method ComputeSignature should work.

      I am sorry that I am probably not being very helpful at this point, but it is too difficult to troubleshoot without the code.

      Delete
  12. This comment has been removed by the author.

    ReplyDelete
  13. Yes I have exactly the same description for RSAPKCS1SHA256SignatureDescription object as mentioned in your link
    http://clrsecurity.codeplex.com/SourceControl/changeset/view/47833#269110 except for Createformatter method.

    One more is that I had just added the reference to the Security.Cryptography in my project and using "gactutil -I" it said it got added GAC but i was not able to locate in the list of dlls in under .Net. tab while adding the refernce. Am I OK with this or the dll was not correctly added to GAC..hence it did not get listed ??

    Also wanted to know if the certificate I use need to have the exactly same signature algorithm as the one with which Im trying to sign as the certificate has sha1 and I am trying to sign the XML with SHA256??

    Let me know if I can provide the code here? Really apprciate your help..Thanks

    ReplyDelete
    Replies
    1. I am not sure of the answer to your first question, I am not familiar with gactutil.

      Yes, your certificate needs to be updated to be a Sha2 cert if you want to use SHA256 (recommended).

      You can provide code here, though I think the comments here will make it ugly and hard to read.

      Delete


  14. public static XmlDocument SignXml(XmlDocument xmlDoc, RSA Key, X509Certificate2 cert)
    {

    // CryptoConfig2.AddAlgorithm(typeof(Security.Cryptography.RSAPKCS1SHA256SignatureDescription1), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");

    // Create a SignedXml object.
    SignedXml signedXml = new SignedXml(xmlDoc);

    signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#";


    KeyInfo keyInfo = new KeyInfo();
    // *** The actual key for signing - MAKE SURE THIS ISN'T NULL!
    signedXml.SigningKey = cert.PrivateKey;
    // *** Specifically use the issuer and serial number for the data rather than the default
    KeyInfoX509Data keyInfoData = new KeyInfoX509Data();
    // keyInfoData.AddIssuerSerial(cert.Issuer, cert.GetSerialNumberString());
    keyInfoData.AddCertificate(cert);
    keyInfo.AddClause(keyInfoData);
    // *** provide the certficate info that gets embedded - note this is only // *** for specific formatting of the message to provide the cert info
    signedXml.KeyInfo = keyInfo;



    // Add the key to the SignedXml document.
    signedXml.SigningKey = Key;

    // Create a reference to be signed.
    Reference reference = new Reference();
    reference.Uri = "";

    // Add an enveloped transformation to the reference.
    XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
    // env.Algorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
    reference.AddTransform(env);

    XmlDsigEnvelopedSignatureTransform env1 = new XmlDsigEnvelopedSignatureTransform();
    env1.Algorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";

    reference.AddTransform(env1);
    // Add the reference to the SignedXml object.
    signedXml.AddReference(reference);


    signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";

    // signedXml.SignedInfo.SignatureMethod = "SHA256";
    // Compute the signature.
    signedXml.ComputeSignature();

    // Get the XML representation of the signature and save
    // it to an XmlElement object.
    XmlElement xmlDigitalSignature = signedXml.GetXml();


    XmlNamespaceManager nmgr = new XmlNamespaceManager(xmlDoc.NameTable);



    XmlElement node = xmlDoc.DocumentElement.SelectSingleNode("//s:Envelope/s:Header/wsse:Security", nmgr) as XmlElement;

    XmlElement oldnode = xmlDoc.DocumentElement.SelectSingleNode("//s:Envelope/s:Header", nmgr) as XmlElement; ;

    node.AppendChild(xmlDigitalSignature);


    xmlDoc.SelectSingleNode("//s:Envelope/s:Header/wsse:Security", nmgr).AppendChild(xmlDigitalSignature);

    // XmlElement soapHeader = xmlDoc.DocumentElement.SelectSingleNode("//SOAP:Header", ns) as XmlElement;

    // Append the element to the XML document.
    xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));

    return xmlDoc;

    }
    }









    ReplyDelete
  15. Paul ,can you tell me what are the other algorithms supported for generating XMl signature other than SHA 1 and SHA256. I have tried using SHA 128,SHA 256, SHA 512 with the same code but they don;t work.Please let me know

    ReplyDelete
  16. muneeb - sorry for the lack of reply, been busy with work all day. You have, if i recall correctly, a SHA1 cert... the algorithms you listed are all SHA2. The one you are looking for is SHA1 (http://msdn.microsoft.com/en-us/library/system.security.cryptography.sha1.aspx).

    I will try to look over your code tomorrow night... been a long day.

    ReplyDelete
  17. Thanks Paul and not at a problem . reaaly appreciate your effort.

    Yes I have SHA cert and I am able to sign the xml with sha1 even if i dont give the signature method as SHA1. it generates it by default. The SHa2 algorithms don;t work.. Thanks once again let me know.

    ReplyDelete
  18. Paul , I have an update that our team has realized that we won;t be going forward with ShA-256 for now due to few other constraints and I will be using SHA-1 for now. I am pretty comfortable with it ..

    Thanks a lot for your effort and time !!!!Appreciate it.

    ReplyDelete
  19. No problem, glad I could help a little and I am happy to hear that you have it all working now. You should, for security reasons, move on to SHA2 as soon as you can... once you have a SHA2 cert, you will be able to use the other algorithms.

    ReplyDelete
  20. Yes we do have that on our agenda for the future. Hope I can reach out to you incase if i need anything else related to Cryptography? Thanks a lot Paul

    ReplyDelete
  21. Yes muneeb... please do. If interested, I do some consulting on the side as well. I am CSSLP (Certified Secure Software Lifecycle Professional) certified (Cert #: 392260) and always looking for ways to utilize those skills.

    ReplyDelete
  22. Hi - long shot but I hope you can help me.
    I'm trying to use RSA-SHA256 with .net 3.5. Because
    CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
    is not supported in .net 3.5 I used
    CryptoConfig2.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"); from (http://clrsecurity.codeplex.com/) but I keep getting "Additional information: Alias 'http://www.w3.org/2001/04/xmldsig-more#... already exists in the CryptoConfig2 map."

    Do you have any idea?

    ReplyDelete

Followers