Code signing How-To

What is code signing and why is it required?

Windows

Code signing provides benefits to application security features like Windows Defender Application Control (WDAC). It allows the system to cryptographically verify that a file hasn’t been tampered with before any code is to run.

Windows doesn’t require software developers to digitally sign their code. Users can install applications from sites other than the Microsoft Store if they allow such applications to run. When code sign is provided, Windows reduces the number of warnings when running.

macOS

App code signing process in macOS

“On devices with macOS 10.15, all apps distributed outside the App Store must be signed by the developer using an Apple-issued Developer ID certificate (combined with a private key) and notarized by Apple to run under the default Gatekeeper settings. Apps developed in-house should also be signed with an Apple-issued Developer ID so that users can validate their integrity.”

Starting with OmegaT 6.0.2, OmegaT is distributed as a notarized application for both the old Intel CPU machines and the new Apple CPU machines (starting with M1).

How to obtain a certificate?

A certificate can be obtained from certification authority companies. Both Microsoft and Apple specify which companies are compatible with their respective platforms.

Windows

Many certification authority companies provide a certification for code signing for Windows.

Certum Open Source developer certificate program

Certum® is one of the certification authority services provided by Asseco Ltd.

They provide discounted a code signing certificate to FOSS projects. They not only check developer individual identities but also check the project itself.

Comodo certificate for individuals

Comodo provides a certification with affordable prices for individuals.

macOS

Apple is the only authority that provides a certification for code signing and notarization. The certification comes with an Apple Developper account.

Certificate issued by Apple

A developper account comes with an Apple verified developper ID.

Application notarized by Apple

Apple provides the notarization service based on the Apple verified developper ID.

Tools

Windows

signtool.exe

signtool.exe is a utility bundled with Windows SDK. SignTool is a command-line tool that digitally signs files, verifies the signatures in files, and time stamps files.

For more information, see SignTool documentation.

Installation

Option 1: Windows SDK

Download the latest Windows SDK (minimum version: 10.0.22621.0). After installation, signtool.exe is located at:

C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe

Option 2: NuGet package (PowerShell)

Download nuget.exe:

Invoke-WebRequest -Uri https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile .\nuget.exe

Download and extract the Windows SDK Build Tools:

.\nuget.exe install Microsoft.Windows.SDK.BuildTools -x

signtool.exe will be found under the extracted package directory.

Signing an installer executable
signtool sign /fd sha256 /n "Open Source Developer: Your Name" /t http://time.certum.pl/ /v OmegaT_6.2.0_Beta_Windows_64.exe

The command will ask you password on the terminal.

Key options:

Option

Description

/fd sha256

File digest algorithm (SHA256 required for modern SDK versions)

/n "..."

Subject name of the signing certificate

/t URL

Timestamp server URL

/tr URL

RFC 3161 timestamp server URL (alternative to /t)

/td sha256

Digest algorithm for RFC 3161 timestamping (required with /tr)

/v

Verbose output

To sign multiple files in a single session (avoids re-entering the PIN for each file), pass them as consecutive arguments:

signtool sign /fd sha256 /n "Open Source Developer: Your Name" /t http://time.certum.pl/ /v OmegaT_6.2.0_Beta_Windows_64.exe OmegaT_6.2.0_Beta_Windows_64_Without_JRE.exe

To verify a signed file:

signtool verify /pa OmegaT_6.2.0_Beta_Windows_64.exe

Linux

osslsigncode

osslsigncode is a small tool that implements part of the functionality of the Microsoft tool signtool.exe — specifically Authenticode signing and timestamping. It is based on OpenSSL and cURL, and compiles on most platforms where those exist.

See osslsigncode on SourceForge for more information.

Note: Starting from November 2022, certification providers require private keys to be stored on a Hardware Security Module (HSM). Since June 1, 2023, industry standards mandate that private keys for standard code signing certificates be stored on hardware certified as FIPS 140 Level 2, Common Criteria EAL 4+, or equivalent. (See bug #1179.) For PKCS#11 HSM-based signing, osslsigncode version 2.5 or later is required.

Installation

On Debian/Ubuntu:

sudo apt install osslsigncode

For PKCS#11 HSM signing with OpenSSL 3, use an up-to-date osslsigncode version. osslsigncode 2.10 and later support OpenSSL 3 PKCS#11 providers; osslsigncode 2.13 has been tested successfully with Certum common-profile tokens.

If the distribution package is older, build a recent version from source:

sudo apt install git build-essential cmake pkg-config libssl-dev libcurl4-openssl-dev
git clone https://github.com/mtrojnar/osslsigncode.git
cd osslsigncode
git checkout 2.13
mkdir build
cmake -S . -B build
cmake --build build
./build/osslsigncode --version

Install the OpenSSL 3 PKCS#11 provider package:


sudo apt install pkcs11-provider

Check where the provider module was installed:


ls -l /usr/lib/x86_64-linux-gnu/ossl-modules/

On Debian/Ubuntu, the provider module is typically:


/usr/lib/x86_64-linux-gnu/ossl-modules/pkcs11.so
Finding the certificate label and PKCS#11 module

When you obtain a certificate from Certum stored on a USB hardware token, use the proCertum Card Manager application to find the certificate label (used as the -pkcs11cert argument):

  1. Open proCertum Card Manager.

  2. Select your USB card reader.

  3. Go to the Common Profile tab.

  4. The certificate label appears in the table shown there.

When proCertum Card Manager is installed on Linux, the PKCS#11 module for the common profile is:


/opt/proCertumCardManager/sc30pkcs11-3.0.6.72-MS.so

The exact filename depends on the installed version.

Certum may provide multiple PKCS#11 modules. Make sure to use the module matching the profile where the certificate/private key is stored. For example, the common/standard profile may appear as:


token label        : profil standardowy
token manufacturer : Certum common profile v.1

The secure profile may appear as:


token label        : profil bezpieczny
token manufacturer : Certum secure profile v.1

If the certificate is in the common profile, use the common-profile module such as sc30pkcs11-...so, not the secure-profile module.

Alternatively, use p11-kit to list all available PKCS#11 modules on your system:

p11-kit list-modules

This shows the path of each module, for example the OpenSC module at /usr/lib/x86_64-linux-gnu/pkcs11/opensc-pkcs11.so.

To inspect the token slots exposed by a PKCS#11 module:

pkcs11-tool \
    --module /opt/proCertumCardManager/sc30pkcs11-3.0.6.72-MS.so \
    --list-slots

To list certificate and key objects on the token:

pkcs11-tool \
    --module /opt/proCertumCardManager/sc30pkcs11-3.0.6.72-MS.so \
    --login \
    --list-objects

Look for matching certificate and private-key objects. They should have the same ID, for example:

Private Key Object; RSA
  label:
  ID:         a3f8c2e701b45d9682f3a07c14e8d53b2c916f74

Certificate Object; type = X.509 cert
  serial:     3F8A2D174C956B0E8912A4F7E63B5D29
  ID:         a3f8c2e701b45d9682f3a07c14e8d53b2c916f74

The PKCS#11 ID is not the certificate serial number. It is binary object identifier data. In a PKCS#11 URI it must be percent-encoded byte by byte:

a3f8c2e701b45d9682f3a07c14e8d53b2c916f74

becomes:

%A3%F8%C2%E7%01%B4%5D%96%82%F3%A0%7C%14%E8%D5%3B%2C%91%6F%74
Signing an installer executable

For OpenSSL 3 provider-based signing, create a local OpenSSL configuration file that loads both the default provider and the PKCS#11 provider, and points the PKCS#11 provider to the Certum common-profile module:

openssl-pkcs11.cnf:

openssl_conf = openssl_init

[openssl_init]
providers = provider_sect

[provider_sect]
default = default_sect
pkcs11 = pkcs11_sect

[default_sect]
activate = 1

[pkcs11_sect]
module = /usr/lib/x86_64-linux-gnu/ossl-modules/pkcs11.so
pkcs11-module-path = /opt/proCertumCardManager/sc30pkcs11-3.0.6.72-MS.so
pkcs11-module-load-behavior = early
activate = 1

Then sign the Windows installer. The certificate can be supplied as a PEM file, while the private key is used from the token through a PKCS#11 URI:

OPENSSL_CONF=$PWD/openssl-pkcs11.cnf \
osslsigncode sign \
    -provider default \
    -provider /usr/lib/x86_64-linux-gnu/ossl-modules/pkcs11.so \
    -certs 3f8a2d174c956b0e8912a4f7e63b5d29.pem \
    -key 'pkcs11:token=profil%20standardowy;id=%A3%F8%C2%E7%01%B4%5D%96%82%F3%A0%7C%14%E8%D5%3B%2C%91%6F%74;type=private' \
    -pass 'token-password' \
    -t http://time.certum.pl/ \
    -n "OmegaT" \
    -i https://omegat.org/ \
    -h sha256 \
    -in OmegaT_6.2.0_Beta_Windows_64.exe \
    -out OmegaT_6.2.0_Beta_Windows_64_signed.exe

Key options:

Option

Description

OPENSSL_CONF

OpenSSL configuration file that enables the PKCS#11 provider

-provider default

Loads OpenSSL’s default provider; required for normal file handling

-provider .../pkcs11.so

Loads the OpenSSL 3 PKCS#11 provider

-certs

Path to the signing certificate file (PEM format)

-key

PKCS#11 URI identifying the private key on the token

-pass

PIN/password for the token

-t URL

Timestamp server URL

-n

Description/name displayed in Windows security dialogs

-i URL

URL of the application’s website

-h sha256

Hash algorithm for signing

-in

Input unsigned executable

-out

Output signed executable

Avoid the older -pkcs11module engine-based flow for this setup. With some OpenSSL 3, engine_pkcs11, and Certum module combinations, the engine-based flow may fail or crash. The OpenSSL 3 provider-based flow shown above has been tested successfully.

To verify a signed file:

osslsigncode verify OmegaT_6.2.0_Beta_Windows_64_signed.exe
Troubleshooting PKCS#11 signing

If osslsigncode reports that the pkcs11 engine cannot be loaded, the older engine-based flow is being used. For OpenSSL 3 provider-based signing, use osslsigncode 2.10 or later and pass the -provider options shown above.

If OpenSSL reports an error such as unregistered scheme: scheme=file, make sure the default provider is loaded:

-provider default

If OpenSSL reports that the token is not recognized, check that the OpenSSL PKCS#11 provider is configured to use the Certum module, not OpenSC or another default module:

pkcs11-module-path = /opt/proCertumCardManager/sc30pkcs11-3.0.6.72-MS.so

If the token exposes multiple profiles, verify the selected module with:


pkcs11-tool \
    --module /opt/proCertumCardManager/sc30pkcs11-3.0.6.72-MS.so \
    --list-slots

If the certificate label is empty or differs from the certificate subject, use the PKCS#11 object ID instead of the label. The object ID must match between the certificate and private key.

If needed, test raw token signing independently of osslsigncode:

printf test > test.bin
pkcs11-tool \
    --module /opt/proCertumCardManager/sc30pkcs11-3.0.6.72-MS.so \
    --login \
    --id a3f8c2e701b45d9682f3a07c14e8d53b2c916f74 \
    --sign \
    --mechanism SHA256-RSA-PKCS \
    --input-file test.bin \
    --output-file test.sig

If this succeeds, the token, PIN, private key, and PKCS#11 module are working; remaining problems are likely in the OpenSSL/osslsigncode configuration.

batch signing

Here is a sample shell script to accept multiple *.exe and write *_signed.exe

#!/usr/bin/bash
set -euo pipefail

OPENSSL_CONF=openssl-pkcs11.cnf
export OPENSSL_CONF

if [[ $# -eq 0 ]]; then
    echo "Usage: $0 <file.exe> [file2.exe ...]" >&2
    exit 1
fi

read -rsp "PIN: " PIN
echo

for infile in "$@"; do
    outfile="${infile%.exe}_signed.exe"
    echo "Signing: $infile -> $outfile"
    osslsigncode sign \
        -provider default \
        -provider /usr/lib/x86_64-linux-gnu/ossl-modules/pkcs11.so \
        -certs 3f8a2d174c956b0e8912a4f7e63b5d29.pem \
        -key 'pkcs11:token=profil%20standardowy;id=%A3%F8%C2%E7%01%B4%5D%96%82%F3%A0%7C%14%E8%D5%3B%2C%91%6F%74;type=private' \
        -pass "$PIN" \
        -t http://time.certum.pl/ \
        -n OmegaT \
        -i https://omegat.org \
        -h sha256 \
        -in "$infile" \
        -out "$outfile"
done

OpenSC

OpenSC is an open source smart card tools and middleware. It supports PKCS#11/MiniDriver/Tokend.

The list of supported hardware is at OpenSC Wiki.

openssl-pkcs11

macOS

The tools and processes are described here:

Notes regarding the notarization of the macOS package