21

My scenario is we have one program (exe) that will start other programs if found in a particular folder. I want to ensure it only ever starts programs which are signed with our Corporate certificate (Verisign approved etc). Essentially then it will only start the programs with the same certificate as itself. I don't want to ship the certificate itself.

I've been searching the web and the system namespace and haven't found a clear example that reads the certificate data from a file and also validates it, and can check against another file. The closest I've found is Signtool and and having this verification in a separate exe is kind of point less. I know the Strong Naming stuff wont help because a digitally signed file is different as helpfully explained here (http://blog.codingoutloud.com/2010/03/13/three-ways-to-tell-whether-an-assembly-dl-is-strong-named/) Also some other examples in SO showing encryption and verification of raw data but not an assembly where it's packaged up together in some way.

Any ideas or suggestions?

Aaron
  • 579
  • 1
  • 4
  • 12

4 Answers4

18

Here's a blog post with code samples on how to verify assembly signatures:
http://blogs.msdn.com/b/shawnfa/archive/2004/06/07/150378.aspx

The code sample at the end shows how to verify if an assembly was signed by Microsoft or not - you can do the same by getting the certificate token for your company's certificate(s).

Update: user @Saber edited this with the following update, but that update was rejected by others. However, it is very valid advice, so I am reposting his/her edit since SO won't let me approve it:

Edit (thank you, OP): If you want to do this more securely (i.e. make your program more tamper-proof), reference an assembly in your program which is strongly named with the relevant key, then use the token of the referenced assembly to compare with the token of the calling assembly. If you use a byte arrays (as per the link), it can simply be hex edited and changed.

KristoferA
  • 12,287
  • 1
  • 40
  • 62
  • 4
    Why the downvote? _Read_ the blog post I linked to, and you will see that it indeed shows how the strong naming links into assembly signing, and that you _can_ use that to verify if an assembly has been signed with a specific certificate. – KristoferA Feb 17 '11 at 02:59
  • Yes this works well and it directly answers my question about verifying the signature without pitching a product (though a comprehensive one). It would be nice if this could read the details about the certificate from any file signed with Signtool not just .NET assemblies. (Eg. Install Shield setup.exe and MSIs etc) I was looking at the Windows API class but couldn't find anything on it to say who it was signed by. This is good enough though. I probably don't need to check the cert owner, if a client has approved the cert or it's authority then it's on their head anyway. – Aaron Feb 17 '11 at 22:35
  • @Aaron your understanding of the certificates is flawed. If you don't validate the certificate - throw away your checks and don't bother implementing them. No security is better than broken security as the latter gives false feeling of safety. – Eugene Mayevski 'Callback Feb 18 '11 at 20:48
  • @Kristofer the blog article doesn't say a word on certificates. – Eugene Mayevski 'Callback Feb 18 '11 at 20:48
  • 2
    I am validating that the certificate is trusted on the clients machine with the API call. How do I check the Public Key Token without loading the assembly into the current application domain? There doesn't appear to be a way to do this with the .Net framework. – Aaron Feb 24 '11 at 06:10
  • Easier than I thought, just need to use: var asmName = AssemblyName.GetAssemblyName(assemblyPath); – Aaron Feb 24 '11 at 06:21
  • The reference code is insecure, as the bytes used to compare with the assembly token can be tampered with to match any signed key the attacker wants to use. This method is helpful if you want to compare the public key token with the currently running assembly, but you need a tamper-proof way to access microsoft's public key token if you want to verify the calling assembly is from microsoft. – Jordan Morris May 20 '13 at 09:44
  • @Eugene Mayevski 'EldoS Corp Are you implying the public key token can be forged? What insecurity exactly are you alluding to here? – Jordan Morris May 20 '13 at 09:51
  • @Saber Thanks, very valid point, and I agree with your edit to the post. Unfortunately, SO's edit approval thing wouldn't let me approve it after it had been rejected by others, so I had to re-post your edit to get it through. – KristoferA May 20 '13 at 17:43
  • 2
    The linked blog post is certainly helpful, but I cannot in good conscience upvote a pure link-only answer. A full duplication of the contents of the blog post are not necessary, but at the very least this answer should contain a mention of the technique used (calling StrongNameSignatureVerificationEx), allowing future visitors to research the solution independently should they be unable to access the link for any reason. – jmbpiano Oct 30 '18 at 19:47
  • 3
    @jmbpiano Highly relevant as of 2020 as it seems to be gone. – Joel Roberts Jan 14 '20 at 21:24
  • 3
    Here is the last valid time it was indexed by the Wayback Machine: https://web.archive.org/web/20151223032841/http://blogs.msdn.com/b/shawnfa/archive/2004/06/07/150378.aspx – kjbartel Feb 17 '20 at 05:28
3

There exist two signing technologies for .NET assemblies: strongnaming and Authenticode (authenticode is used to sign PE and some other files, not just .NET assemblies). They are used for different purposes. Certificates are used in Authenticode only to authenticate the author. Strongnaming doesn't authenticate the author at all.

Besides checking the signature, the certificate must be validated to ensure that it was issued for given author. Proper validation is a complex procedure that involves CRL (certificate revocation list) and OCSP (online certificate status) checking.

To perform verification of Authenticode signature you need Authenticode verification component. One of the options is to use PKIBlackbox package of our SecureBlackbox product. The package includes Authenticode verification as well as complete certificate validation mechanisms.

Note that if you are not going to validate the certificate, there's no sense in verifying the signature at all, because one can create a self-signed certificate with the same Subject, Serial number etc., and use it to sign the forged assembly.

Eugene Mayevski 'Callback
  • 45,135
  • 8
  • 71
  • 121
3

You can try three option here.

1) The first one is using Assembly load like here:

Assembly myDll =
    Assembly.Load("myDll, Version=1.0.0.1, Culture=neutral, PublicKeyToken=9b35aa32c18d4fb1");

You can print the hexadecimal format of the public key and public key token for a specific assembly by using the following Strong Name (Sn.exe) command:

sn -Tp <assembly>

If you have a public key file, you can use the following command instead (note the difference in case on the command-line option):

sn -tp <assembly>

2) The second one is mentioned here. And use p/Invoke for such issue.

3) Also exists third, more agile, and more complex way for doing this. This is a Binding Policy. You can take it into account in the case when you should to provide upgrades for an application that's already deployed. When a new version of a shared component comes out that your application can benefit from, an application policy file will allow you to provide these benefits without recompiling or replacing existing installations.

You can find more about this feature here:

http://msdn.microsoft.com/en-us/library/aa309359%28v=vs.71%29.aspx

http://ondotnet.com/pub/a/dotnet/2003/03/17/bindingpolicy.html

Community
  • 1
  • 1
apros
  • 2,848
  • 3
  • 27
  • 31
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?

<PackageHandlers>
  <PackageHandler>
    <Package type="Package1" assembyName="SomeAssembly" publickey="d45755dbb8b44e59" />
  </PackageHandler>
</PackageHandlers>
Iftikhar Ali
  • 369
  • 3
  • 12