Migrate to using new Digicert keylocker cert for authenticode signing

The old one is about to expire. The new keylocker based service is
highway robbery some 800 dollars a year. Migrate to Azure Trust based
signing once that leaves beta for $120 a year instead, assuming it was
not designed by incompetent buffoons, which is always a possibility.
This commit is contained in:
Kovid Goyal 2025-09-18 14:40:17 +05:30
parent 20dcc7331a
commit b03dbbe128
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -362,7 +362,7 @@ def build_portable(env):
obj, 'User32.lib', 'Shell32.lib']
run(*cmd)
launchers.append(exe)
sign_files(env, launchers)
sign_files(launchers)
printf('Creating portable installer')
shutil.copytree(env.base, j(base, 'Calibre'))
@ -378,24 +378,48 @@ def build_portable(env):
subprocess.check_call([PREFIX + r'\bin\elzma.exe', '-9', '--lzip', name])
def sign_files(env, files):
with open(os.path.expandvars(r'${HOMEDRIVE}${HOMEPATH}\code-signing\cert-cred')) as f:
pw = f.read().strip()
CODESIGN_CERT = os.path.abspath(os.path.expandvars(r'${HOMEDRIVE}${HOMEPATH}\code-signing\authenticode.pfx'))
args = [SIGNTOOL, 'sign', '/a', '/fd', 'sha256', '/td', 'sha256', '/d',
'calibre - E-book management', '/du',
'https://calibre-ebook.com', '/f', CODESIGN_CERT, '/p', pw, '/tr']
def sign_files(files):
printf('Signing {} files'.format(len(files)))
cspath = os.path.expandvars(r'${HOMEDRIVE}${HOMEPATH}\code-signing')
evars = os.environ.copy()
with open(os.path.join(cspath, 'digicert-api-key')) as f:
evars['SM_API_KEY'] = f.read().strip()
with open(os.path.join(cspath, 'digicert-client-certificate-password')) as f:
evars['SM_CLIENT_CERT_PASSWORD'] = f.read().strip()
evars['SM_CLIENT_CERT_FILE'] = os.path.abspath(os.path.join(cspath, 'digicert-client-certificate.p12'))
evars['SM_HOST'] = 'https://clientauth.one.digicert.com'
evars['PATH'] += os.pathsep + os.path.dirname(SIGNTOOL)
keylocker_path = r'C:\Program Files\DigiCert\DigiCert Keylocker Tools'
evars['PATH'] += os.pathsep + keylocker_path
subprocess.check_call([os.path.join(keylocker_path, 'smctl.exe'), 'healthcheck'], env=evars)
# To get the certificate thumbprint run the following commands with SM_API_KEY set to the key from digicert-api-key.
# smctl is found in C:\Program Files\DigiCert\DigiCert Keylocker Tools
# To get keypair alias:
# smctl keypair list
# To get certificate thumbprint:
# smctl windows certsync --keypair-alias=alias from previous step
certificate_thumbprint = 'e30cac630f80fbe04964e221b56d07b4a177c96a'
args = [SIGNTOOL, 'sign', '/sha1', certificate_thumbprint,
'/fd', 'sha256', '/td', 'sha256', '/d', 'calibre - E-book management',
'/du', 'https://calibre-ebook.com', '/v', '/debug', '/tr']
# with open(os.path.expandvars(r'${HOMEDRIVE}${HOMEPATH}\code-signing\cert-cred')) as f:
# pw = f.read().strip()
# CODESIGN_CERT = os.path.abspath(os.path.expandvars(r'${HOMEDRIVE}${HOMEPATH}\code-signing\authenticode.pfx'))
# args = [SIGNTOOL, 'sign', '/a', '/fd', 'sha256', '/td', 'sha256', '/d',
# 'calibre - E-book management', '/du',
# 'https://calibre-ebook.com', '/f', CODESIGN_CERT, '/p', pw, '/tr']
def runcmd(cmd):
# See https://gist.github.com/Manouchehri/fd754e402d98430243455713efada710 for list of timestamp servers
for timeserver in (
'http://timestamp.digicert.com', # DigiCert
'http://timestamp.acs.microsoft.com/', # this is Microsoft Azure Code Signing
'http://rfc3161.ai.moda/windows', # this is a load balancer
'http://timestamp.comodoca.com/rfc3161',
'http://timestamp.sectigo.com'
):
try:
subprocess.check_call(cmd + [timeserver] + list(files))
subprocess.check_call(cmd + [timeserver] + list(files), env=evars)
break
except subprocess.CalledProcessError:
print(f'Signing failed with timestamp server {timeserver}, retrying with different timestamp server')
@ -415,7 +439,7 @@ def sign_installers(env):
os.remove(f)
if not installers:
raise ValueError('No installers found')
sign_files(env, installers)
sign_files(installers)
def add_dir_to_zip(zf, path, prefix=''):
@ -574,8 +598,7 @@ def sign_executables(env):
for path in walk(env.base):
if path.lower().endswith('.exe') or path.lower().endswith('.dll'):
files_to_sign.append(path)
printf('Signing {} exe/dll files'.format(len(files_to_sign)))
sign_files(env, files_to_sign)
sign_files(files_to_sign)
def main():