keypresser/scripts/signing.py

105 lines
4.7 KiB
Python
Raw Normal View History

2025-06-19 18:38:55 +00:00
import subprocess
import os
import sys
import logging
# --- Configuration for Code Signing ---
# !!! IMPORTANT: Replace these placeholders with your actual details !!!
# Path to your .pfx or .p12 certificate file
CERTIFICATE_PATH = r"C:\Path\To\Your\my_certificate.pfx"
# Password for your certificate file
CERTIFICATE_PASSWORD = "YourSecureCertificatePassword"
# Path to signtool.exe (usually in Windows SDK bin folder)
# You might need to adjust the version number (e.g., 10.0.19041.0) and architecture (x64/x86)
SIGNTOOL_PATH = r"C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe"
# Name of your PyInstaller-generated executable (found in the 'dist' folder)
EXE_NAME = "JarvisKeyPressUtility.exe"
# Timestamp server URL (use one provided by your CA, e.g., DigiCert, Sectigo)
TIMESTAMP_SERVER = "http://timestamp.digicert.com"
# --- Logging Setup ---
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def run_command(command, cwd=None):
"""Executes a shell command and captures its output."""
try:
logging.info(f"Executing command: {' '.join(command)}")
process = subprocess.run(command, capture_output=True, text=True, check=True, cwd=cwd)
logging.info("Command completed successfully.")
logging.info(f"STDOUT:\n{process.stdout}")
if process.stderr:
logging.warning(f"STDERR:\n{process.stderr}")
return True
except subprocess.CalledProcessError as e:
logging.error(f"Command failed with exit code {e.returncode}.")
logging.error(f"STDOUT:\n{e.stdout}")
logging.error(f"STDERR:\n{e.stderr}")
return False
except FileNotFoundError:
logging.error(f"Error: Executable not found. Check the path for: {command[0]}")
return False
except Exception as e:
logging.error(f"An unexpected error occurred: {e}")
return False
def sign_executable(exe_path, cert_path, cert_password, signtool_path, timestamp_server):
"""
Signs the given executable using signtool.exe.
"""
if not os.path.exists(signtool_path):
logging.error(f"SignTool not found at: {signtool_path}. Please ensure Windows SDK is installed and path is correct.")
return False
if not os.path.exists(cert_path):
logging.error(f"Certificate file not found at: {cert_path}. Please check the path.")
return False
logging.info(f"Attempting to sign executable: {exe_path}")
# Signtool command components
command = [
signtool_path,
"sign",
"/f", cert_path, # Certificate file
"/p", cert_password, # Certificate password
"/fd", "sha256", # File digest algorithm
"/tr", timestamp_server, # Timestamp server URL
"/td", "sha256", # Timestamp digest algorithm
exe_path # Executable to sign
]
return run_command(command)
if __name__ == "__main__":
# Ensure this script is run from the same directory where 'dist' folder is or adjust paths.
# Typically, you'd run this script after PyInstaller has finished its build.
current_script_dir = os.path.dirname(os.path.abspath(__file__))
dist_folder = os.path.join(current_script_dir, "dist")
exe_path_in_dist = os.path.join(dist_folder, EXE_NAME)
if not os.path.exists(dist_folder):
logging.error(f"Error: 'dist' folder not found at '{dist_folder}'. Please run PyInstaller first.")
sys.exit(1)
if not os.path.exists(exe_path_in_dist):
# Handle --onefile vs. no --onefile
if not os.path.exists(os.path.join(dist_folder, EXE_NAME.replace(".exe", ""), EXE_NAME)):
logging.error(f"Error: Executable '{EXE_NAME}' not found in '{dist_folder}' or its subfolder (for non-onefile builds).")
logging.info("If you used PyInstaller without --onefile, the EXE might be in a subfolder like 'dist/JarvisKeyPressUtility/JarvisKeyPressUtility.exe'.")
sys.exit(1)
else:
exe_path_to_sign = os.path.join(dist_folder, EXE_NAME.replace(".exe", ""), EXE_NAME)
logging.info(f"Detected non-onefile build. Signing: {exe_path_to_sign}")
else:
exe_path_to_sign = exe_path_in_dist
logging.info(f"Detected onefile build. Signing: {exe_path_to_sign}")
logging.info("Starting code signing process.")
success = sign_executable(exe_path_to_sign, CERTIFICATE_PATH, CERTIFICATE_PASSWORD, SIGNTOOL_PATH, TIMESTAMP_SERVER)
if success:
logging.info(f"Successfully signed '{EXE_NAME}'. Your AVG should now be less annoyed, theoretically.")
else:
logging.error(f"Failed to sign '{EXE_NAME}'. Check the log for details on the signtool error.")