Can strong naming an assembly be used to verify the assembly author?
Asked Answered
S

5

22

I have been reading the proper article in MSDN, Strong-Named Assemblies and a related Stack Overflow question, Checking an assembly for a strong name.

  1. To which extent can a strong-named assembly be verified to avoid tampering?
  2. Is it possible to use strong-naming to verify an assembly author?

The first question arises after reading the CSharp411 article .NET Assembly FAQ – Part 3 – Strong Names and Signing, which mentions this, among other problems of using strong names:

"Cannot Stop Full Replacement. Strong names cannot prevent a hacker from removing the strong name signature, maliciously modifying your assembly, re-signing it with his own key, and then passing off his assembly as yours."

The second question intends to find the differences between strong naming and other signing schemes like, say, Authenticode. The same MSDN article mentioned early states:

"Note, however, that strong names in and of themselves do not imply a level of trust like that provided, for example, by a digital signature and supporting certificate."

Am I trying to use strong-naming for much more than it was created for? Was strong-naming created just to avoid name clashes or a new kind of "GAC DLL Hell"?

Scopolamine answered 15/12, 2008 at 18:17 Comment(0)
H
23

When you sign an assembly with a strong name based on a private key that you create, this has the following benefits:

  • A strong name guarantees the uniqueness of an assembly's identity by adding a public key token and a digital signature to the assembly.
  • A strong name can be matched to a public key to prove that the assembly comes from the publisher with that public key, and only that publisher.
  • A strong name provides a strong integrity check. Passing the .NET Framework security checks guarantees that the contents of the assembly haven't been changed since it was last built.

Is it possible to use strong-naming to verify an assembly author?

Yes, as discussed above strong-naming can verify the assembly's latest author. But it doesn't verify the original author. If an attacker replaces your assembly's strong name, then all that can be verified is that you weren't the latest author of the assembly. If he removes the strong name, then no author verification can be done at all.

To which extent can a strong-named assembly be verified to avoid tampering?

The following C# code verifies that an attacker hasn't tampered with the public key token that was written to your assembly when you applied the strong name. It doesn't avoid tampering, but it can detect some types of tampering. The method below accepts a byte array containing your public key token, and compares it with the actual token of the assembly. Note that for this technique to be effective, your obfuscator of choice should encrypt the string containing your public key token, and only decrypt it on the fly as it's used. And also be aware that you need to have FullTrust permission for this code to work because it uses reflection underneath the hood.

// Check that public key token matches what's expected.
private static bool IsPublicTokenOkay_Check(byte [] tokenExpected)
{
    // Retrieve token from current assembly
    byte [] tokenCurrent = Assembly.GetExecutingAssembly().GetName().GetPublicKeyToken();

    // Check that lengths match
    if (tokenExpected.Length == tokenCurrent.Length)
    {
        // Check that token contents match
        for (int i = 0; i < tokenCurrent.Length; i++)
            if (tokenExpected[i] != tokenCurrent[i]) 
                return false;
    }
    else
    {
        return false;
    }
    return true;
}

As long as you're running under a version of the .NET Framework before .NET 3.5 SP1, you can also force verification of the strong name signature in case the strong name was removed by an attacker or the strong name check was disabled in the registry. The following code demonstrates a call into a static method of another class called NativeMethods. This is where the verification will be enforced.

// Check that this assembly has a strong name.
private bool IsStrongNameValid_Check()
{
    byte wasVerified = Convert.ToByte(false); 
     byte forceVerification = Convert.ToByte(true);
    string assemblyName = AppDomain.CurrentDomain.BaseDirectory + 
                          AppDomain.CurrentDomain.FriendlyName; 
    return NativeMethods.CheckSignature(assemblyName, 
                                        forceVerification, 
                                        ref wasVerified);
}

The actual signature verification is done using P/Invoke as shown below. The usage of the StrongNameSignatureVerificationEx API is quite convoluted - for a decent explanation, see this blog entry.

// P/Invoke to check various security settings
// Using byte for arguments rather than bool, 
// because bool won't work on 64-bit Windows!
[DllImport("mscoree.dll", CharSet=CharSet.Unicode)]
private static extern bool StrongNameSignatureVerificationEx(string wszFilePath, 
                                                             byte fForceVerification, 
                                                             ref byte pfWasVerified);

// Private constructor because this type has no non-static members
private NativeMethods()
{
}

public static bool CheckSignature(string assemblyName, 
                                  byte forceVerification, 
                                  ref byte wasVerified)
{
    return StrongNameSignatureVerificationEx(assemblyName, 
                                             forceVerification, 
                                             ref wasVerified );
}

Note that this won't work by default for applications using .NET 3.5 SP1 or higher, which has the strong name bypass feature. It's possible to disable this feature for your application by adding a setting to its config file. But of course any attacker with read/write access to that config file can reverse your decision.

Heron answered 15/12, 2008 at 19:40 Comment(6)
Thanks! Excelent example. Which kinds of tampering would be overlooked /bypassed by this check?Scopolamine
This code won't detect a situation where the attacker has changed your copy of the public key token to match his own public key. And of course it can't detect tampering where the code itself is prevented from running.Heron
@RoadWarrior: Great stuff. I'm going to have to think about this again.Bedevil
This requires .NET 3.5. For a Windows 8 machine that has .NET 4 and newer, how can this code be adapted to verify the strong name signature?Hippodrome
@Igor, I've updated my answer to answer your question.Heron
Expected public key token check + StrongNameSignatureVerificationEx with "wasVerified" must be true. This even works with strong naming bypass. MSDN states: pfWasVerified is also set to false if the verification was successful due to registry settings.Trickle
M
12

Authenticode relies on a third party certificate authority for certificate validation. Strong naming works like a self-signed certificate and can be treated as such. It does use standard digital signatures, but the problem lies in verifying the public key of the assembly author is indeed valid. If you get it separately via a trusted channel from the author and you trust that channel, then yes, you can verify it just like a self-signed certificate.

As long as you are sure the strong name private key is kept safe by the author and you know author's public key, you can make sure it's not tampered with (to the extent you can make sure a digitally signed email is not tampered with). By the way, don't get me wrong: the quote is completely true and an attacker can easily resign the assembly or remove the existing signature. However, the resulting assembly will have a **different* digital signature that can be checked against the original (if you have the original public key).

In this case, it's similar to a self-signed certificate. If you can somehow be sure of the author's public key, you can verify the authority. However, unlike Authenticode which relies on a certificate authority, there is no straightforward, system-defined, way to distribute the public key.

Measure answered 15/12, 2008 at 18:27 Comment(2)
But the verification that checks whether the public key is the one you know is valid, has to be made mannualy. Isn't it?Scopolamine
Yeah, we're talking theoretically about this fact. In practice, it's not suggested to use strong name for these purposes. It's a strong name after all, not strong signature. But theoretically, it's possible and pretty safe way to accomplish what I said, just like GPG signature or so..Measure
B
1

I think that strong names are useful for versioning and can be used to help with setting trust levels for Code Access Security Edit: but you can't use them to verify the assembly is authored by someone trusted or by a specific author. See comment from @RoadWarrior and @RoadWarrior's answer to this question.

Strong naming your assembly does not make it tamper-proof.
Edit: See comments from @RoadWarrior and @divo. If your application is checking the original private key from the author of the assembly and forcing strong name verification my statement is incorrect. If however the attacker has access to all the assemblies in your application and/or you are using out of the box strong name verification as provided by free from the CLR then I stand by what I said.

This can be subverted by a determined attacker.

I read a discussion some time ago about strong names and security which led me to believe that for assemblies we produce, authenticode was a better guarantee for our customers that the assembly came from a trusted source.

Bedevil answered 15/12, 2008 at 18:40 Comment(2)
> Strong naming your assembly does not make it tamper-proof. < I think that is not true when you know (and check) the public key of the original author, because the tampered assembly will have a different signature. Trusted MS-Office add-ins written in .NET rely on that fact, btw.Fritz
Hamish, you can use the strong name to verify that an assembly was created by a specific author, because although an attacker can tamper with the assembly, it's impossible for him to re-use the original assembly's strong name.Heron
E
0

I believe there is a way to use strong name for the purpose of "Trust". I understand Microsoft only recommends strong name to ensure assembly contents have not been modified and suggests using "Authenticode" for trust.

But if the loader application (the application which loads these assemblies/programs) maintains an Encrypted list of "Assemblies" which it can load; wouldn't that solve the "Trust" problem?

For example, the package loader can maintain assembly name with public keys and loads the assembly/program via full assembly name?

Ethylene answered 22/7, 2016 at 18:27 Comment(0)
T
0

If you want to integrity check your assembly, you have to make the public key token check (see above "HTTP 410"'s answer) and a check with StrongNameSignatureVerificationEx (https://learn.microsoft.com/de-de/dotnet/framework/unmanaged-api/strong-naming/strongnamesignatureverificationex-function). Both the result of StrongNameSignatureVerificationEx and the out-parameter pfWasVerified must be true. Just use one signing key file (.snk) for all your assemblies.

Better would be a good .NET assembly obfuscator like BabelFor.Net or so. Many of them have a built-in anti-tampering check.

Cheers

Trickle answered 22/10, 2020 at 3:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.