From 10dbd186ed3c59af7742f460a79e796705267d8e Mon Sep 17 00:00:00 2001 From: nessi Date: Wed, 18 Mar 2026 12:28:53 +0100 Subject: [PATCH] feat: add code signing support for Windows desktop client executables and installers Add osslsigncode dependency and implement three-step signing workflow. Decode base64-encoded PFX certificate from secrets, sign bundled tunnel helper before build, then sign desktop executable and NSIS installer after build. Use configurable timestamp URL with DigiCert fallback. Clean up certificate file in always-run step to prevent secret leakage. --- .gitea/workflows/windows-desktop-client.yml | 58 +++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/.gitea/workflows/windows-desktop-client.yml b/.gitea/workflows/windows-desktop-client.yml index 2296819..2252deb 100644 --- a/.gitea/workflows/windows-desktop-client.yml +++ b/.gitea/workflows/windows-desktop-client.yml @@ -39,6 +39,7 @@ jobs: wget \ file \ nsis \ + osslsigncode \ pkg-config \ libssl-dev \ libayatana-appindicator3-dev \ @@ -57,9 +58,66 @@ jobs: - name: Build bundled Windows tunnel helper run: npm run helper:windows-x64 + - name: Prepare signing certificate + if: ${{ secrets.WINDOWS_SIGN_PFX_B64 != '' && secrets.WINDOWS_SIGN_PFX_PASSWORD != '' }} + run: | + printf '%s' "${{ secrets.WINDOWS_SIGN_PFX_B64 }}" | base64 -d > /tmp/nexavpn-signing-cert.pfx + + - name: Sign bundled Windows tunnel helper + if: ${{ secrets.WINDOWS_SIGN_PFX_B64 != '' && secrets.WINDOWS_SIGN_PFX_PASSWORD != '' }} + run: | + set -euo pipefail + timestamp_url="${{ secrets.WINDOWS_SIGN_TIMESTAMP_URL }}" + if [ -z "${timestamp_url}" ]; then + timestamp_url="http://timestamp.digicert.com" + fi + helper_path="src-tauri/bundled/windows-x64/nexavpn-tunnel-helper.exe" + signed_path="${helper_path}.signed" + osslsigncode sign \ + -pkcs12 /tmp/nexavpn-signing-cert.pfx \ + -pass "${{ secrets.WINDOWS_SIGN_PFX_PASSWORD }}" \ + -h sha256 \ + -ts "${timestamp_url}" \ + -in "${helper_path}" \ + -out "${signed_path}" + mv "${signed_path}" "${helper_path}" + - name: Build Windows installer run: npm run tauri:build:windows-x64:linux + - name: Sign Windows desktop executable and installer + if: ${{ secrets.WINDOWS_SIGN_PFX_B64 != '' && secrets.WINDOWS_SIGN_PFX_PASSWORD != '' }} + run: | + set -euo pipefail + timestamp_url="${{ secrets.WINDOWS_SIGN_TIMESTAMP_URL }}" + if [ -z "${timestamp_url}" ]; then + timestamp_url="http://timestamp.digicert.com" + fi + + sign_file() { + local input_path="$1" + local output_path="${input_path}.signed" + osslsigncode sign \ + -pkcs12 /tmp/nexavpn-signing-cert.pfx \ + -pass "${{ secrets.WINDOWS_SIGN_PFX_PASSWORD }}" \ + -h sha256 \ + -ts "${timestamp_url}" \ + -in "${input_path}" \ + -out "${output_path}" + mv "${output_path}" "${input_path}" + } + + sign_file "src-tauri/target/x86_64-pc-windows-msvc/release/nexavpn-desktop.exe" + + shopt -s nullglob + for installer in src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe; do + sign_file "${installer}" + done + + - name: Cleanup signing certificate + if: ${{ always() && secrets.WINDOWS_SIGN_PFX_B64 != '' && secrets.WINDOWS_SIGN_PFX_PASSWORD != '' }} + run: rm -f /tmp/nexavpn-signing-cert.pfx + - name: Upload Windows installer uses: christopherhx/gitea-upload-artifact@v4 with: