[PATCH v5 0/2] binman: add PKCS#11/HSM signing support for X509 certificates

Sergio Prado sergio.prado at e-labworks.com
Thu Jun 11 11:14:09 CEST 2026


Hi Simon, Quentin,

Do you have any feedback on the v5 series?

Best regards,

Sergio Prado

Em seg., 25 de mai. de 2026 às 10:29, Sergio Prado <
sergio.prado at e-labworks.com> escreveu:

> Motivation
> ----------
>
> TI K3 secure boot requires X509 certificates to be signed with a private
> key at build time. Until now, binman expected this key to be present as
> a plain PEM file on the build host. For production use, however, private
> keys should never exist unprotected on a build machine — they belong
> inside a Hardware Security Module (HSM) that enforces access control and
> keeps the key material unexportable.
>
> This series adds support for signing X509 certificates via any PKCS#11-
> capable HSM (YubiKey, TPM, network HSM, SoftHSM2 for development, etc.).
>
> Design
> ------
>
> The 'keyfile' entry argument of x509_cert (and its TI K3 subclasses)
> now accepts either a filesystem path to a PEM key, as before, or a
> PKCS#11 URI referring to a key stored in an HSM. The URI is forwarded
> as-is to 'openssl -key', which resolves it via the pkcs11 provider (or
> engine) configured externally through OPENSSL_CONF. This keeps binman
> itself free of any PKCS#11-specific knowledge.
>
> A new make variable, BINMAN_X509_KEY_URI, overrides the 'keyfile' entry
> argument for all x509 certificate signing operations:
>
>     make BINMAN_X509_KEY_URI="pkcs11:token=mytk;object=mykey;type=private"
> \
>          OPENSSL_CONF=/path/to/openssl.cnf
>
> The recommended way to deliver the PIN for non-interactive signing is
> to configure pkcs11-module-token-pin in openssl.cnf under the pkcs11
> provider section. As a convenience fallback, the PKCS11_PIN environment
> variable can be set; its value is appended to the URI as a percent-
> encoded ?pin-value=<pin> (per RFC 7512) so the PIN does not have to be
> embedded in BINMAN_X509_KEY_URI.
>
> Two URI forms are supported on OpenSSL 3.x:
>
>   - Provider path (recommended), via the pkcs11-provider package
>   - Engine path, prefixed with org.openssl.engine:<engine_name>: so
>     OpenSSL 3.x's STORE API routes it to the engine
>
> Testing
> -------
>
> Tested on a Toradex Verdin AM62 (verdin-am62_a53_defconfig) board with
> both the engine path and the pkcs11 provider path, using SoftHSM2 and
> YubiKey 5 NFC.
>
> A SoftHSM2-based integration test (testX509CertPkcs11) exercises the
> full signing path end-to-end against an in-tree test key; it skips
> cleanly when the OpenSSL pkcs11 provider is not installed. A unit test
> (testX509CertBuildPkcs11Key) covers the URI/PIN combiner including
> percent-encoding of reserved characters.
>
> Changes in v5:
> - Split the Entry_x509_cert docstring expansion into a separate
>   preparatory commit (Simon)
> - Unify URI support under the existing 'keyfile' entry argument
>   rather than introducing a new 'x509-key-uri' arg; drop the
>   Entry_x509_cert state and override logic that v4 added (Quentin)
> - Percent-encode the PIN before appending to the URI using
>   urllib.parse.quote(), per RFC 7512, and add a unit test
>   covering PINs with reserved characters (Simon)
> - Detect the OpenSSL pkcs11 provider via
>   'openssl list -providers -provider pkcs11' rather than discovering
>   the provider .so path manually; drops the 'openssl version -m'
>   MODULESDIR lookup (Quentin)
> - Drop the 'p11-kit print-config' / softhsm2 .so discovery from the
>   test; the simpler openssl.cnf relies on softhsm2 being registered
>   with p11-kit globally (Quentin). As a side effect this also fixes
>   the test on Ubuntu 22.04, where 'p11-kit print-config' is not a
>   valid subcommand
> - Move the test openssl.cnf in-tree as
>   tools/binman/test/fit/openssl_provider.conf and trim it to the
>   minimum needed (no 'module = ', no 'pkcs11-module-path = ') (Quentin)
> - Replace 'pkcs11-tool --keypairgen' with 'softhsm2-util --import' of
>   tools/binman/test/fit/rsa2048.key so the test runs faster and drops
>   the pkcs11-tool dependency (Quentin)
> - Recommend pkcs11-module-token-pin in openssl.cnf as the primary
>   way to deliver the PIN; PKCS11_PIN env var is now documented as
>   the convenience fallback (Quentin)
> - Drop the incorrect claim that PKCS11_PIN keeps the PIN out of
>   shell history (Quentin)
> - Rephrase the URI intro in binman.rst and clarify that
>   'pkcs11-provider' is a Debian package name, not a path (Quentin)
> - Drop the inheritance notes added to Entry_ti_secure and
>   Entry_ti_secure_rom in v4; with the v5 keyfile unification the
>   original motivation (an inherited x509-key-uri property) no
>   longer applies
>
> Changes in v4:
> - Drop the v3 bintool extra_env commit entirely; binman no longer
>   sets any PKCS#11-related environment variables (Quentin)
> - Drop BINMAN_PKCS11_MODULE / pkcs11-module entry argument; the
>   PKCS#11 module path must be configured externally via openssl.cnf
>   (Quentin)
> - Drop provider/engine auto-detection (_pkcs11_use_provider,
>   _build_key_args, _run_cmd_pkcs11) along with the threading.Lock;
>   the user selects provider or engine via OPENSSL_CONF and the URI
>   form (Quentin)
> - Rename the v3 BINMAN_PKCS11_URI / pkcs11-uri to BINMAN_X509_KEY_URI
>   / x509-key-uri to scope the names to x509 certificate entries
>   without locking them to a specific URI scheme (Quentin)
> - Document that the engine path is supported on OpenSSL 3.x by
>   prefixing the URI with org.openssl.engine:<engine_name>: (Quentin)
> - Replace the mocked openssl test with a real SoftHSM2-based
>   integration test using the provider path and OPENSSL_CONF (Quentin)
> - Use 'p11-kit print-config' to locate the softhsm2 library at test
>   time instead of hardcoding a distro-specific path (Quentin)
> - Use 'openssl version -m' (MODULESDIR) to locate the OpenSSL pkcs11
>   provider .so file, so multiarch paths like
>   /usr/lib/x86_64-linux-gnu/ossl-modules on Debian/Ubuntu are handled
>   correctly
> - Generate the test RSA keypair with pkcs11-tool; softhsm2-util has
>   no key-generation action (only --import) and silently exits 0 on an
>   unknown --generate-keypair option, which would leave the token
>   empty and make the openssl step fail with 'Could not read private
>   key'
> - Add self._CheckBintool() to all PKCS#11 test paths so tests skip
>   cleanly when bintools are missing (Quentin)
> - Extract the URI/PIN combiner into Entry_x509_cert._build_pkcs11_key()
>   and add a unit test for it
> - Document that PKCS11_PIN keeps the PIN out of the make command line
>   but is still visible in 'ps' output via the openssl invocation; for
>   improved isolation, configure the PIN in openssl.cnf
> - Document all Entry_x509_cert properties (content, keyfile,
>   x509-key-uri, cert-ca, cert-revision-int, sw-rev) in its docstring
>   (Quentin)
> - Add inheritance notes to Entry_ti_secure and Entry_ti_secure_rom
>   docstrings, pointing out that they extend Entry_x509_cert via
>   super() and therefore accept its properties (notably x509-key-uri)
>   (Quentin)
>
> Changes in v3:
> - Split into two patches: bintool infrastructure (1/2) and x509_cert
>   feature (2/2)
> - Fix global environment mutation: _run_cmd_pkcs11() no longer writes
>   to os.environ directly; it now uses the new extra_env parameter so
>   module paths are scoped to the subprocess only, which is both
>   cleaner and safe under concurrent execution
> - Add module-level threading.Lock to serialise concurrent PKCS#11
>   signing calls and fix intermittent login failures caused by binman's
>   ThreadPoolExecutor
> - Fix URI query string separator: use '&' when the URI already
>   contains '?' (e.g. module-path already present), '?' otherwise
> - Test cases updated
>
> Changes in v2:
> - Add tests for _build_key_args() (PEM path, PKCS#11 provider, PKCS#11
>   engine, PIN appending), _pkcs11_use_provider() (caching),
>   _run_cmd_pkcs11() (with and without module path), and end-to-end
>   x509_cert signing with a PKCS#11 URI (testX509CertPkcs11), ensuring
>   btool/openssl.py and etype/x509_cert.py have 100% test coverage
>
> Sergio Prado (2):
>   binman: x509_cert: document Entry_x509_cert properties
>   binman: x509_cert: support PKCS#11 URI in keyfile for HSM signing
>
>  Makefile                                    |  1 +
>  tools/binman/binman.rst                     | 55 ++++++++++++
>  tools/binman/etype/x509_cert.py             | 45 ++++++++--
>  tools/binman/ftest.py                       | 92 +++++++++++++++++++++
>  tools/binman/test/fit/openssl_provider.conf | 14 ++++
>  5 files changed, 202 insertions(+), 5 deletions(-)
>  create mode 100644 tools/binman/test/fit/openssl_provider.conf
>
> --
> 2.34.1
>
>


More information about the U-Boot mailing list