Client-Server Windows Codesign Tool
Go to file
Sergey Sharybin cb24c54d24 Add optional --description argument to the client
It allows to provide description of the signed file, which
is used by UAC to show name of an MSI installer.

Ref #1
2024-09-06 11:35:17 +02:00
client Add optional --description argument to the client 2024-09-06 11:35:17 +02:00
resource Add example systemd service file 2023-09-28 15:22:44 +02:00
server Cleanup: Black formatting 2023-09-29 10:29:51 +02:00
.gitignore Add missing .gitignore file 2023-09-28 16:56:42 +02:00
LICENSE Initial commit 2023-09-27 18:22:00 +02:00
pyproject.toml Initial commit 2023-09-27 18:22:00 +02:00
README.md Add more detailed install instructions 2023-09-28 14:53:22 +02:00
requirements.txt Initial commit 2023-09-27 18:22:00 +02:00

Client-Server Windows Codesign Tool

This tool allows to use a code signing key stored on a secure token (such as a Yubikey) for code-signing binaries produced during CI/CD process.

Only minimal amount of data is transferred between the client and server.

Theory of operation

The operation of the took is based around the signtool.exe and its ability of offloading the digest signing to an external tool.

The overview of the process with an example commands to help visualizing the process and possibly following it manually:

  1. Client requests public certificate from the server. The server uses the ykman command to do so:
ykman piv certificates export 9a -
  1. Client creates the digest. Example command of doing so:
signtool.exe sign /f .\key\certificate.crt /fd sha256 /dg .\digest\ blender.exe
  1. Client requests server to sign .\digest\blender.exe.dig

  2. Server uses OpenSSL to perform code signing operation. The command looks like the following:

cat blender.exe.dig | base64 -d | \
    openssl pkeyutl \
        -engine pkcs11 -keyform engine -inkey "pkcs11:object=Private key for PIV Authentication;type=private;pin-value=123456" \
        -sign -pkeyopt digest:sha256 | \
    base64 > blender.exe.dig.signed
  1. Client receives the signed digest, stores it on disk, and uses signtool to create signature on the file:
signtool.exe sign /di .\digest\ blender.exe
  1. The client add a timestamp:
signtool.exe timestamp /tr http://ts.ssl.com /td sha256 blender.exe

Client deployment

For the client script codesign.py it is enough to have a modern Python interpreter installed.

Server deployment

The server requires the following components:

  1. Python interpreter.
  2. Tornado Python web server. Can either be installed system-wide as python3-tornado, or installed to a virtual environment using the provided requirements.txt.
  3. Yubikey manager: sudo apt install yubikey-manager
  4. OpenSSL, which is commonly comes pre-installed on modern Linux distribution.

Server deployment example on Ubuntu

Here comes an example of how the server can be deployed on a fairly clean install of Ubuntu Linux.

Assumes that the repository has been already checked out.

Install dependencies:

sudo apt install yubikey-manager scdaemon python3-tornado ykcs11 libengine-pkcs11-openssl

The libengine-pkcs11-openssl is required to be able to point OpenSSL to a pkcs11 engine. The scdaemon is needed to make tools like gpg to talk to the Yubikey in a transparent manner.

A quirk was notices on Ubuntu 20.04 where a manual server startup was needed:

sudo systemctl start pcscd.service

Without this the ykman info command was reporting the following error: "failure to establish context: service not available".

To check the connectivity to the Yubikey:

$ ykman info
Device type: YubiKey 5
Serial number: [redacted]
Firmware version: 5.4.3
Enabled USB interfaces: OTP+FIDO+CCID

Applications
OTP     	Enabled
FIDO U2F	Enabled
OpenPGP 	Enabled
PIV     	Enabled
OATH    	Enabled
FIDO2   	Enabled

Copy template of production config to the actual place:

cp server/config/config_prod_template.py server/config/config_prod.py

Run the server

./server/server.py

To check the operation:

$ curl http://127.0.0.1:8080/certificate
{"certificate": "-----BEGIN CERTIFICATE-----\n....\n-----END CERTIFICATE-----\n"}

$ curl http://127.0.0.1:8080/sign?digest=QmxlbmRlcgo=
{"digest_signed": "..."}