2

I'm using C# check some exe or dll file digital certificate.

Assembly asm = Assembly.LoadFrom(assembly);


Module module = asm.GetModules().First();
System.Security.Cryptography.X509Certificates.X509Certificate certificate = module.GetSignerCertificate();
// it will null if the certificate time expired
if (null != certificate)
{
     if (certificate.Subject.Contains("Company Name"))
         Console.Write("success");
     else
         Console.Write("invalid");
}
else
{
   Console.Write("failed");
}

But now problem is : If the certificate time is expired, or because modify local time to after certificate time the code module.GetSignerCertificate() always get null.

Because I just want check the assembly has digital certificate and company name, not check the time, So Is there any way can check it use other way or not let it null?

qakmak
  • 1,287
  • 9
  • 31
  • 62
  • There's some justification behind this: an expired certificate is invalid (it could have been produced with a key that has since been compromised, for example). Since you can't trust the information in it, the framework just chooses not to return it rather than give it to you with some "beware, invalid" flag you must not forget to check. Do you really need the certificate or could you get away with [`FileVersionInfo`](https://msdn.microsoft.com/library/system.diagnostics.fileversioninfo)? – Jeroen Mostert Feb 17 '15 at 08:07
  • @JeroenMostert that is not entirely correct, a module should be usable after the cert expired. The problem is that the module is incorrectly signed, it lacks a timestamp. Read my answer for details. – Remus Rusanu Feb 17 '15 at 08:19
  • @RemusRusanu, Are you sure the modules usable after the cert expired? If that's true, maybe your right. Because for test the cer expired, I change my system time. maybe that will let the timestamp validation not correct....(I'm not use a expired cert test yet, because my cert still not expired yet) – qakmak Feb 17 '15 at 09:17
  • @RemusRusanu, I just try use a cert expired .dll files. and the result is same. So timestamp is all fine. when it expired I will get ``null`` – qakmak Feb 17 '15 at 09:31
  • @JeroenMostert, at least I can check my assembly not modify by other people. So I really need it. – qakmak Feb 17 '15 at 09:45
  • @qakmak what is the output of `signtool verify /v /tw ` – Remus Rusanu Feb 17 '15 at 09:49
  • @RemusRusanu, I'm not use signtool manually, I use some package software can add digital sign for me. like : Advanced Installer. – qakmak Feb 17 '15 at 09:58
  • I'm asking you to use `signtool` to **verify** what your tool is doing. Although is obvious the tools i either not doing it correctly, or you're not using it correctly, since the evidence points toward an incorrectly signed module. Enjoy your customer calls after the signing certificate expires! – Remus Rusanu Feb 17 '15 at 10:05
  • @RemusRusanu, I just try it, and it display 1 error. ``SignTool Error: A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider.`` I'm use startssl digital cert by the way. – qakmak Feb 17 '15 at 10:18
  • 1
    For StartSSL you need to purchase their ['Extended Validation level.'](https://www.startssl.com/?app=40) certs for code signing. All other certs will expire the signature when the cert expires because they contain the "Lifetime Signing" (1.3.6.1.4.1.311.10.3.13) attribute. see http://stackoverflow.com/a/2219856/105929 – Remus Rusanu Feb 17 '15 at 10:24
  • btw, if you believe that verifying a module signing certificate subject to be 'company name' you can somehow enforce to load only your own company assemblies, you are wrong. I can use a self-signed cert and provide any subject I like, install it in the local machine root trust, and then your code will be fooled. This is much harder than you think, is DRM. – Remus Rusanu Feb 17 '15 at 10:42
  • @RemusRusanu, It's just a simple test, and I know all .net code can be decompile. there is no any way can protect it. Of course I'm very happy If you tell me a good solution for protect it. Thanks. – qakmak Feb 17 '15 at 10:57

2 Answers2

2

This code does work well in my case, including expired certificate case.

using System.Security.Cryptography.X509Certificates;
...
// "assembly" is a string of full path for .exe or .dll.
var cert = X509Certificate.CreateFromSignedFile(assembly);
jsakamoto
  • 146
  • 2
  • 6
  • See also: [http://stackoverflow.com/questions/11248554/how-to-retrieve-certificate-information-of-a-net-dll-in-c-sharp?rq=1](http://stackoverflow.com/questions/11248554/how-to-retrieve-certificate-information-of-a-net-dll-in-c-sharp?rq=1) – jsakamoto Jan 13 '16 at 14:32
1

The module is incorrectly signed. A properly signed module includes the timestamp when the module was signed. The validation rule dictates that the certificate should had been valid at the moment of signing (this, at the timestamp). Read this answer here https://stackoverflow.com/a/3428386/105929 for more details.

If this is your own module, then the best action is to modify your release sign-off to include a timestamp in the signature. Read Time Stamping Authenticode Signatures to understand how to do it (time stamping involves a URL service provided by your certificate CA). Also read Everything you need to know about Authenticode Code Signing.

As to your question: if you look at module.cs reference code, you'll see that the implementation is native and not much you can do about it to modify it:

    [System.Security.SecurityCritical]  // auto-generated
    [ResourceExposure(ResourceScope.None)]
    [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
    [SuppressUnmanagedCodeSecurity]
    static private extern void GetSignerCertificate(RuntimeModule module, ObjectHandleOnStack retData);

You could instead use the signtool.exe verify command, afaik it has options to display all certificates, including expired ones on modules lacking a proper timestamp.

The module is correct. Because before it expired, it can use very well. not get null. after it expired, or I change my system time. it will get null

This can easily be proven to be incorrect. Find an old correctly signed assembly on your machine, one distributed by a serious vendor that knows what is doing. Eg. I found this on my machine: c:\Program Files (x86)\Microsoft SQL Server\100\Setup Bootstrap\Release\x64\Microsoft.AnalysisServices.DLL. Look at the Digital Signature properties:

enter image description here

Do note the signature timestamp (20110922) and the certificate expiration (20120521).

Write a small app to get the cert using CLR API:

    static void Main(string[] args)
    {
        Assembly asm = Assembly.LoadFile(@"c:\Program Files (x86)\Microsoft SQL Server\100\Setup Bootstrap\Release\x64\Microsoft.AnalysisServices.DLL");
        Module m = asm.GetModules()[0];

        var cert = m.GetSignerCertificate();

        Console.WriteLine("{0}", cert);
    }

Is the certificate found? Yes. Is the certificate expired? Yes. Is the module properly signed, using a timestmap? Yes.

QED

Community
  • 1
  • 1
Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
  • The module is correct. Because before it expired, it can use very well. not get ``null``. after it expired, or I change my system time. it will get ``null`` – qakmak Feb 17 '15 at 09:34
  • I think maybe that's because that's from Microsoft, Microsoft has a high priority, and it's always a trusted provider. – qakmak Feb 17 '15 at 10:22
  • You statement shows that you really don't understand how PKI works. – Remus Rusanu Feb 17 '15 at 10:26
  • Yes. Sorry, If that's because ``startssl``, I think you're right. because I only have startssl digital cert to test. and I'm not a expert of PKI – qakmak Feb 17 '15 at 10:32