Sign Windows builds with SHA-256 file digests
The one-step "signtool sign /fd sha256" fails on the build host with 0xc0000225 before the PIN is asked for, because crypt32 converts the legacy CryptoAPI key handle to a CNG one for SHA-2 digests, and no CNG provider is registered under the name of Certum's crypto3 CSP. Rebinding to cryptoCertum3 KSP is not an option, as it cannot see the Common profile's code-signing key. signtool's split flow avoids the conversion, so pkg/win32/sign-files.bat now does the signing in /dg, /ds, /di, timestamp and verify steps, with one PIN prompt per call as before. Make.bat's :SIGN_FILES and Inno Setup's SignTool both go through it, the latter via cmd /c. Also default PGADMIN_SIGNTOOL_DIR to SDK 10.0.26100.0, the one installed on the host, and match the PIN dialog on "Logowanie do profilu zwyk" so that a Secure profile prompt is never answered with the Common PIN. SIGNING.md gains the full setup procedure, the root cause and the split signing flow.
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -4,3 +4,4 @@
|
||||
# Make.bat has always been stored with CRLF; the blanket eol=lf above would
|
||||
# silently rewrite the whole file the first time anything touches it.
|
||||
Make.bat -text
|
||||
pkg/win32/*.bat text eol=crlf
|
||||
|
||||
9
.github/workflows/check-windows-build.yml
vendored
9
.github/workflows/check-windows-build.yml
vendored
@@ -1,10 +1,11 @@
|
||||
# Replaces the pgadmin4-windows-x64-qa Jenkins job.
|
||||
#
|
||||
# This runs on a hosted runner, not a self-hosted one. Code signing in Make.bat
|
||||
# is conditional on PGADMIN_WINDOWS_CSC being set (see Make.bat:393, :447 and
|
||||
# :469), so an unsigned build check needs neither the Certum hardware token nor
|
||||
# the machine it is plugged into. Only the release and snapshot builds, which
|
||||
# do sign, need the self-hosted runner.
|
||||
# is conditional on PGADMIN_WINDOWS_CSC being set (see :SIGN_FILES,
|
||||
# :CREATE_INSTALLER and :VERIFY_SIGNATURE in Make.bat), so an unsigned build
|
||||
# check needs neither the Certum hardware token nor the machine it is plugged
|
||||
# into. Only the release and snapshot builds, which do sign, need the
|
||||
# self-hosted runner.
|
||||
#
|
||||
# Dependencies come from dpage/winpgbuild, which is exactly where the
|
||||
# buildfarm's winpgbuild-update job gets them, so the toolchain matches what
|
||||
|
||||
28
Make.bat
28
Make.bat
@@ -127,7 +127,7 @@ REM Main build sequence Ends
|
||||
IF "%PGADMIN_INNOTOOL_DIR%" == "" SET "PGADMIN_INNOTOOL_DIR=C:\Program Files (x86)\Inno Setup 6"
|
||||
IF "%PGADMIN_VCREDIST_DIR%" == "" SET "PGADMIN_VCREDIST_DIR=C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Redist\MSVC\14.40.33807"
|
||||
IF "%PGADMIN_VCREDIST_FILE%" == "" SET "PGADMIN_VCREDIST_FILE=vc_redist.x64.exe"
|
||||
IF "%PGADMIN_SIGNTOOL_DIR%" == "" SET "PGADMIN_SIGNTOOL_DIR=C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64"
|
||||
IF "%PGADMIN_SIGNTOOL_DIR%" == "" SET "PGADMIN_SIGNTOOL_DIR=C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64"
|
||||
IF "%PGADMIN_WINDOWS_CSC%" == "" SET "PGADMIN_WINDOWS_CSC="
|
||||
|
||||
REM Set additional variables we need
|
||||
@@ -444,8 +444,12 @@ REM Main build sequence Ends
|
||||
DEL /s "%WD%\pkg\win32\installer.iss.in_stage*" > nul
|
||||
|
||||
ECHO Creating windows installer using INNO tool...
|
||||
REM Inno signs the installer and its uninstaller itself, one file per call,
|
||||
REM through the same script as :SIGN_FILES. Inno replaces $q with a quote
|
||||
REM and $f with the already-quoted file name. The outer pair of quotes is
|
||||
REM for cmd /c, which strips the first and last quote of its command line.
|
||||
IF NOT "%PGADMIN_WINDOWS_CSC%" == "" (
|
||||
CALL "%PGADMIN_INNOTOOL_DIR%\ISCC.exe" "%WD%\pkg\win32\installer.iss" "/DSIGNED" "/SpgAdminSigntool=%PGADMIN_SIGNTOOL_DIR%\signtool.exe sign /sm /n $q%PGADMIN_WINDOWS_CSC%$q /tr http://timestamp.digicert.com /td sha256 /fd sha1 /v $f" || EXIT /B 1
|
||||
CALL "%PGADMIN_INNOTOOL_DIR%\ISCC.exe" "%WD%\pkg\win32\installer.iss" "/DSIGNED" "/SpgAdminSigntool=%ComSpec% /c $q$q%WD%\pkg\win32\sign-files.bat$q $q%PGADMIN_WINDOWS_CSC%$q $f$q" || EXIT /B 1
|
||||
) ELSE (
|
||||
CALL "%PGADMIN_INNOTOOL_DIR%\ISCC.exe" "%WD%\pkg\win32\installer.iss" || EXIT /B 1
|
||||
)
|
||||
@@ -489,22 +493,18 @@ REM Main build sequence Ends
|
||||
EXIT /B 0
|
||||
|
||||
|
||||
REM Sign one or more files, passed as quoted arguments. Signing is done in a
|
||||
REM single signtool invocation as the hardware token prompts for a PIN on the
|
||||
REM first signature of a session.
|
||||
REM
|
||||
REM The file digest is SHA-1 because that is all the build host's key can do:
|
||||
REM the certificate is bound to the Certum card's legacy CryptoAPI provider,
|
||||
REM crypto3 CSP, and asking it for a SHA-256 digest fails in SignerSign() with
|
||||
REM 0xc0000225 before the PIN is even requested. Moving to SHA-256 means
|
||||
REM rebinding the certificate to the CNG provider first; see pkg\win32\SIGNING.md.
|
||||
REM The same flag appears in the Inno Setup signtool string in :CREATE_INSTALLER.
|
||||
REM Sign one or more files, passed as quoted arguments, with a SHA-256 file
|
||||
REM digest. pkg\win32\sign-files.bat does the work, in signtool's split
|
||||
REM digest, sign and ingest steps, because the one-step form cannot use a
|
||||
REM SHA-2 digest with the card's key; the script explains why. All the files
|
||||
REM go in one call because each call costs one PIN prompt, and their names
|
||||
REM must be unique, which the script checks.
|
||||
:SIGN_FILES
|
||||
IF "%PGADMIN_WINDOWS_CSC%" == "" EXIT /B 0
|
||||
|
||||
CALL "%PGADMIN_SIGNTOOL_DIR%\signtool.exe" sign /sm /n "%PGADMIN_WINDOWS_CSC%" /tr http://timestamp.digicert.com /td sha256 /fd sha1 /v %*
|
||||
CALL "%WD%\pkg\win32\sign-files.bat" "%PGADMIN_WINDOWS_CSC%" %*
|
||||
REM IF ERRORLEVEL, not IF %ERRORLEVEL%: the latter would be expanded before
|
||||
REM signtool had run were this ever moved inside a parenthesised block.
|
||||
REM the script had run were this ever moved inside a parenthesised block.
|
||||
IF ERRORLEVEL 1 (
|
||||
ECHO.
|
||||
ECHO ************************************************************
|
||||
|
||||
@@ -191,38 +191,71 @@ update does not require editing anything.
|
||||
## Certificate setup
|
||||
|
||||
The card's "simple" registration through proCertum Card Manager does not
|
||||
correctly link the certificate to its private key, and without the repair
|
||||
below signing fails.
|
||||
correctly link the certificate to its private key, and without the repair in
|
||||
step 4 signing fails.
|
||||
|
||||
**All of this needs local console access.** Smart card operations do not work
|
||||
over Remote Desktop, and the usual symptom is `certutil -key` failing with
|
||||
`NTE_KEYSET_NOT_DEF (0x80090019)`.
|
||||
|
||||
### 0. Before starting
|
||||
|
||||
Take a VM snapshot, or a full backup if the host is physical. Install the ACS
|
||||
CCID reader driver, proCertum Card Manager and the Windows SDK's Signing Tools;
|
||||
the versions known to work are driver 1.0.6.1, Card Manager 4.14.0 and SDK
|
||||
10.0.26100.0. Keep the Common profile PIN to hand, remembering that wrong PINs
|
||||
count towards blocking the card.
|
||||
|
||||
### 1. Put proCertum in CSP mode
|
||||
|
||||
Open proCertum Card Manager, go to Options, select **CSP driver** rather than
|
||||
Minidriver, apply, and restart Windows.
|
||||
|
||||
### 2. Import the certificate to the machine store
|
||||
|
||||
In proCertum Card Manager: Read Card, then the Common Profile tab, select the
|
||||
code signing certificate, Show certificate details, and install it into the
|
||||
Windows **machine** store, under Personal. The machine store is why the build
|
||||
signs with `/sm`.
|
||||
|
||||
### 3. Link the certificate to the key
|
||||
|
||||
`certutil -store My` will show the certificate with `No key provider
|
||||
information`, which is expected at this point.
|
||||
|
||||
Find the key container:
|
||||
Open proCertum Card Manager, go to Options, set the driver for the Common
|
||||
profile to **CSP** rather than Minidriver, leave "Enable PIN cache for
|
||||
CSP-based applications" on, apply, and restart Windows. Then check:
|
||||
|
||||
```batch
|
||||
certutil -key -csp "crypto3 CSP"
|
||||
certutil -csplist | findstr /i "crypto3 Certum"
|
||||
```
|
||||
|
||||
Create `keyprov.inf`, substituting the container ID. The trailing `&` on each
|
||||
line is required INF syntax, not a typo:
|
||||
which should list `Provider Name: crypto3 CSP` with
|
||||
`Provider Type: 1 - PROV_RSA_FULL`.
|
||||
|
||||
### 2. Check the key is reachable
|
||||
|
||||
```batch
|
||||
certutil -csp "crypto3 CSP" -key
|
||||
```
|
||||
|
||||
should list one container, marked `[Default Container]`, with `RSA` and
|
||||
`AT_KEYEXCHANGE, AT_SIGNATURE`; note its ID for step 4. `NTE_KEYSET_NOT_DEF`
|
||||
here means Remote Desktop, the wrong driver mode, no restart since changing
|
||||
it, or a card that is not seated.
|
||||
|
||||
The other Certum providers are no use for this key: `cryptoCertum3 KSP` only
|
||||
sees the card's Secure profile, and the code-signing key is in the Common
|
||||
profile, whilst `cryptoCertum3 CSP` fails with `NTE_BAD_PROVIDER`.
|
||||
|
||||
### 3. Import the certificate to the machine store
|
||||
|
||||
In proCertum Card Manager: Read Card, then the Common Profile tab, select the
|
||||
code signing certificate, Show certificate details, and Install certificate
|
||||
into the Windows **machine** store (Local Machine, Personal). The machine
|
||||
store is why the build signs with `/sm`.
|
||||
|
||||
`certutil -store My` will then show the certificate, issued by `Certum Code
|
||||
Signing 2021 CA`, with its `Cert Hash(sha1)` thumbprint and `No key provider
|
||||
information`, which is expected at this point.
|
||||
|
||||
### 4. Link the certificate to the key
|
||||
|
||||
As Administrator, first save the certificate's current properties, which are
|
||||
what a rollback puts back:
|
||||
|
||||
```batch
|
||||
certutil -store -v My "<thumbprint>" > before-repair.txt
|
||||
```
|
||||
|
||||
Create `keyprov.inf`, substituting the container ID from step 2. The trailing
|
||||
`&` on each line is required INF syntax, not a typo:
|
||||
|
||||
```ini
|
||||
[Properties]
|
||||
@@ -234,15 +267,55 @@ line is required INF syntax, not a typo:
|
||||
_continue_ = "KeySpec=2"
|
||||
```
|
||||
|
||||
Find the certificate's SHA1 thumbprint with `certutil -store My`, looking for
|
||||
`Cert Hash(sha1)`, and repair the store as Administrator:
|
||||
Then repair the store and check it:
|
||||
|
||||
```batch
|
||||
certutil -repairstore My "<thumbprint>" keyprov.inf
|
||||
certutil -store My "<thumbprint>"
|
||||
```
|
||||
|
||||
`certutil -store My` should now report `Provider = crypto3 CSP` and a
|
||||
`Key Container`.
|
||||
The second command runs a signature test, so it raises one PIN prompt, and
|
||||
should report the `Key Container`, `Provider = crypto3 CSP`, `Private key is
|
||||
NOT exportable` and `Signature test passed`. If the test does not pass, check
|
||||
the container ID, that the provider name is exactly `crypto3 CSP`, and
|
||||
`KeySpec=2`.
|
||||
|
||||
Only the certificate's store properties change here; the key on the card is
|
||||
not touched. To undo it, rerun `-repairstore` with the values saved in
|
||||
`before-repair.txt`, or restore the snapshot.
|
||||
|
||||
### 5. Prove signing works
|
||||
|
||||
On a throwaway copy of any unsigned executable, run the same steps the build
|
||||
does (see below), at the console so that the PIN prompt can be answered by
|
||||
hand if the helper is not yet running:
|
||||
|
||||
```batch
|
||||
powershell -NoProfile -Command "[IO.File]::WriteAllBytes('signer.cer', (Get-Item Cert:\LocalMachine\My\<thumbprint>).Export('Cert'))"
|
||||
mkdir dig
|
||||
signtool sign /dg dig /fd sha256 /f signer.cer test.exe
|
||||
signtool sign /ds /sm /n "<certificate subject>" /fd sha256 dig\test.exe.dig
|
||||
signtool sign /di dig test.exe
|
||||
signtool timestamp /tr http://timestamp.digicert.com /td sha256 test.exe
|
||||
signtool verify /pa /v test.exe
|
||||
```
|
||||
|
||||
Only the `/ds` step prompts for the PIN. Each sign step should report
|
||||
`Successfully signed`, the timestamp `Successfully timestamped`, and the
|
||||
verification a `Hash of file (sha256)`, `The signature is timestamped` and
|
||||
`Successfully verified`.
|
||||
|
||||
The one-step form, `signtool sign /sm /n "<certificate subject>" /fd sha256
|
||||
... test.exe`, fails after about a second, without asking for the PIN, with
|
||||
`SignerSign() failed. (-1073741275/0xc0000225)`. That is expected, and is not
|
||||
a setup fault; see below.
|
||||
|
||||
### Undoing it all
|
||||
|
||||
Restore the snapshot. Without one, set the proCertum driver back and restart,
|
||||
then put the certificate's properties back as in step 4. Deleting and
|
||||
reimporting the certificate in the store would probably also work, but whether
|
||||
that can delete the key on the card was not tested, so prefer the snapshot.
|
||||
|
||||
## How the build signs
|
||||
|
||||
@@ -253,36 +326,71 @@ certificate renewal is a settings change rather than a commit. The snapshot
|
||||
workflow fails immediately if it is unset, rather than quietly producing an
|
||||
unsigned installer that looks like a successful build.
|
||||
|
||||
The signing call it makes is equivalent to:
|
||||
All signing goes through `pkg\win32\sign-files.bat`, called by `Make.bat` for
|
||||
the components it signs, and by Inno Setup (through `cmd /c`) for the
|
||||
installer and its uninstaller. For a set of files, it:
|
||||
|
||||
```batch
|
||||
signtool sign /sm /n "<certificate subject>" /tr http://timestamp.digicert.com /td sha256 /fd sha1 /v <file>
|
||||
```
|
||||
1. exports the certificate's public part from the machine store, requiring
|
||||
exactly one certificate whose name is the subject, with a private key;
|
||||
2. `signtool sign /dg <dir> /fd sha256 /f signer.cer <files>` writes each
|
||||
file's SHA-256 digest into a private working directory, without the key;
|
||||
3. `signtool sign /ds /sm /n "<subject>" /fd sha256 <dir>\<file>.dig ...`
|
||||
signs the digests with the card, which is the only step that uses it;
|
||||
4. `signtool sign /di <dir> <files>` puts the signatures into the files;
|
||||
5. `signtool timestamp /tr http://timestamp.digicert.com /td sha256 <files>`
|
||||
adds the timestamp, which the split steps cannot do as part of signing;
|
||||
6. `signtool verify /pa <files>` checks the result.
|
||||
|
||||
| Parameter | Why |
|
||||
|---|---|
|
||||
| `/sm` | machine store, which is where the certificate was installed |
|
||||
| `/n` | selects the certificate by subject name |
|
||||
| `/fd sha1` | file digest algorithm; see below for why it is not SHA-256 |
|
||||
| `/tr` | RFC 3161 timestamp server; prefer this over the legacy `/t` |
|
||||
| `/td sha256` | timestamp digest algorithm |
|
||||
`/dg` names each digest after the file's name alone, so two files with the
|
||||
same name in one call would overwrite each other's digest; the script refuses
|
||||
such a call rather than sign one file with the other's signature. Everything
|
||||
`Make.bat` signs in one call comes from the one directory, so this does not
|
||||
arise in the build.
|
||||
|
||||
The file digest is SHA-1 because the key, bound through step 3 to the legacy
|
||||
CryptoAPI provider `crypto3 CSP`, cannot produce anything else: `/fd sha256`
|
||||
fails within a couple of seconds, before the PIN is requested, with
|
||||
`Error: SignerSign() failed. (-1073741275/0xc0000225)`. The timestamp digest is
|
||||
unaffected, since the card's key plays no part in the timestamp, which is why
|
||||
`/td sha256` works. Getting to SHA-256 file digests means binding the certificate to
|
||||
Certum's CNG provider, `cryptoCertum3 KSP`, instead. That is untested; before
|
||||
switching the build over, sign a scratch copy of an executable with each digest
|
||||
at the console, and snapshot the host before changing the binding.
|
||||
Each `/ds` process asks for the PIN once, however many files it signs, and the
|
||||
PIN is not cached between processes, so a build raises three prompts: one for
|
||||
the components, and one each for Inno's installer and uninstaller. Each of
|
||||
Inno's retries (`SignToolRetryCount`, 2 by default) is another prompt. The
|
||||
helper answers them all.
|
||||
|
||||
### Why not a single `signtool sign /fd sha256`
|
||||
|
||||
That is the usual command, and it fails here, as step 5 above shows. The card,
|
||||
the CSP and the binding are all capable of SHA-256: the CSP signs a SHA-256
|
||||
hash correctly through CryptoAPI, and the split steps work. The failure is in
|
||||
crypt32's message-signing code, which signtool uses through `SignerSign()`.
|
||||
For a SHA-2 digest it does not sign with a legacy CryptoAPI key directly, but
|
||||
first converts the key handle to a CNG one with `NCryptTranslateHandle`,
|
||||
which, as
|
||||
[Microsoft documents](https://learn.microsoft.com/en-us/windows/win32/api/ncrypt/nf-ncrypt-ncrypttranslatehandle),
|
||||
only works when a CNG key storage provider is registered under the CSP's name
|
||||
or an alias of it. None is registered for `crypto3 CSP` (Certum's
|
||||
`cryptoCertum3 KSP` aliases only `cryptoCertum3 CSP`), so the call fails with
|
||||
`STATUS_NOT_FOUND`, 0xc0000225, before the card is used. SHA-1 takes the older
|
||||
CryptoAPI path, which is why it worked. That `NCryptTranslateHandle` is the
|
||||
failing call is inferred from it returning the same code when called directly,
|
||||
rather than confirmed under a debugger, and why `/ds` avoids the conversion is
|
||||
observed rather than documented.
|
||||
|
||||
Binding the certificate to `cryptoCertum3 KSP` instead cannot work, as that
|
||||
provider cannot see the code-signing key (step 2).
|
||||
|
||||
Certum's own answer to SHA-2 problems with signtool, in their manual *Code
|
||||
Signing: Using Signtool and Jarsigner*, is to switch proCertum to Minidriver
|
||||
mode, which would make the one-step command work through Microsoft's Smart
|
||||
Card Key Storage Provider. That is untested here. It changes the driver and the
|
||||
certificate binding, so it needs this setup redone and a snapshot first, and
|
||||
the PIN prompt would probably become a Windows Security dialog, which the
|
||||
helper does not answer.
|
||||
|
||||
### Timestamps and verification
|
||||
|
||||
Timestamping matters: without it, signatures stop validating when the
|
||||
certificate expires. If DigiCert's server is unavailable,
|
||||
`http://timestamp.sectigo.com` and `http://ts.ssl.com` are alternatives.
|
||||
Certum's own, `http://time.certum.pl`, was unreliable during setup.
|
||||
|
||||
Verify with:
|
||||
To check a signed file by hand:
|
||||
|
||||
```batch
|
||||
signtool verify /pa /v <file>
|
||||
@@ -295,13 +403,21 @@ Remote Desktop: smart card operations need a local console session. Otherwise
|
||||
check proCertum is in CSP mode rather than Minidriver, that Windows was
|
||||
restarted after changing it, and that the card is seated and readable.
|
||||
|
||||
**"No certificates were found that met all the given criteria".** Check `/sm`
|
||||
is being passed, that `certutil -store My` shows the certificate, and that the
|
||||
key provider link from step 3 exists.
|
||||
**"No certificates were found that met all the given criteria", or
|
||||
`sign-files: expected exactly one certificate`.** Check that
|
||||
`certutil -store My` shows the certificate, that `PGADMIN_WINDOWS_CSC` is its
|
||||
subject name exactly, that no second certificate (an expired predecessor, say)
|
||||
has the same name, and that the key provider link from step 4 exists.
|
||||
|
||||
**`SignerSign() failed`.** Check the card is inserted and readable, that the
|
||||
PIN prompt has not appeared behind another window, and that the
|
||||
certificate-to-key link is intact.
|
||||
**`SignerSign() failed. (-1073741275/0xc0000225)`, with no PIN prompt.**
|
||||
Something has run the one-step `signtool sign /fd sha256`, which cannot work
|
||||
with this key; see 'Why not a single `signtool sign /fd sha256`'. Sign through
|
||||
`sign-files.bat` instead.
|
||||
|
||||
**Any other `SignerSign() failed`.** Check the card is inserted and readable,
|
||||
that the PIN prompt has not appeared behind another window, and that the
|
||||
certificate-to-key link is intact. A failed call leaves its working files in
|
||||
the directory it names, under `%TEMP%`.
|
||||
|
||||
**The build hangs rather than failing.** The runner is almost certainly running
|
||||
as a service, or as a scheduled task set to "run whether user is logged on or
|
||||
@@ -317,9 +433,11 @@ is wrong, which needs the card checking before it is retried enough times to
|
||||
block. Recovery is to log in to the console, clear whatever is on screen,
|
||||
restart the helper, and rerun the job.
|
||||
|
||||
**The first signature after the card is inserted prompts for a PIN.** Expected.
|
||||
Subsequent operations in the same session may be cached, depending on proCertum
|
||||
settings.
|
||||
**Every signing call prompts for the PIN.** Expected: the PIN is asked for
|
||||
once per process that uses the key, and is not cached between processes, so a
|
||||
build raises one prompt for the components and one each for the installer and
|
||||
the uninstaller. `certutil -store My` raises one too, because it runs a
|
||||
signature test.
|
||||
|
||||
## Fully unattended signing
|
||||
|
||||
|
||||
@@ -41,16 +41,20 @@ Persistent
|
||||
; The proCertum software is localised, and the host runs it in Polish, so the
|
||||
; dialog is titled "Logowanie do profilu zwyklego" ("log in to the common
|
||||
; profile"), with a Polish barred l in the last word, rather than anything in
|
||||
; English. Only the leading, unaccented part of that is matched, with
|
||||
; English. Only the part of that before the barred l is matched, with
|
||||
; SetTitleMatchMode 2 for a substring match: it survives the wording after it
|
||||
; changing between proCertum releases or profiles, and it keeps this file to
|
||||
; plain ASCII, so that no editor or AutoHotkey version can quietly mangle an
|
||||
; accented character and leave behind a title that never matches. If the
|
||||
; display language is ever changed, this is the line to change with it.
|
||||
; changing between proCertum releases, and it keeps this file to plain ASCII,
|
||||
; so that no editor or AutoHotkey version can quietly mangle an accented
|
||||
; character and leave behind a title that never matches. The match stops no
|
||||
; earlier than "zwyk" because a login to the card's Secure profile, which
|
||||
; has a different PIN, would otherwise match too; nothing in the build uses
|
||||
; that profile, but should anything ever ask, it must not be answered with
|
||||
; this PIN. If the display language is ever changed, this is the line to
|
||||
; change with it.
|
||||
SetTitleMatchMode 2
|
||||
DetectHiddenWindows false
|
||||
|
||||
PIN_DIALOG := "Logowanie do profilu"
|
||||
PIN_DIALOG := "Logowanie do profilu zwyk"
|
||||
|
||||
PIN_DIR := EnvGet("LOCALAPPDATA")
|
||||
if (PIN_DIR = "")
|
||||
|
||||
93
pkg/win32/sign-files.bat
Normal file
93
pkg/win32/sign-files.bat
Normal file
@@ -0,0 +1,93 @@
|
||||
@ECHO OFF
|
||||
REM Sign one or more files with a SHA-256 file digest:
|
||||
REM
|
||||
REM sign-files.bat "<certificate subject>" "file" ["file" ...]
|
||||
REM
|
||||
REM Called by Make.bat for the components it signs, and by Inno Setup, through
|
||||
REM cmd.exe, for the installer and its uninstaller. signtool.exe is taken from
|
||||
REM PGADMIN_SIGNTOOL_DIR, which Make.bat sets.
|
||||
REM
|
||||
REM The one-step "signtool sign /fd sha256" cannot be used. The card's key is
|
||||
REM reached through Certum's legacy CryptoAPI provider, crypto3 CSP, and for a
|
||||
REM SHA-2 digest Windows' CMS code first converts that key handle to a CNG one
|
||||
REM with NCryptTranslateHandle, which only works when a CNG provider is
|
||||
REM registered under the same name. None is, so signing fails with 0xc0000225
|
||||
REM before the card is touched. signtool's split flow avoids that conversion:
|
||||
REM /dg writes the digests without the key, /ds signs them on the card, /di
|
||||
REM puts the signatures into the files, and the timestamp is added separately
|
||||
REM because /tr cannot be combined with the split flow. See
|
||||
REM pkg\win32\SIGNING.md.
|
||||
REM
|
||||
REM Only /ds uses the card, and it is one signtool process however many files
|
||||
REM it signs, so a call to this script costs one PIN prompt.
|
||||
REM
|
||||
REM Paths containing ! are not supported, as delayed expansion would eat it.
|
||||
SETLOCAL EnableDelayedExpansion
|
||||
|
||||
SET "SIGNTOOL=%PGADMIN_SIGNTOOL_DIR%\signtool.exe"
|
||||
IF NOT EXIST "%SIGNTOOL%" (
|
||||
ECHO sign-files: signtool.exe not found; is PGADMIN_SIGNTOOL_DIR set?
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
SET "SUBJECT=%~1"
|
||||
IF "!SUBJECT!" == "" (
|
||||
ECHO sign-files: no certificate subject given
|
||||
EXIT /B 1
|
||||
)
|
||||
SHIFT
|
||||
|
||||
SET "FILES="
|
||||
:ARGS
|
||||
IF "%~1" == "" GOTO ARGS_DONE
|
||||
SET "FILES=!FILES! "%~f1""
|
||||
SHIFT
|
||||
GOTO ARGS
|
||||
:ARGS_DONE
|
||||
IF "!FILES!" == "" (
|
||||
ECHO sign-files: no files given
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
REM A private working directory per call, so that neither a concurrent run nor
|
||||
REM one that failed and left its files behind can mix digests with this one.
|
||||
SET "WORK=%TEMP%\sign-files-%RANDOM%%RANDOM%"
|
||||
MKDIR "!WORK!" || EXIT /B 1
|
||||
|
||||
REM /dg names each digest after the file's name alone, so two files with the
|
||||
REM same name would overwrite one another's digest and one would be signed
|
||||
REM with the other's signature. Refuse rather than rely on the callers.
|
||||
SET "DIGS="
|
||||
FOR %%f IN (!FILES!) DO (
|
||||
IF EXIST "!WORK!\%%~nxf.name" (
|
||||
ECHO sign-files: more than one file is named %%~nxf; sign them in separate calls
|
||||
GOTO FAIL
|
||||
)
|
||||
TYPE NUL > "!WORK!\%%~nxf.name"
|
||||
SET "DIGS=!DIGS! "!WORK!\%%~nxf.dig""
|
||||
)
|
||||
|
||||
REM /dg needs the signing certificate, but only its public part: exported from
|
||||
REM the machine store here, since asking certutil for it runs a signature test
|
||||
REM and so raises a PIN prompt. The match is on the certificate's simple name,
|
||||
REM and exactly one must match, so a subject that signtool /n would take as a
|
||||
REM substring of a longer name fails here rather than signing with the wrong
|
||||
REM certificate.
|
||||
powershell -NoProfile -NonInteractive -Command "$c = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.GetNameInfo('SimpleName', $false) -eq $env:SUBJECT -and $_.HasPrivateKey }; if (@($c).Count -ne 1) { exit 1 }; [IO.File]::WriteAllBytes((Join-Path $env:WORK 'signer.cer'), $c.Export('Cert'))"
|
||||
IF ERRORLEVEL 1 (
|
||||
ECHO sign-files: expected exactly one certificate named "!SUBJECT!" in the machine store
|
||||
GOTO FAIL
|
||||
)
|
||||
|
||||
"%SIGNTOOL%" sign /dg "!WORK!" /fd sha256 /f "!WORK!\signer.cer" !FILES! || GOTO FAIL
|
||||
"%SIGNTOOL%" sign /ds /sm /n "!SUBJECT!" /fd sha256 !DIGS! || GOTO FAIL
|
||||
"%SIGNTOOL%" sign /di "!WORK!" !FILES! || GOTO FAIL
|
||||
"%SIGNTOOL%" timestamp /tr http://timestamp.digicert.com /td sha256 !FILES! || GOTO FAIL
|
||||
"%SIGNTOOL%" verify /pa !FILES! || GOTO FAIL
|
||||
|
||||
RMDIR /S /Q "!WORK!"
|
||||
EXIT /B 0
|
||||
|
||||
:FAIL
|
||||
ECHO sign-files: signing failed; the working files are in !WORK!
|
||||
EXIT /B 1
|
||||
Reference in New Issue
Block a user