Why Can’t I Change the KeySize of Asymmetric Algoritms or: The Joys of Backwards Compatibility

Here’s a little quirk that can definitely cause a lot of confusion.  When I run the following code snippet, what do you suppose the output will be:

RSA rsa = new RSACryptoServiceProvider();
rsa.KeySize = 4096;

If you guessed 1024 and 4096, you’re in for a surprise when you try running the code.  Instead the result is actually 1024 and, well, 1024.

Modifying the KeySize property of RSACryptoServiceProvider or DSACryptoServiceProvider won’t actually change the key size.  However, it won’t throw an error either.  Instead, it will just silently accept the new key size and do nothing about it.  In fact, it won’t even update the value you get out of the KeySize property.

This seems like a simple thing to fix for Whidbey, right?  Well, here’s where the joys of maintaining backwards compatibility come into play.  There are two options for a fix:

  1. Provide a KeySize setter that throws an exception
  2. Generate a new key of the specified size whenever KeySize is set

Option one will clearly break any application that attempts to set KeySize on one of the asymmetric implementations, since we will now throw an exception where we didn’t before.

Option two is also a break, since any application that uses this property will now be using keys of a different size.  The case is easily made that this is a bug in these applications, however, that doesn’t make the change on our end any less breaking for them.

Since the bar for allowing a breaking change into the platform is very high, neither of these are acceptable.  Instead, if you’d like to create a key with a different key size than the default, you should use the constructor overload that takes a key size parameter.  This method has always worked, and will continue to work in Whidbey.  And if you’d like a bin to throw your garbage bits into, you can continue to use RSACrytpoServiceProvider.KeySize 🙂

Comments (12)

  1. Nicholas Allen says:

    Can the setter at least be obsoleted in this release so that it’s eligible for removal in the future? It seems unlikely that this will ever get fixed.

    Although really this seems like a security exploit waiting to happen. You’re fooling users of this API into thinking they have strong protection when in fact they may be vulnerable due to a low strength method.

  2. Eric Newton says:

    we [the developers consuming your products] cannot continue to expect backward compatibility with newer versions of the framework… this is the whole point of SxS installations!

    we [the developers] should always RETEST our products when WE decide to use newer framework versions…

    come on guys… ! I think you’ve got the mentality that we’re stupid or something… fix the problems and DO IT RiGHT. Mistakes happen… DONT PERPETUATE them.

  3. Tony Chow says:

    I’ve heard that Microsoft developers have regular "security drives". Does this top ever come up at one of these meetings? From your tone, I don’t get the impression that you’ve taken a very hard look at the security aspect of the problem. Rather it appears that you are seeing this merely as a matter of elegance.

    This kind of mentality obviously isn’t helping Microsoft’s security initiative.

  4. Shawn says:

    Good comments … let me address them one at a time.

    Nicholas: Obsoleting the setter sounds like a good idea, until you start digging down into the crux of the problem. If you ILDasm mscorlib.dll and look at the RSACryptoServiceProvider class, you’ll notice something interesting. This class only provides a getter for the KeySize property, not a setter. The setter is actually defined in the base AsymmetricAlgorithm class. That’s the underlying cause of the bug, the derived class overrode only one portion of the property.

    We could technically go ahead and add a setter to the derived classes, and then mark them as Obsolete, however we could never actually remove the setter since it’s derived in the base class, and that would break any number of people who have derived from it. This would also cause incompatibilities between our interfaces and other projects who have implemented the CLI. Declaring a method as obsolete without ever intending to remove it is not a good solution.

    Eric: While its true that people should be running tests on new verisons of the framework, its a fact of life that not everyone will. That’s part of the reason that Microsoft keeps its compatibility bar so high. There’s a reason that you can still run Win95 code on Windows XP and that the PDC version of Longhorn still runs Lotus 1-2-3!

    Tony: We do have regular security pushes. Generally these are scheduled for after code complete on a project, since after the security push is done we don’t like to modify the code base and introduce potential new vulnerabilities. As part of the security push, we generate threat matrixes, and rank every potential vulnerability. This one doesn’t rank as high as some other potential threats simply because the user is still getting encryption — just not encryption as strong as they wanted. Also, if they were to ever check the KeySize property again, they would notice that the value did not change, so we’re not completely lying to them.

    All that being said, thanks a lot for the feedback. Its obvious that you guys think this is a bigger issue than we first imagined it to be. Because of this we’ll continue to look at our options for this before we ship Whidbey. Hopefully my above comments help to explain why the fix isn’t as clear cut as it first may seem.

    Thanks for the feeback!


  5. Nicholas Allen says:

    You’re entirely right about the origin of the setter. That’s a shame because obsoleting gives good feedback to the user about what has changed.

    Here’s proposal #2 then:

    The specification for the setter of KeySize says that a CryptographicException may be thrown if the key size is invalid. It goes on to say that the valid sizes are defined by the LegalKeySizes property. The specification for LegalKeySizes makes no guarantees that I can see about what those legal sizes are.

    Why can’t the LegalKeySizes for a particular instance of RSACryptoServiceProvider contain only the key size provided at creation time? Then there’s no problem that the setter doesn’t work. Either the property is being set to its current value, no work needed, or it’s being set to an illegal value. This seems to maintain strict CLI compatibility.

  6. AT says:

    The only correct proposal: Create a FxCop rule for this !!

  7. Luc Cluitmans says:

    This whole issue is interesting. I don’t want to sound challenging, but it could be phrased as ‘microsoft treats backward compatibility as more important than security’, which is probably not the best message you can send out to the world.

    I think that at least some mechanism should be added that will cause alarm bells to go off, preferably at compile time, when this setter is called. Or, alternatively, add some mechanism that will cause the setter to throw an exception (NotSupportedException, for instance) if a certain administrative action has taken place (for instance by setting a flag in the application configuration file, or event the system configuration files). This will give people the warm feeling that, in the case some software triggers this feature they will get informed about it, instead of not being made aware that the wrong key size is being used.

    Frankly, I think this is a case where MS has to swallow the bitter pill, and security has to be prioritized over backward compatibility. Add those exceptions, or remove the setter, even if it will break existing software.

    Btw, I disagree with AT. Yes, adding an FxCop rule is a good idea, but not everyone will run FxCop, and this is something that everyone needs to be aware of, so it cannot be the only solution.

  8. Fred says:

    I completely agree with Luc Cluitmans.

    When you say lotus 1-2-3 is still running on… Yes. But this is different. There is no security issue to make lotus running.

    If not so, why fixing any bug? Some older applications may use this bug as a fact and beeing manufactored to take it into account.

    This is a normal process to correct an application even if I understand that a framework is a special kind of application.

  9. Shawn says:

    So we’ve taken everything into consideration. The LegalKeySize idea that Nicholas presented is a good and very creative one, however in many ways that would be more breaking than fixing the original bug. (Think of someone who news up a RSACryptoServiceProvider, then uses LegalKeySizes to see what size keys are available on that machine).

    The FxCop rule is a very good idea, and most likely the route that we’ll end up taking on this. Great suggestion AT.


  10. Eric Newton says:

    Shawn: A good portion of apps have an AppCompatibility SHIM to work in Windows XP, for various reasons. (Mostly I believe because of GDI changes and so forth).

    The usage of Side-by-Side and targetting specific framework versions should be eliminating this "problem" or "compatibility bar" as you call it… Microsoft should see this as a way to minimize regression testing old apps on new code.

    Think about COM… we had a problem when an app installed a newer version of a component because the apps were FORCED to use the new version, because only one version could be used, especially in binary compatibility modes [a vb term].

    SxS and .Net have virtually eliminated this problem.

    The only problem now is ASP.Net as a hosting platform, because if you want to use asp.net 2.0, but only want to use framework 1.1, its not possible, and in THAT sense, we should do some regression testing and acknowledge the fact that OLD APPS have a high probability of not properly running on the NEW FRAMEWORK… [caps only for emphasis]