9

I have done considerable research and not found any suitable answer. Here is the scenario.

I have an application which was compiled to target .NET Framework 4. At runtime I want that application to actually execute within the .NET Framework 4.6.1. I found two options so far.

  1. Recompile the application under .NET Framework 4.6.1
  2. Add the configuration/startup/supportedRuntime element to app.config with version="v4.0" sku=".NETFramework,Version=v4.6.1"

Option 1 is not desirable as it would require a re-release of software.

Option 2 doesn't do what I would expect. It seems to check if CLR 4.0 (not framework 4.0) is installed and if not prompts to download the appropriate SKU to install it. After being installed, the application still executes under .NET Framework 4.0

As a test, and the reason this question is posted, I created a small console app which simply does this

Console.WriteLine(System.Net.ServicePointManager.SecurityProtocol);

If that is compiled against .NET Framework 4 then the output is

Ssl3, Tls

If that is compiled against .NET Framework 4.6.1 then the output is

Tls, Tls11, Tls12

Greg Bogumil
  • 1,903
  • 15
  • 23
  • 2
    http://stackoverflow.com/a/28502562/17034 – Hans Passant Jun 17 '16 at 13:04
  • 1
    Thank you Hans. This does fix the issues in my sample and is what we are going to use. I am also interested in the answer to the larger question also. Do you by chance know if it is possible and if so, how? – Greg Bogumil Jun 17 '16 at 13:20
  • 1
    Hmya, the .NET 4.0 version of SecurityProtocolType does not have the enum values you like. How your app could possibly interpret the property value correctly is a very hard guess. But if it works then it works. – Hans Passant Jun 17 '16 at 13:44
  • 1
    It works only if >=4.5 framework is installed. Even if the app is compiled against 4.0 – Greg Bogumil Jun 17 '16 at 17:50
  • Why don't you just change your target to 4.5.2+? What you are trying to do is a hack that can break in untestable ways. The change from 4.0 to 4.5.2 is *not* that great – Panagiotis Kanavos Jun 28 '16 at 10:05

2 Answers2

11

I am ... asking about the general case of forcing an application compiled against framework 4.0 to run using framework 4.6.1

Well, you already did with app.config file entry. It empowers this feature, the user can't get the program running without installing 4.6.1 first. All he has to do is click the Yes button. Not that this gets exercised very often, 4.6.1 should always be present on the machine when the user is responsible about keeping his machine updated with Windows Update. If he intentionally doesn't then "forcing" isn't very likely to be received well.

But that is not actually what your question is about. You want your program to behave like it is installed on 4.6.1. That's a very different kettle of fish. Do note that 2) did not work, you can't fool the runtime that easily. The compiler embedded a TargetFrameworkAttribute attribute in your executable file, that's the one the runtime uses to determine how it should behave. Have a look-see with ildasm.exe, double-click the manifest to see it. Your app.config entry does not override it.

Biggest issue is that .NET 4.5 is rather fundamentally different with heavily breaking changes in the both the runtime and the framework assemblies. Heavy enough to would have warranted bumping the version up to 5.0. But that always causes lots of pain and suffering on the customers, Microsoft pulled out every trick in the book to make 4.5 (and up) behave like 4.0 if it runs a program that targeted 4.0.

Not just one trick. One core approach were the reference assemblies stored in c:\Program Files (x86)\Reference Assemblies directory. They store the targeting pack files. You used the ones stored in C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.0 when you originally built your program. If you retarget your project then you'd use the ones stored in v4.6.1. They are very different. And notably the SecurityProtocolType enum you are talking about is different, it acquired two new values. This is a breaking change, a .NET 4.0 program is liable to suffer a heart-attack when it sees SecurityProtocolType.Tls12, it has no idea whatsoever what it could mean. Building a program with the wrong targeting pack files can cause deeply mystifying exceptions.

And other tricks. Bug fixes made in post .NET 4.0 releases are selectively turned on depending on the [TargetFrameworkAttribute], backwards compatibility for bugs is important to ensure that a program doesn't observe changed runtime behavior. And the CLR is filled to the brim with appcompat switches. I could point to the source code file in CoreCLR but it is entirely too scary to look at :)

So no, making a program compiled to target .NET 4.0 behave like it runs on a higher version is not possible. The registry key and the appcontext switch you learned about is highly specific to just the ServicePointManager.SecurityProtocol property. They are there simply because you are not the only customer that wants to do this, TLS versions are rather important. Just make sure that the new enum values don't trip up your program.

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Marked as answer because it seems it is not actually possible to do what I'm asking. You explanation contains great detail and reference. Thank you Hans. – Greg Bogumil Jun 26 '16 at 23:45
  • @GregBogumil what you are trying to do is force a hack on end users - you don't want to upgrade your compilation target (why?) but want to force a newer runtime to use a TLS value that doesn't exist in 4.0. The real fix is to change your target – Panagiotis Kanavos Jun 28 '16 at 10:02
4

The specific ServicePointManager.SecurityProtocol determination is quite unrelated to the general question of 4 vs 4.6.1 framework for which I'm afraid there's not definitive answer, because it's not relevant in the general case (all cases are probably specific, if you will).

For the specific answer, this is described here: Mitigation: TLS Protocols

Starting with the .NET Framework 4.6, the System.Net.ServicePointManager and System.Net.Security.SslStream classes are allowed to use one of the following three protocols: Tls1.0, Tls1.1, or Tls 1.2. The SSL3.0 protocol and RC4 cipher are not supported.

If the 4.6+ is installed, then you can indeed change the behavior of your program w/o recompiling it, as detailed in the article, just add this line to your .config files:

<configuration>
  ...
  <runtime>
    ...
    <AppContextSwitchOverrides value="Switch.System.Net.DontEnableSchUseStrongCrypto=false" />
  </runtime>
  ...
</configuration>
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • 1
    Thank you for the answer. It is quite valid for the sample I posed to highlight something I'm trying to determine. I am, in this question, asking about the general case of forcing an application compiled against framework 4.0 to run using framework 4.6.1. I am more well versed than many in the difference between CLR vs framework and understand that forcing to run using a different CLR is attainable. It seems based that the answer to this question very well may be that it cannot be done without recompiling the application to target framework 4.6.1. – Greg Bogumil Jun 24 '16 at 02:24
  • 1
    And specifically thank you for the additional runtime configuration change. Based on much research the best answer I had thus far was to make a registry change. At client machines that sometimes requires administrative staff. – Greg Bogumil Jun 24 '16 at 02:25