Created
March 2, 2026 13:30
-
-
Save flozz/2c0c25d885bf6b735d4545f06998bd6b to your computer and use it in GitHub Desktop.
Signs given Windows executable or DLL using Microsoft™ signtool.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """Signs given Windows executable or DLL using Microsoft™ signtool.""" | |
| # USAGE: | |
| # | |
| # ./signexe.py --help | |
| # | |
| # LICENSE: | |
| # | |
| # Copyright © 2026 Fabien LOISON <https://blog.flozz.fr> | |
| # This work is free. You can redistribute it and/or modify it under the | |
| # terms of the Do What The Fuck You Want To Public License, Version 2, | |
| # as published by Sam Hocevar. See http://www.wtfpl.net/ for more details. | |
| import sys | |
| import argparse | |
| import subprocess | |
| from pathlib import Path | |
| from collections.abc import Sequence | |
| def locate_signtool_exe() -> Path: | |
| """Locates Microsoft™'s signtool.exe. | |
| :raise FileNotFoundError: if the signtool cannot be found. | |
| :return: The path of signtool.exe. | |
| """ | |
| path = Path("C:\\Program Files (x86)\\Windows Kits\\") | |
| signtools = list(path.glob("**/signtool.exe", case_sensitive=False)) | |
| if not signtools: | |
| raise FileNotFoundError("Signtool not found on the system!") | |
| return signtools[0] | |
| def sign_exe( | |
| exe_path: Path, | |
| cert_name: str | None = None, | |
| cert_sha1: str | None = None, | |
| file_digest: str = "SHA256", | |
| timestamp_server: str | None = None, | |
| timestamp_digest: str = "SHA256", | |
| ) -> None: | |
| """Signs the given executable or library. | |
| :param exe_path: The path of the exe or DLL file. | |
| :param cert_name: The common name (or a part of it) of the certificate to use. | |
| :param cert_sha1: The sha1 thumbprint of the certificate to use. | |
| :param file_digest: The digest algorythm used for file signature. | |
| :param timestamp_server: URL of the RFC 3161 compliant trusted time stamp server. | |
| :param timestamp_digest: The digest algorythm used for the timestamp signature. | |
| .. IMPORTANT:: | |
| At least one of the ``cert_*`` parameters must be provided. | |
| """ | |
| if not cert_name and not cert_sha1: | |
| raise ValueError("Either cert_name or cert_sha1 is required") | |
| signtool = locate_signtool_exe() | |
| cli = [str(signtool), "sign"] | |
| if cert_name: | |
| cli += ["/n", cert_name] | |
| if cert_sha1: | |
| cli += ["/sha1", cert_sha1] | |
| cli += ["/fd", file_digest] | |
| if timestamp_server: | |
| cli += ["/tr", timestamp_server, "/td", timestamp_digest] | |
| cli += [str(exe_path.absolute())] | |
| print("Running: %s" % str(cli)) | |
| subprocess.run(cli) | |
| def main(args: Sequence[str] = sys.argv[1:]) -> None: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| cert_id_group = parser.add_argument_group( | |
| "Certificat Identification", | |
| "At least one of the identification option must be provided", | |
| ) | |
| cert_id_group.add_argument( | |
| "--cert-name", | |
| help="the common name of the certificate to use", | |
| type=str, | |
| ) | |
| cert_id_group.add_argument( | |
| "--cert-sha1", | |
| help="the sha1 thumbprint of the certificate to use", | |
| type=str, | |
| ) | |
| parser.add_argument( | |
| "--trusted-timestamp-server", | |
| help="an RFC 3161 compliant trusted timestamp server", | |
| type=str, | |
| ) | |
| parser.add_argument( | |
| "EXE_PATH", | |
| help="path to the executable or DLL file to sign", | |
| type=argparse.FileType("rb"), | |
| ) | |
| parsed_args = parser.parse_args(args) | |
| sign_exe( | |
| Path(parsed_args.EXE_PATH.name), | |
| cert_name=parsed_args.cert_name, | |
| cert_sha1=parsed_args.cert_sha1, | |
| timestamp_server=parsed_args.trusted_timestamp_server, | |
| ) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment