[sysadmin/ci-notary-service] /: Add support for JSign which will be used for signing Windows artifacts (exe, dll, appx) on Linux systems

Ben Cooksley <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 7e8743b5e0a5c02506aec81bf5e3df2624fd5b67 by Ben Cooksley.
Committed on 29/07/2026 at 10:41.
Pushed by bcooksley into branch 'master'.

Add support for JSign which will be used for signing Windows artifacts (exe, dll, appx) on Linux systems

M  +7    -0    doc/windowsbinariessigner.sample.ini
M  +53   -1    sftpnotary/windowsbinariessigner.py

https://invent.kde.org/sysadmin/ci-notary-service/-/commit/7e8743b5e0a5c02506aec81bf5e3df2624fd5b67

diff --git a/doc/windowsbinariessigner.sample.ini b/doc/windowsbinariessigner.sample.ini
index 66f0eb2..0eb6899 100644
--- a/doc/windowsbinariessigner.sample.ini
+++ b/doc/windowsbinariessigner.sample.ini
@@ -6,8 +6,15 @@ Enabled = true
 GitLabAPIv4Url = https://gitlab.example.net/api/v4
 
 [CodeSigning]
+## Utility to use when signing
+# For Windows using Windows SDK Sign Tool
+Utility = codesign
+# For all other systems
+# Utility = jsign
 ## Path of SignTool
 SignTool = C:/windowsbinariessigner/signtool.exe
+## Path of JSign
+JSignPath = /usr/bin/jsign
 ## Path of Certificate
 # Certificate = C:\windowsbinariessigner\mycert.pfx
 ## Password to unlock the certificate
diff --git a/sftpnotary/windowsbinariessigner.py b/sftpnotary/windowsbinariessigner.py
index 5677941..a2d67a5 100644
--- a/sftpnotary/windowsbinariessigner.py
+++ b/sftpnotary/windowsbinariessigner.py
@@ -7,7 +7,7 @@ import os
 import re
 import shutil
 from pathlib import Path
-from tempfile import TemporaryDirectory
+from tempfile import NamedTemporaryFile, TemporaryDirectory
 from typing import Dict, List, Optional
 from zipfile import ZipFile
 
@@ -230,6 +230,58 @@ class SignWindowsBinariesProcessor(TaskProcessor):
             log.info("No files to sign")
             return
 
+        signingUtility = config.settings.get("CodeSigning", "Utility", "codesign")
+
+        if signingUtility == "jsign":
+            self.signFilesWithJSign(fileNames, path)
+        elif signingUtility == "codesign":
+            self.signFilesWithCodesign(fileNames, path)
+        else:
+            raise Error(
+                "System configuration error - only jsign (ebourg.github.io/jsign/) and "
+                "codesign (Microsoft Windows SDK) are supported"
+            )
+
+    def signFilesWithJSign(self, fileNames: List[str], path: Path):
+        log.info(f"Signing files: {', '.join(fileNames)}")
+        workingDirectory = path
+
+        jsignPath = Path(config.settings.get("CodeSigning", "JSignPath"))
+        certFile = config.settings.get("CodeSigning", "Certificate", "")
+        password = config.settings.get("CodeSigning", "Password", "")
+        subjectName = config.settings.get("CodeSigning", "CommonName", "")
+        command = [
+            jsignPath,
+            "sign",
+            "--verbose",
+            "--storetype PKCS11",
+            "--tsaurl",
+            "http://timestamp.digicert.com",
+            "--alg",
+            "SHA-256",
+        ]
+        if certFile:
+            command += ["--keystore", certFile]
+        if password:
+            command += ["--storepass", password]
+        if subjectName:
+            command += ["--alias", subjectName]
+
+        # To avoid issues with command line length we pass the list of files to sign to JSign by way of a file
+        with NamedTemporaryFile(mode="w", delete=False) as filesToSign:
+            # Write that list and ensure it is flushed
+            filesToSign.write("\n".join(fileNames))
+            filesToSign.close()
+
+            commandToRun = command + [f"@{filesToSign.name}"]
+            commandToLog = sftpnotary.log.maskFollowingItem(commandToRun, "--storepass")
+            util.runCommand(commandToRun, commandToLog=commandToLog, cwd=workingDirectory)
+
+            # Cleanup
+            # In an ideal world we would use delete_on_close=False, however the linters we use do not like that
+            os.unlink(filesToSign.name)
+
+    def signFilesWithCodesign(self, fileNames: List[str], path: Path):
         log.info(f"Signing files: {', '.join(fileNames)}")
         workingDirectory = path
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.