If you publish public Nuget packages, I propose that…
- Your assemblies should be strongly named
- Your signing settings and private key must be PRIVATE
- This kinda sucks
- Maybe there’s a way…
Let’s start at the top.
Your assemblies should be strongly named
You should be strongly naming your assemblies. Done correctly, it improves the security of your application by making your .dll and .exe files tamper resistant.
Strong-naming gives an application or component a unique identity that other software can use to refer explicitly to it. For example, strong-naming enables application authors and administrators to specify a precise servicing version to be used for a shared component. This enables different applications to specify different versions without affecting other applications. In addition, you can use the strong name of a component as security evidence to establish a trust relationship between two components. – MSDN
It is also really easy to setup, you can do it in just a few clicks in Visual Studio.
It’s especially important for Nuget packages. That’s because strong assemblies can only reference other strong assemblies, but weak assembly can reference either. There is nothing to stop a developer from signing (strongly naming) a weak package with their own key, in fact, it’s preferable.
The problem is that a weak package cannot act as a dependency for any strongly named packages.
So, why aren’t assemblies strongly named by default? Is there a downside? Well, that depends…
Your signing settings and private key must be PRIVATE
The problem is that strongly named assemblies depend upon asymmetric encryption. The assembly name, version, culture, and files are signed, and the public key is bundled into the assembly.
When your assembly is loaded at runtime, the .NET frame uses the public key to verify the signature. This is a strong indication that the signed data is authentic.
That’s all fine and dandy, assuming your private key is actually private.
Here’s the rub.
This kinda sucks…
If the package is open-source AND strongly named then one of three things must be true.
- The private key is committed as part of the code
- The signing is handled outside of Visual Studio
- You’ll get a big fat build error 🙂
Pick your poison.
I opted for option #2 for ColorMine.
Committing the private key defeats the purpose of strong naming and I can’t bear to have a persistent build error sitting in my repository.
It is not difficult to do the signing outside of Visual Studio, I have a bigger problem. Now I have a secret.
Maybe there’s a way…
Here’s an idea:
Maybe we can get around the problem with a simple script. If we generate a new key on every publish, then the script we use to publish CAN be checked in, the secret doesn’t need to be stored, and best of all NO BUILD ERRORS!
I’ve got a rough script that does this already, but I need to verify that this is a valid approach. It seems strange to me that we can throw away the private keys, but I’ve yet to come up with a reason why it wouldn’t work.
— UPDATE: 6/7/2014 —
I’ve been talked out of changing the key with every publish. A big part of a signature’s benefit is that it authenticates the publisher.
No reason to throw it away, thanks Peter!
— END UPDATE: 6/7/2014 —
Conclusion
To recap: Strongly named assemblies are good, key management is hard for open-source projects, and there’s no great solution, but maybe there’s a better way.
In the meantime, if you see any other solutions or problems with this approach then I’d love to hear it!
Please Note: You might have noticed some qualified language like “strong indication” and “resistant”. That’s because…
- When you are talking about digital security, there is no such thing as 100%.
- Improper key storage, untrustworthy keys, or an attacker who strips and re-signs your assemblies can invalidate the protection, and strong name checking can be bypassed entirely on the host. Strong names making tampering more difficult, not foolproof.
- Detection and reaction are more important than prevention. 🙂