18

I have a C#/.NET 4.5 x64 project in Visual Studio 2013. More than one developer works on this project, so the code is managed in Git. I'm signing the compiled .dlls and .exe with signtool.exe. My company bought a code signing certificate, and if I sign it manually from the command line like so:

signtool.exe sign /f cert.p12 /p "password" "compiled.dll"

...then it looks like everything's great: I get a success message and properties of the compiled DLL in Windows Explorer show it as properly signed. So, I have no problem with the actual signing process.

But, the certificate and its password must not live in Git. They will be provided out-of-band to all developers on the project. I can make the assumption that every developer when he's building the project will have the certificate stored in a predefined location on his computer and he'll know the password for it.

So, here's my question: how can I configure Visual Studio 2013 to automatically sign its compiled output without keeping the certificate or its password in Git? I want it to be as simple as, once the developer has the certificate in a predefined path (or imported, or whatever), and working on the assumption that the developer knows the password for the certificate, that clicking "Build" in Visual Studio 2013 just builds and signs it, no questions asked.

If the signing process can be non-interactive (no password prompt), that's a bonus. Eventually this will be part of a continuous integration (CI) server that may sign its output too, and because it's automated, nobody will be there to enter the password. However, I'll take any solution for now.

My certificate is a PKCS #12 format and is protected with a password. Windows claims it's not marked for export.

user3466413
  • 805
  • 1
  • 7
  • 16

6 Answers6

17

A different way is to import the certificate in each developers private certificate store and then use the thumbprint with signtool like this:

signtool ... /sha1 'hex thumbprint' ...

Then you only need the password during the initial import of the certificate and not during builds.

  • This approach works well and solved several security issues I had with the code signing. Now my projects do not need the certificate or the password. I just install it in the certificate store and use the thumbprint everywhere instead. Thanks for the tip!, very good advice. – Sie Raybould Dec 09 '17 at 08:10
15

A solution I've used before is similar to @Mikko's answer, but it's split into two pieces:

  1. A local non-controlled script that just sets an environment variable containing the password. This is the file you give to each developer.

    @echo off
    set SIGNPASS=whatever
    
  2. A source-controlled script that calls the previous script and the does the actual signing.

    @echo off
    setlocal
    call "C:\local\signing_password.bat"
    "C:\toolpath\signtool.exe" sign /f "c:\certpath\cert.p12" /p "%SIGNPASS%" "%1"
    endlocal
    

The setlocal/endlocal pair ensure that the password doesn't leak into the environment if the script is run manually.

The "%1" is the path to the executable passed as a script parameter in the Post Build step. ..

RobertV
  • 28
  • 4
devstuff
  • 8,277
  • 1
  • 27
  • 33
  • I didn't think of splitting it that way; I had ideas floating around about using a text file, but an environment variable seems much better. Thank you; I'll try this tomorrow. – user3466413 Sep 30 '14 at 04:31
  • 2
    You're missing the "sign" command after your path to signtool – scone Dec 17 '15 at 14:17
8

I am unable to add to Daniel Schlößer's answer - which really is the best answer in my opinion. Once the certificate has been added to the certificate store then the password and pfx are not needed for signing.

However I wanted to add the following comments:

  1. The certificate has to be correctly installed in the certificate store (I used the .pfx and the password)
  2. signtool.exe must support the /sha1 command line option (I'm using 10.0.16299)
  3. For my certificate it took me ages to realize that I needed to use the /sm switch to specify the 'Machine Store' instead of the 'User Store'
  4. The Thumbprint is found in the Details tab of the Certificate properties
  5. Despite another post I read, the Thumbprint hash is not case sensitive

So here's my command line that works for a SHA256 (hash has been changed):

signtool.exe sign /sm /debug /v /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 /sha1 65cc2e77dc52b99d46e7caae2377bb5d7d9384b2 "D:\app.msi"
RonnieScotland
  • 132
  • 2
  • 5
  • This also fixed my problem, you have to use both `/fd sha256` and `/sha1 `. I found that unintuitive at first. – Lennart Jul 07 '21 at 07:12
4

You could add a batch file in your Project directory, for example sign.bat.

@echo off
<path>\signtool.exe /f cert.p12 /p "password" "compiled.dll"
echo Signed with certificate

Add the file to your .gitignore but do not add it to Visual Studio project.

In your project's properties, call the batch as post-build event.

enter image description here

Mikko Viitala
  • 8,344
  • 4
  • 37
  • 62
2

As a newer alternative to these, I have created a code signing service that individuals and organizations can deploy into their Azure environments that can help automate code signing while ensuring the private keys never leave Key Vault's HSM.

Details are on the project page here: https://github.com/onovotny/SignService

Claire Novotny
  • 1,593
  • 1
  • 15
  • 19
0

Invoking signtool from VS2015 did not work for me when I supplied the password in the form "$(SIGNPASS)" but it DID work when you use the notation "%SIGNPASS%".

Checking how the complete signtool command was formed in the VS output Window the commands look the same but under the hood there is some difference.

Remember to double quote paths to protect yourself against paths that contain spaces.

CodeNotFound
  • 22,153
  • 10
  • 68
  • 69
Peter P
  • 31
  • 1
  • $(SIGNPASS) would be interpreted as a visual studio macro, you need to add it to a props file in order to use it. You can then make the macro as environment variable too as there is a flag on the props file to be used for it. – Robson Apr 24 '18 at 10:43