12
votes

I want to automate the code signing of some ClickOnce deployment artifacts - application exe's and manifests. I am using signtool to accomplish this. In an attempt to make the private key available for signing and yet protect the certificate file containing the private key (.pfx file), my plan is to install the certificate into the local machine certificate store with a non-exportable key. (I am aware that there are ways of exporting the key even if it is marked non-exportable.) The machine is a continuous integration server that will be accessible to a select few. My hope was to set it up in such a way that any time the private key needed to be used, it would require the private key password to be entered. I would then set up an automated job (using Jenkins) which would require a build parameter that would collect the private key password. The Mask Passwords plugin would be used to mask the password while being entered and in the console output.

However, I've run into a couple of roadblocks. First of all, even though there is the "Enable strong private key protection. You will be prompted every time the private key is used by an application if you enable this option." when importing a certificate, it appears to be only available when importing it into the current user store, not the local machine store. Secondly, even if this option were available, the signtool tool doesn't provide an option for setting the password when signing using a certificate in a store. The password parameter, '/p', is only applicable when using a pfx file as the source of the private key ('/f' option). Given that, this doesn't appear to be a viable option. Note: Even if "Enable strong private key protection." is available for certificates in the machine store, my testing shows that attempting to use a certificate with this option enabled just pops up a dialog asking for permission to use it, which obviously wouldn't work for an automated job. I originally thought "prompting" meant it would ask for the password.

One other option I've considered is to create ACLs to secure the private key within a certificate store. This can be done by right clicking on the certificate and selecting All Tasks... | Manage Private Keys... dialog. This would restrict the private key usage to only those authorized. (Note: When a user without permissions on the private key attempts to use it for signing, they get the message "SignTool Error: No certificates were found that met all the given criteria.") However, I don't want to give access to the credentials being used by the Jenkins build service because then any build job would be able to sign code. I could create a job that would execute a script to run the signing command as a specific user. This would require taking the domain user name and password in as build parameters. I could do this using the Jenkins Mask Passwords plugin. I don't really like that, though, because I'm not comfortable that Mask Passwords is sufficient protection against exposing the domain credentials, which, if compromised, would give access to a lot more than just the private key.

If I abandon my original idea of storing the certificate in the machine store, there is the option of placing the certificate pfx file in an ACL secured folder on the build machine that only the build process and signing users have permissions on. Doing that would allow me to create a build job to use the contained private key while not exposing the file to others that have access to the machine. To use the private key, the build parameters would need to collect the private key password.

Finally, there is the option of using a smart card for storing the certificate but we've decided against that.

So, my question is, are there any other ways to do this that 1) protects the private key from being copied, 2) prevents the private key from being used by unauthorized users to sign code and 3), given the private key password is provided by an authorized user, makes the private key for signing by build service?

3

3 Answers

5
votes

First of all I will try to answer your questions separately:

  1. Protects the private key from being copied:
    Only the cryptographic hardware (smartcard or hsm) can really protect the key from being copied. Windows certificate store (even with non-exportable option as you correctly noted) or PKCS#12 (PFX) file provide only the false sense of protection.
  2. Prevents the private key from being used by unauthorized users to sign code:
    IMO this requires user interaction such as entering password or PIN. If you pass the password as a parameter there is always a possibility that other processes would be able to gain it i.e. from process info, logs etc.
  3. Given the private key password is provided by an authorized user, makes the private key for signing by build service:
    Cryptographic hardware (smartcard or hsm) accessible via CSP (windows certificate store) with interactively entered PIN should work with signtool without any problems.

I agree that the requirement of user interaction may not be exactly convenient for an automatic build service but you will probably have to choose between secure solution with user interaction and less secure solution without user interaction.

Convenient but less secure solution no. 1: It seems to me that you have already found an acceptable solution because you did not provide any of its disadvantages.

If I abandon my original idea of storing the certificate in the machine store, there is the option of placing the certificate pfx file in an ACL secured folder on the build machine that only the build process and signing users have permissions on. Doing that would allow me to create a build job to use the contained private key while not exposing the file to others that have access to the machine. To use the private key, the build parameters would need to collect the private key password.

However please note that PFX file can be copied undetectably not only from the live system but also from the backups.

Convenient but less secure solution no. 2: Store private key on a smartcard that does not require PIN to be entered and allow system access only to the trusted users. This would ensure that your private key cannot be copied while it would remain easily accessible for signtool. However it would probably require you to have two separate build servers - one without the smartcard accessible to all users and one with the smartcard accessible only to the trusted users.

Inconvenient secure solution: Store private key on a smartcard that requires PIN to be entered and require user interaction (entering of the PIN) during the build process.

You could also consider signing development builds with self-signed codesigning certificate in an automatic mode and signing public release builds with trusted codesigning certificate in manual mode.

0
votes

The only option left that I see is to use HSM.

You could generate private key protected by operator card/cardset (for example Thales has this sort of HSMs). Operator cardset can be set with a quorum that specifies how many operators have to insert the card before the private key can be usable by application that requested use of this private key. Thales also has a CSP that supports this feature.

The problem might be that the CSP will invoke window to show user that a card has to be inserted (and optionaly password to that card entered). This problem might be solved when you run you build server under some user account with desktop that can be logged into. When you log in as this user and start the build server it has to request the use of private key (for example by signing some dummy file or something). Windows will pop up and operators (as many as required by the quorum) will one by one insert their cards and optionally enter their passwords. After all required cards have been inserted the key will be usable by your build server. Any other application (possibly started by other user) will go through same procedure to use that key. If your build server crashes (do build servers crash? ) you will go through the same procedure.

HSM have also tamper protection so the private key is IMHO safe in there. Sorry for talking about Thales HSMs only but I personally do not have experience with any other HSM.

-1
votes

It seems that relying on a password-protected PFX file, rather than the certificate store, is secure. Even without ACL protection on the hard drive folder where the PFX resides, no-one who takes the PFX file could use it to sign anything, unless they also had the password.

Then, set up a parameterized Jenkins "signing" job on that computer that fully encapsulates the PFX-password so that no-one can see it. (I am contemplating a script of some sort, Powershell maybe, converted to a black-box EXE.) Authorized Jenkins jobs could chain to the signing job to accomplish the signature. The EXE would log every time it was used, as an auditing feature. Just need to figure out how to prevent unauthorized use of the signing job...there must be a way to do that?