Code Signing¶
Tugger has support for automatically performing code signing when evaluating Starlark configuration files.
Various platforms and distribution channels enforce requirements that binaries and other artifacts are cryptographically signed by a trusted certificate.
For example:
On Windows, executables and installers must be signed by a trusted certificate to avoid warnings about running untrusted applications.
On macOS, executables, pkg installers, and more need to be signed by a trusted certificate or Gatekeeper (read: the OS) may refuse to run them.
Tugger’s support for automatic signing enables you to meet these requirements with hpoefully minimal effort.
Code Signing Support¶
Tugger supports signing the following signable entities:
PE binaries. This is the file executable format in use on Windows platforms.
MSI installers. This is a common file-based installer format on Windows.
Mach-O binaries. This is the file executable format in use on Apple platforms.
Apple application bundles. e.g.
My Program.app
directories. Bundles are a common application packaging format on Apple platforms.
Signing on Windows currently uses Microsoft’s signtool.exe
to perform the
signing. So signing Windows entities requires access to this tool. (We have plans
to implement equivalent functionality in Rust to avoid this dependency.)
Signing Apple formats uses a pure Rust implementation of the code signing
functionality and works on any machine. Apple’s codesign
tool or access
to Apple hardware is not required to sign Apple entities.
Code signing requires the use of a code signing certificate. See Understanding Code Signing Certificates for more.
Tugger supports using code signing certificates in the following locations:
From a PFX / PKCS #12 file. (e.g.
.pfx
or.p12
files.)Certificates available in the Windows certificate store. Via the Windows certificate store, certificates stored in hardware devices (such as HSMs and hardware tokens such as YubiKeys) can also be used.
Configuring Code Signing in Starlark¶
Code signing needs to be explicitly enabled and configured in your Starlark configuration file.
From a high level, here’s how it works:
Your Starlark configuration instantiates, configures, and enables a
CodeSigner
, which is the entity that performs code signing.As your configuration file is evaluated, actions that produce or encounter signable entities (such as creating Windows MSI installers) interact with registered
CodeSigner
instances and attempt code signing.
Tugger abstracts away a lot of the complexity around code signing, such as figuring out which files need to be signed (it looks at the content of files and determines if a file is signable). So in many cases, all you need to do is tell Tugger where your code signing certificate is and it can do the rest!
Continuing reading for details on how to customize code signing. Or just straight into Code Signing Examples.
Instantiating CodeSigner
to Perform Code Signing¶
To perform code signing, first instantiate a CodeSigner
via one
of its available constructor functions:
code_signer_from_pfx_file()
is the most versatile method, as it
gives Tugger full access to the signing certificate and private key. However,
this method is arguably the least secure, as it requires the private key to
exist in a file and Tugger holds the decrypted private key in memory during
signing. Both of these make the private key much more susceptible to being
accessed by unwanted parties. If you are paranoid about security, you should
only use this method on machines that you trust.
The code_signer_from_windows_
functions reference code signing keys stored in
the Windows certificate store. Signature requests are processed through the
Windows APIs and the private key never leaves the control of the Windows
certificate store, helping to keep the private key secure.
Important
Constructed CodeSigner
instances must be activated in order
to automatically perform code signing. See Activating Automatic Code Signing
for more.
Configuring CodeSigner
Instances¶
Once you’ve obtained a CodeSigner
, you may need to register
additional settings to influence signing.
Registering the Issuing Certificate Chain¶
Produced signatures should often contain details about the chain of certificates that issued the code signing certificate. See Understanding Code Signing Certificates for more on this topic.
You may need to tell CodeSigner
about the existence of
these certificates.
When using a code signing certificate backed by the Windows certificate store, you do not need to register the certificate’s signing chain.
When using a code signing certificate backed by a PFX file, you need to register the certificate chain, even if those X.509 certificates are in the PFX file (we don’t yet support reading these from the PFX file).
CodeSigner.chain_issuer_certificates_pem_file()
is the most versatile method to register issuer certificates, as it works on all platforms and PEM is a very widespread format for storing X.509 certificates.On macOS,
CodeSigner.chain_issuer_certificates_macos_keychain()
can be called to attempt to resolve the certificate chain by speaking directly to the macOS keychain APIs. This requires that the signing certificate be accessible in the current user’s keychain and its entire issuing chain to be present in that keychain.
Influencing Signing Operations¶
CodeSigner
instances have the opportunity to influence individual
signing operations. This gives you significant control over how signing is
performed.
CodeSigner.set_signing_callback()
registers a function that will be
invoked on each attempted signing operation. This callback function receives
an argument - a CodeSigningRequest
instance - that describes
the entity capable of being signed. This type exposes functionality
for influencing the signing operation. For example:
Setting
CodeSigningRequest.defer
toTrue
will opt thisCodeSigner
out of signing this particular entity.Setting
CodeSigningRequest.prevent_signing
toTrue
will prevent this and otherCodeSigner
from signing this entity.
See the CodeSigningRequest
API documentation for all available
functionality on this type.
Leveraging custom callback functions enables configuration files to employ
arbitrarily complex logic for influencing code signing. Your main constraint
are the settings exposed on CodeSigningRequest
. If you find
yourself needing a setting that doesn’t exist, please file a feature request!
Activating Automatic Code Signing¶
A CodeSigner
needs to be activated for automatic use
by Tugger. i.e. your signable files won’t be signed as your Starlark
configuration file is evaluated unless a CodeSigner
is
activated.
To activate your CodeSigner
, simply call
CodeSigner.activate()
.
Code Signing Actions¶
Various activities within the evaluation of your Starlark configuration file trigger the assessment of - and possible performing of - code signing.
Each unique activity has its own string action name describing it.
This name is accessible via CodeSigningRequest.action
, enabling
callback functions to key off of it. For example, you may want to not
sign during certain operations.
The following named actions are defined by Tugger:
file-manifest-install
Used when a
FileManifest
is materialized on the filesystem through an action likeFileManifest.install()
.macos-application-bundle-creation
When a macOS Application Bundle is created by Tugger.
This will be triggered by
MacOsApplicationBundleBuilder.build()
.windows-installer-creation
When a Windows installer file is created by Tugger.
Methods like
WiXMSIBuilder.build()
andWiXBundleBuilder.build()
will trigger this action.windows-installer-file-added
When a file that will be installed is added to a Windows installer.
Triggered by
WiXMSIBuilder.add_program_files_manifest()
,WiXInstaller.add_install_file()
, andWiXInstaller.add_install_files()
.
Other applications extending Tugger’s core functionality may define their own actions.
Duplicate Events¶
It is possible for the same logical file to trigger multiple signing events
as it is processed. For example, MacOsApplicationBundleBuilder.build()
may trigger an event for macOS Application Bundle generation then a later
action loads the bundle files into a FileManifest
and materializes
them somewhere else via FileManifest.install()
, which would
trigger an additional signability check.
As a result, the same file or entity may be signed multiple times.
If this behavior is undesirable, the use of a custom callback function can be used to choose which signing requests to respond to.
Unfortunately, we do not yet expose metadata on CodeSigningRequest
indicating if a file is signed or not. This would likely be the obvious
attribute to filter against. This feature is tracked at
https://github.com/indygreg/PyOxidizer/issues/400.
Code Signing Examples¶
Automatically Sign all Signable Content with a Specific Certificate in the Windows Store¶
Say you have a code signing certificate in the Windows certificate store
with the SHA-1 thumbprint deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
and
you want Tugger to sign all signable files as it runs. Here’s what you’ll
need to do in your Starlark configuration file:
signer = code_signer_from_windows_store_sha1_thumbprint("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
signer.activate()
As Tugger encounters .exe
, .dll
, .msi
files and any file that
it identifies as signable, it will attempt to automatically sign them!
Choosing a Code Signing Certificate Dynamically¶
Say you have multiple code signing certificates but want to parameterize
which one to use. We can do that through the use of the VARS
global
dict, which holds settings passed in via the command line.
PFX_PATH = VARS.get("PFX_PATH")
PFX_PASSWORD = VARS.get("PFX_PASSWORD", "")
# This needs to be in its own function because Starlark doesn't allow `if`
# at the file/module scope.
def make_code_signers():
if PFX_PATH:
signer = code_signer_from_pfx_file(PFX_PATH, PFX_PASSWORD)
signer.activate()
# Don't forget to call the function!
make_code_signers()
Then when running the configuration file, specify an extra variable. e.g.:
$ pyoxidizer --var PFX_PATH /path/to/certificate.pfx --var PFX_PASSWORD hunter2
Or you could use functions like prompt_confirm()
, prompt_input()
,
and prompt_password()
to ask the user which certificate to use.
def make_code_signers():
if prompt_confirm("enable code signing?", default=False):
pfx_path = prompt_input("enter path to PFX file:")
pfx_password = prompt_password("enter path to PFX password:", confirm=True)
signer = code_signer_from_pfx_file(pfx_path, pfx_password)
signer.activate()
make_code_signers()
Selectively Ignoring Files to Sign¶
It is common to want to ignore certain files from signing. For example, you may ship a pre-built binary that already has a valid code signature. Here’s how you can do that.
# Define a function that will be called for every signing request that
# can influence operation.
def code_signer_callback(request):
# Match a known filename that doesn't need signed and set
# `prevent_signing = True` to prevent it from being signed.
if request.filename == "vcruntime140.dll":
request.prevent_signing = True
signer = code_signer_from_windows_store_sha1_thumbprint("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
signer.set_signing_callback(code_signer_callback)
signer.activate()
You could even use the prompt_confirm()
function to prompt whether
to sign each file:
def code_signer_callback(request):
request.prevent_signing = not prompt_confirm("sign %s?" % request.filename)
signer = code_signer_from_...()
signer.set_signing_callback(code_signer_callback)
signer.activate()
Understanding Code Signing Certificates¶
A code signing certificate consists of a secure, private key and a public certificate that describes itself to others. These components are strictly separate but are often represented and stored together.
The public certificate is an X.509 certificate, much like those used in HTTP to identify web sites. The main difference is that the certificate’s subject describes a person or organization (instead of a website) and the certificate contains attributes that denote it for use by code signing.
Like web site X.509 certificates, code signing certificates are signed by another X.509 certificate. This is called the issuing certificate. There is often a chain of certificates - the certificate chain - leading to a self-signed certificate (a certificate whose issuer was itself), which is referred to as the root certificate.
Typically, the certificate chain is included in code signatures. This enables readers of the signature to have full access to all relevant certificates, without an implicit dependency on them being present on the reading machine. This enables validation to be conducted more robustly.
Code Signing Certificate Storage¶
Code signing certificates can be stored in a number of formats. Here are the popular ones:
As standalone
.pfx
or.p12
files. These are files containing data as defined by the PFX and PKCS #12 specifications. Most tools that support saving code signing certificates to files support this format if not use it by default.In your operating system’s certificate store. Windows, macOS, and other operating systems have built-in functionality for storing and accessing certificates. On Windows, the
certmgr.msc
tool can be used to view certificates. On macOS,Keychain Access
is the official GUI application.
In addition, the public X.509 certificates and the certificates in the
certificate chain are often represented as PEM. This is a human-readable
text format with content like -----BEGIN CERTIFICATE-----
. PEM is actually
base64 encoded BER/DER encoding of ASN.1 data structures, but that’s not
important. What is important is public certificates are often stored in files
having this -----BEGIN CERTIFICATE-----
content. These files often have
the extension .pem
or .crt
.
The certificate chain is constant for the lifetime of a code signing certificate. So it is possible to export these certificates to a persisted file and reference this file when you need to access the issuer certificates chain.
Securing Your Code Signing Certificate¶
Your code signing certificate’s private key attests that its owner was in possession of that certificate and has vouched for the integrity of whatever it signed.
Important
Code signing certificates can be very attractive theft targets for hackers, as possession of a code signing certificate enables you to sign software that can run on other machines and appears to be trusted. Therefore, it is often important to try to secure your code signing certificates!
The most secure way to store code signing certificates is in dedicated hardware devices, such as HSMs or personal hardware tokens (such as YubiKeys). Often, the private key component of the certificate is generated directly in said hardware and it is impossible to export the private key and obtain its raw value. Instead, operations like signing are issued to the hardware and the hardware gives you the rest.
Tugger doesn’t yet support interfacing directly with hardware devices. However, we do have support for interfacing with the operating system’s certificate stores:
On Windows, a certificate in the Windows certificate store can be referenced by its SHA-1 fingerprint. (This is the preferred mechanism to reference a certificate on Windows.)
On Windows, a certificate in the Windows certificate store can be referenced by specifying a string to match against in the certificate’s subject field. (This is less precise than specifying a certificate’s SHA-1 fingerprint.)
On Windows, you can tell the signing tool to automatically find the most appropriate certificate to use. It will look for a certificate in known certificate stores. (This is the least precise of all options available on Windows.)
Note
Your operating system’s certificate store can often interface with hardware devices holding code signing certificates. So Tugger’s support for interfacing with the operating system store is often just as effective as interfacing directly with hardware devices.
For example, on Windows, certificates stored in a YubiKey will be available if you have the YubiKey Smart Card Minidriver installed.
If Tugger doesn’t support using a remote certificate, you will need to export a certificate to a file and have Tugger use that. If you export your certificate to a file, you should take care to secure that file as best you can.
File-based code signing certificates often exist in .pfx
or .p12
files.
These are often protected with a password. You should use a strong and unique
password to secure this file.
Important
If someone else gains access to the file containing your code signing certificate, they will be able to perform an offline attack using as many compute resources as possible to guess your password and gain access to the code signing certificate.
You should take the following precautions to protect file-based code signing certificates:
Choose a strong, unique password for protecting the file content.
Limit the time the files exist. If you can create the file only when needed, this is better than having the file linger on the filesystem.
Limit the number of copies of the file. Every copy of the file is an opportunity for the file to be obtained by someone else.
Exporting a Code Signing Certificate from macOS Keychain¶
Apple platforms require a code signing certificate issued by Apple to sign distributed files.
If you have an Apple-issued code signing certificate, it is likely registered
in a keychain on your machine. Tugger doesn’t currently support interfacing
directly with the macOS keychain and you will need to export your signing
certificate to a PFX / .p12
file so Tugger can use it. Here’s how to do that.
Press
command + spacebar
and search for and open theKeychain Access
application.Make sure the correct keychain is selected. The keychain code signing certificates are typically located in is the
login
keychain under theDefault Keychains
list.From the horizontal list of filters above the main pane, select
Certificates
(it is probably the last item).Find the certificate you want to export. It likely has a name like
Developer ID Application: <your name (some ID)>
Do a double finger tap, right click, or
File -> Export Items ...
to bring up the export dialog.For the file format, make sure
Personal Information Exchange (.p12)
is selected.Navigate to a folder where you want to save the file, choose an appropriate name, and click
Save
.You will be asked for a password which will be used to protect the exported items. Enter one. This password will need to be provided to Tugger later to unlock the content in the file.
You may be prompted to enter the password to the keychain to allow the key export. If so, enter that password.
You may be prompted multiple times. Just keep entering your keychain password(s) until it is done.
You are done! There should be a
.p12
file wherever you toldKeychain Access
to save it.
Important
Please see Securing Your Code Signing Certificate for important information on keeping your file-based code signing certificate secure.
Finding the Code Signing SHA-1 Thumbprint on Windows¶
On Windows, it is recommended to use code signing certificates in the Windows certificate store and to specify those certificates via their SHA-1 thumbprint, which should uniquely identify a certificate.
The Windows certificate store supports interfacing with hardware certificate stores (such as YubiKeys and other hardware devices). So this method should work with connected hardware certificate stores as well.
Press
Windows Key + r
to open theRun
panel. Type incertmgr.msc
and run that program.Code signing certificates are likely under
Personal
->Certificates
. Find that item in the tree and look for a certificate in the main pane.Find the certificate you want to use and double click on it to view its details.
Open the
Details
tab.In the table of fields, find and select
Thumbprint
.Copy the 40 character hexadecimal value that is printed.
The SHA-1 thumbprint can be fed into
code_signer_from_windows_store_sha1_thumbprint()
to construct a
CodeSigner
that uses the specified certificate.
If the certificate is protected by a password or requires key to unlock, you should see prompts to do that as Tugger attempts to sign things.
Exporting a Code Signing Certificate from Windows Certificate Store¶
Code signing certificates on Windows are often stored in the Windows certificate store.
Important
Tugger has support for using certificates directly in the Windows certificate store. Exporting certificates to files will likely result in a net loss of security.
Here is how you can export a certificate to a PFX file.
Press
Windows Key + r
to open theRun
panel. Type incertmgr.msc
and run that program.Code signing certificates are likely under
Personal
->Certificates
. Find that item in the tree and look for a certificate in the main pane.Double click on the certificate you want to export, open its
Details
table, and click theCopy to File...
button. This should open the Certificate Export Wizard.Click
Next
.Make sure
Yes, export the private key
is selected and clickNext
.For the format, make sure the selected value is
Personal Information Exchange PKCS #12 (PFX)
. For the checkboxes, checkInclude all certificates in the certificate path, if possible
. Then clickNext
.You should be prompted for a password. Enter a secure, unique password. In the
Encryption
drop-down, ensureTripleDES-SHA1
is selected (we don’t yet supportAES256-SHA256
). Then clickNext
.Select a filename and click
Next
.Click
Finish
to close the wizard.
Important
Please see Securing Your Code Signing Certificate for important information on keeping your file-based code signing certificate secure.