Skip to content

Commit d6d3efb

Browse files
feat(bundler): support custom sign command on Windows (#9865)
* feat(bundler): support custom sign command on Windows closes #7188 closes #9578 * fix double quotes * fix build * fix build * clippy * Update sign.rs * clippy && replace `winreg` with `windows-registry` * remove log [skip ci] * Apply suggestions from code review * tweak arg so path with spaces work on macOS * create nsis toolset paths --------- Co-authored-by: Lucas Nogueira <lucas@tauri.app>
1 parent fc1543c commit d6d3efb

File tree

15 files changed

+279
-150
lines changed

15 files changed

+279
-150
lines changed

.changes/custom-sign-command.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri-bundler": "patch:feat"
3+
---
4+
5+
On Windows, add option to specify a custom signing command to be used. This opens an endless possibilities, for example use `osslsigncode` on non-Windows or use hardware tokens and HSM or even using Azure Trusted Signing.

.changes/utils-sign-command.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri-utils": "patch:feat"
3+
---
4+
5+
Add `sign_command` in `WindowsConfig`

core/tauri-config-schema/schema.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
"certificateThumbprint": null,
113113
"digestAlgorithm": null,
114114
"nsis": null,
115+
"signCommand": null,
115116
"timestampUrl": null,
116117
"tsp": false,
117118
"webviewFixedRuntimePath": null,
@@ -1619,6 +1620,7 @@
16191620
"certificateThumbprint": null,
16201621
"digestAlgorithm": null,
16211622
"nsis": null,
1623+
"signCommand": null,
16221624
"timestampUrl": null,
16231625
"tsp": false,
16241626
"webviewFixedRuntimePath": null,
@@ -1977,6 +1979,13 @@
19771979
"type": "null"
19781980
}
19791981
]
1982+
},
1983+
"signCommand": {
1984+
"description": "Specify a custom command to sign the binaries. This command needs to have a `%1` in it which is just a placeholder for the binary path, which we will detect and replace before calling the command.\n\nExample: ```text sign-cli --arg1 --arg2 %1 ```\n\nBy Default we use `signtool.exe` which can be found only on Windows so if you are on another platform and want to cross-compile and sign you will need to use another tool like `osslsigncode`.",
1985+
"type": [
1986+
"string",
1987+
"null"
1988+
]
19801989
}
19811990
},
19821991
"additionalProperties": false

core/tauri-utils/src/config.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,20 @@ pub struct WindowsConfig {
859859
pub wix: Option<WixConfig>,
860860
/// Configuration for the installer generated with NSIS.
861861
pub nsis: Option<NsisConfig>,
862+
/// Specify a custom command to sign the binaries.
863+
/// This command needs to have a `%1` in it which is just a placeholder for the binary path,
864+
/// which we will detect and replace before calling the command.
865+
///
866+
/// Example:
867+
/// ```text
868+
/// sign-cli --arg1 --arg2 %1
869+
/// ```
870+
///
871+
/// By Default we use `signtool.exe` which can be found only on Windows so
872+
/// if you are on another platform and want to cross-compile and sign you will
873+
/// need to use another tool like `osslsigncode`.
874+
#[serde(alias = "sign-command")]
875+
pub sign_command: Option<String>,
862876
}
863877

864878
impl Default for WindowsConfig {
@@ -873,6 +887,7 @@ impl Default for WindowsConfig {
873887
allow_downgrades: true,
874888
wix: None,
875889
nsis: None,
890+
sign_command: None,
876891
}
877892
}
878893
}

tooling/bundler/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ dunce = "1"
4444
[target."cfg(target_os = \"windows\")".dependencies]
4545
uuid = { version = "1", features = [ "v4", "v5" ] }
4646
bitness = "0.4"
47-
winreg = "0.52"
47+
windows-registry = "0.1.1"
4848
glob = "0.3"
4949

5050
[target."cfg(target_os = \"windows\")".dependencies.windows-sys]

tooling/bundler/src/bundle.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ pub fn bundle_project(settings: Settings) -> crate::Result<Vec<Bundle>> {
6363
log::warn!("Cross-platform compilation is experimental and does not support all features. Please use a matching host system for full compatibility.");
6464
}
6565

66-
#[cfg(target_os = "windows")]
67-
{
66+
if settings.can_sign() {
6867
// Sign windows binaries before the bundling step in case neither wix and nsis bundles are enabled
6968
for bin in settings.binaries() {
7069
let bin_path = settings.binary_path(bin);
@@ -75,16 +74,24 @@ pub fn bundle_project(settings: Settings) -> crate::Result<Vec<Bundle>> {
7574
for bin in settings.external_binaries() {
7675
let path = bin?;
7776
let skip = std::env::var("TAURI_SKIP_SIDECAR_SIGNATURE_CHECK").map_or(false, |v| v == "true");
77+
if skip {
78+
continue;
79+
}
7880

79-
if !skip && windows::sign::verify(&path)? {
81+
#[cfg(windows)]
82+
if windows::sign::verify(&path)? {
8083
log::info!(
8184
"sidecar at \"{}\" already signed. Skipping...",
8285
path.display()
83-
)
84-
} else {
85-
windows::sign::try_sign(&path, &settings)?;
86+
);
87+
continue;
8688
}
89+
90+
windows::sign::try_sign(&path, &settings)?;
8791
}
92+
} else {
93+
#[cfg(not(target_os = "windows"))]
94+
log::warn!("Signing, by default, is only supported on Windows hosts, but you can specify a custom signing command in `bundler > windows > sign_command`, for now, skipping signing the installer...");
8895
}
8996

9097
for package_type in &package_types {

tooling/bundler/src/bundle/settings.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,20 @@ pub struct WindowsSettings {
447447
///
448448
/// /// The default value of this flag is `true`.
449449
pub allow_downgrades: bool,
450+
451+
/// Specify a custom command to sign the binaries.
452+
/// This command needs to have a `%1` in it which is just a placeholder for the binary path,
453+
/// which we will detect and replace before calling the command.
454+
///
455+
/// Example:
456+
/// ```text
457+
/// sign-cli --arg1 --arg2 %1
458+
/// ```
459+
///
460+
/// By Default we use `signtool.exe` which can be found only on Windows so
461+
/// if you are on another platform and want to cross-compile and sign you will
462+
/// need to use another tool like `osslsigncode`.
463+
pub sign_command: Option<String>,
450464
}
451465

452466
impl Default for WindowsSettings {
@@ -462,6 +476,7 @@ impl Default for WindowsSettings {
462476
webview_install_mode: Default::default(),
463477
webview_fixed_runtime_path: None,
464478
allow_downgrades: true,
479+
sign_command: None,
465480
}
466481
}
467482
}

tooling/bundler/src/bundle/windows/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#[cfg(target_os = "windows")]
77
pub mod msi;
88
pub mod nsis;
9-
#[cfg(target_os = "windows")]
109
pub mod sign;
1110

1211
mod util;

tooling/bundler/src/bundle/windows/msi/wix.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,11 @@ pub fn build_wix_app_installer(
798798
&msi_output_path,
799799
)?;
800800
rename(&msi_output_path, &msi_path)?;
801-
try_sign(&msi_path, settings)?;
801+
802+
if settings.can_sign() {
803+
try_sign(&msi_path, settings)?;
804+
}
805+
802806
output_paths.push(msi_path);
803807
}
804808

tooling/bundler/src/bundle/windows/nsis.rs

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
// SPDX-License-Identifier: Apache-2.0
33
// SPDX-License-Identifier: MIT
44

5-
#[cfg(target_os = "windows")]
65
use crate::bundle::windows::sign::{sign_command, try_sign};
6+
77
use crate::{
88
bundle::{
99
common::CommandExt,
@@ -67,6 +67,7 @@ pub fn bundle_project(settings: &Settings, updater: bool) -> crate::Result<Vec<P
6767
let nsis_toolset_path = tauri_tools_path.join("NSIS");
6868

6969
if !nsis_toolset_path.exists() {
70+
create_dir_all(&nsis_toolset_path)?;
7071
get_and_extract_nsis(&nsis_toolset_path, &tauri_tools_path)?;
7172
} else if NSIS_REQUIRED_FILES
7273
.iter()
@@ -114,12 +115,10 @@ fn get_and_extract_nsis(nsis_toolset_path: &Path, _tauri_tools_path: &Path) -> c
114115
NSIS_TAURI_UTILS_SHA1,
115116
HashAlgorithm::Sha1,
116117
)?;
117-
write(
118-
nsis_plugins
119-
.join("x86-unicode")
120-
.join("nsis_tauri_utils.dll"),
121-
data,
122-
)?;
118+
119+
let target_folder = nsis_plugins.join("x86-unicode");
120+
create_dir_all(&target_folder)?;
121+
write(target_folder.join("nsis_tauri_utils.dll"), data)?;
123122

124123
Ok(())
125124
}
@@ -163,9 +162,6 @@ fn build_nsis_app_installer(
163162

164163
log::info!("Target: {}", arch);
165164

166-
#[cfg(not(target_os = "windows"))]
167-
log::info!("Code signing is currently only supported on Windows hosts, skipping...");
168-
169165
let output_path = settings.project_out_directory().join("nsis").join(arch);
170166
if output_path.exists() {
171167
remove_dir_all(&output_path)?;
@@ -197,16 +193,9 @@ fn build_nsis_app_installer(
197193
);
198194
data.insert("copyright", to_json(settings.copyright_string()));
199195

200-
// Code signing is currently only supported on Windows hosts
201-
#[cfg(target_os = "windows")]
202196
if settings.can_sign() {
203-
data.insert(
204-
"uninstaller_sign_cmd",
205-
to_json(format!(
206-
"{:?}",
207-
sign_command("%1", &settings.sign_params())?.0
208-
)),
209-
);
197+
let sign_cmd = format!("{:?}", sign_command("%1", &settings.sign_params())?);
198+
data.insert("uninstaller_sign_cmd", to_json(sign_cmd));
210199
}
211200

212201
let version = settings.version_string();
@@ -517,9 +506,12 @@ fn build_nsis_app_installer(
517506

518507
rename(nsis_output_path, &nsis_installer_path)?;
519508

520-
// Code signing is currently only supported on Windows hosts
521-
#[cfg(target_os = "windows")]
522-
try_sign(&nsis_installer_path, settings)?;
509+
if settings.can_sign() {
510+
try_sign(&nsis_installer_path, settings)?;
511+
} else {
512+
#[cfg(not(target_os = "windows"))]
513+
log::warn!("Signing, by default, is only supported on Windows hosts, but you can specify a custom signing command in `bundler > windows > sign_command`, for now, skipping signing the installer...");
514+
}
523515

524516
Ok(vec![nsis_installer_path])
525517
}

0 commit comments

Comments
 (0)