Skip to content

Commit a4fcaf1

Browse files
fix: don't override default keychain, closes #4008 (#4053)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
1 parent c82b476 commit a4fcaf1

File tree

3 files changed

+189
-143
lines changed

3 files changed

+189
-143
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"cli.rs": patch
3+
"cli.js": patch
4+
---
5+
6+
Don't override the default keychain on macOS while code signing.

tooling/bundler/src/bundle/macos/app.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
use super::{
2525
super::common,
2626
icon::create_icns_file,
27-
sign::{notarize, notarize_auth_args, setup_keychain_if_needed, sign},
27+
sign::{notarize, notarize_auth_args, sign},
2828
};
2929
use crate::Settings;
3030

@@ -81,9 +81,6 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
8181
copy_binaries_to_bundle(&bundle_directory, settings)?;
8282

8383
if let Some(identity) = &settings.macos().signing_identity {
84-
// setup keychain allow you to import your certificate
85-
// for CI build
86-
setup_keychain_if_needed()?;
8784
// sign application
8885
sign(app_bundle_path.clone(), identity, &settings, true)?;
8986
// notarization is required for distribution

tooling/bundler/src/bundle/macos/sign.rs

Lines changed: 182 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// SPDX-License-Identifier: MIT
44

55
use std::{
6+
ffi::OsString,
67
fs::File,
78
io::prelude::*,
89
path::PathBuf,
@@ -12,181 +13,223 @@ use std::{
1213
use crate::{bundle::common, Settings};
1314
use regex::Regex;
1415

16+
const KEYCHAIN_ID: &str = "tauri-build.keychain";
17+
const KEYCHAIN_PWD: &str = "tauri-build";
18+
1519
// Import certificate from ENV variables.
1620
// APPLE_CERTIFICATE is the p12 certificate base64 encoded.
1721
// By example you can use; openssl base64 -in MyCertificate.p12 -out MyCertificate-base64.txt
1822
// Then use the value of the base64 in APPLE_CERTIFICATE env variable.
1923
// You need to set APPLE_CERTIFICATE_PASSWORD to the password you set when youy exported your certificate.
2024
// https://help.apple.com/xcode/mac/current/#/dev154b28f09 see: `Export a signing certificate`
21-
pub fn setup_keychain_if_needed() -> crate::Result<()> {
22-
match (
23-
std::env::var_os("APPLE_CERTIFICATE"),
24-
std::env::var_os("APPLE_CERTIFICATE_PASSWORD"),
25-
) {
26-
(Some(certificate_encoded), Some(certificate_password)) => {
27-
// we delete any previous version of our keychain if present
28-
delete_keychain_if_needed();
29-
common::print_info("setup keychain from environment variables...")?;
30-
31-
let key_chain_id = "tauri-build.keychain";
32-
let key_chain_name = "tauri-build";
33-
let tmp_dir = tempfile::tempdir()?;
34-
let cert_path = tmp_dir
35-
.path()
36-
.join("cert.p12")
37-
.to_string_lossy()
38-
.to_string();
39-
let cert_path_tmp = tmp_dir
40-
.path()
41-
.join("cert.p12.tmp")
42-
.to_string_lossy()
43-
.to_string();
44-
let certificate_encoded = certificate_encoded
45-
.to_str()
46-
.expect("failed to convert APPLE_CERTIFICATE to string")
47-
.as_bytes();
25+
pub fn setup_keychain(
26+
certificate_encoded: OsString,
27+
certificate_password: OsString,
28+
) -> crate::Result<()> {
29+
// we delete any previous version of our keychain if present
30+
delete_keychain();
31+
common::print_info("setup keychain from environment variables...")?;
4832

49-
let certificate_password = certificate_password
50-
.to_str()
51-
.expect("failed to convert APPLE_CERTIFICATE_PASSWORD to string")
52-
.to_string();
33+
let keychain_list_output = Command::new("security")
34+
.args(["list-keychain", "-d", "user"])
35+
.output()?;
5336

54-
// as certificate contain whitespace decoding may be broken
55-
// https://github.com/marshallpierce/rust-base64/issues/105
56-
// we'll use builtin base64 command from the OS
57-
let mut tmp_cert = File::create(cert_path_tmp.clone())?;
58-
tmp_cert.write_all(certificate_encoded)?;
37+
let tmp_dir = tempfile::tempdir()?;
38+
let cert_path = tmp_dir
39+
.path()
40+
.join("cert.p12")
41+
.to_string_lossy()
42+
.to_string();
43+
let cert_path_tmp = tmp_dir
44+
.path()
45+
.join("cert.p12.tmp")
46+
.to_string_lossy()
47+
.to_string();
48+
let certificate_encoded = certificate_encoded
49+
.to_str()
50+
.expect("failed to convert APPLE_CERTIFICATE to string")
51+
.as_bytes();
5952

60-
let decode_certificate = Command::new("base64")
61-
.args(vec!["--decode", "-i", &cert_path_tmp, "-o", &cert_path])
62-
.stderr(Stdio::piped())
63-
.status()?;
53+
let certificate_password = certificate_password
54+
.to_str()
55+
.expect("failed to convert APPLE_CERTIFICATE_PASSWORD to string")
56+
.to_string();
57+
58+
// as certificate contain whitespace decoding may be broken
59+
// https://github.com/marshallpierce/rust-base64/issues/105
60+
// we'll use builtin base64 command from the OS
61+
let mut tmp_cert = File::create(cert_path_tmp.clone())?;
62+
tmp_cert.write_all(certificate_encoded)?;
63+
64+
let decode_certificate = Command::new("base64")
65+
.args(["--decode", "-i", &cert_path_tmp, "-o", &cert_path])
66+
.stderr(Stdio::piped())
67+
.status()?;
6468

65-
if !decode_certificate.success() {
66-
return Err(anyhow::anyhow!("failed to decode certificate",).into());
67-
}
69+
if !decode_certificate.success() {
70+
return Err(anyhow::anyhow!("failed to decode certificate",).into());
71+
}
6872

69-
let create_key_chain = Command::new("security")
70-
.args(vec!["create-keychain", "-p", key_chain_name, key_chain_id])
71-
.stdout(Stdio::piped())
72-
.stderr(Stdio::piped())
73-
.status()?;
73+
let create_key_chain = Command::new("security")
74+
.args(["create-keychain", "-p", KEYCHAIN_PWD, KEYCHAIN_ID])
75+
.stdout(Stdio::piped())
76+
.stderr(Stdio::piped())
77+
.status()?;
7478

75-
if !create_key_chain.success() {
76-
return Err(anyhow::anyhow!("failed to create keychain",).into());
77-
}
79+
if !create_key_chain.success() {
80+
return Err(anyhow::anyhow!("failed to create keychain",).into());
81+
}
7882

79-
let set_default_keychain = Command::new("security")
80-
.args(vec!["default-keychain", "-s", key_chain_id])
81-
.stdout(Stdio::piped())
82-
.stderr(Stdio::piped())
83-
.status()?;
83+
let unlock_keychain = Command::new("security")
84+
.args(["unlock-keychain", "-p", KEYCHAIN_PWD, KEYCHAIN_ID])
85+
.stdout(Stdio::piped())
86+
.stderr(Stdio::piped())
87+
.status()?;
8488

85-
if !set_default_keychain.success() {
86-
return Err(anyhow::anyhow!("failed to set default keychain",).into());
87-
}
89+
if !unlock_keychain.success() {
90+
return Err(anyhow::anyhow!("failed to set unlock keychain",).into());
91+
}
8892

89-
let unlock_keychain = Command::new("security")
90-
.args(vec!["unlock-keychain", "-p", key_chain_name, key_chain_id])
91-
.stdout(Stdio::piped())
92-
.stderr(Stdio::piped())
93-
.status()?;
93+
let import_certificate = Command::new("security")
94+
.args([
95+
"import",
96+
&cert_path,
97+
"-k",
98+
KEYCHAIN_ID,
99+
"-P",
100+
&certificate_password,
101+
"-T",
102+
"/usr/bin/codesign",
103+
"-T",
104+
"/usr/bin/pkgbuild",
105+
"-T",
106+
"/usr/bin/productbuild",
107+
])
108+
.stderr(Stdio::inherit())
109+
.output()?;
94110

95-
if !unlock_keychain.success() {
96-
return Err(anyhow::anyhow!("failed to set unlock keychain",).into());
97-
}
111+
if !import_certificate.status.success() {
112+
return Err(
113+
anyhow::anyhow!(format!(
114+
"failed to import keychain certificate {:?}",
115+
std::str::from_utf8(&import_certificate.stdout)
116+
))
117+
.into(),
118+
);
119+
}
98120

99-
let import_certificate = Command::new("security")
100-
.arg("import")
101-
.arg(cert_path)
102-
.arg("-k")
103-
.arg(key_chain_id)
104-
.arg("-P")
105-
.arg(certificate_password)
106-
.arg("-T")
107-
.arg("/usr/bin/codesign")
108-
.arg("-T")
109-
.arg("/usr/bin/pkgbuild")
110-
.arg("-T")
111-
.arg("/usr/bin/productbuild")
112-
.stderr(Stdio::inherit())
113-
.output()?;
114-
115-
if !import_certificate.status.success() {
116-
return Err(
117-
anyhow::anyhow!(format!(
118-
"failed to import keychain certificate {:?}",
119-
std::str::from_utf8(&import_certificate.stdout)
120-
))
121-
.into(),
122-
);
123-
}
121+
let settings_keychain = Command::new("security")
122+
.args(["set-keychain-settings", "-t", "3600", "-u", KEYCHAIN_ID])
123+
.stdout(Stdio::piped())
124+
.stderr(Stdio::piped())
125+
.status()?;
124126

125-
let settings_keychain = Command::new("security")
126-
.args(vec![
127-
"set-keychain-settings",
128-
"-t",
129-
"3600",
130-
"-u",
131-
key_chain_id,
132-
])
133-
.stdout(Stdio::piped())
134-
.stderr(Stdio::piped())
135-
.status()?;
136-
137-
if !settings_keychain.success() {
138-
return Err(anyhow::anyhow!("failed to set keychain settings",).into());
139-
}
127+
if !settings_keychain.success() {
128+
return Err(anyhow::anyhow!("failed to set keychain settings",).into());
129+
}
140130

141-
let partition_list = Command::new("security")
142-
.args(vec![
143-
"set-key-partition-list",
144-
"-S",
145-
"apple-tool:,apple:,codesign:",
146-
"-s",
147-
"-k",
148-
key_chain_name,
149-
key_chain_id,
150-
])
151-
.stdout(Stdio::piped())
152-
.stderr(Stdio::piped())
153-
.status()?;
154-
155-
if !partition_list.success() {
156-
return Err(anyhow::anyhow!("failed to set keychain settings",).into());
157-
}
131+
let partition_list = Command::new("security")
132+
.args([
133+
"set-key-partition-list",
134+
"-S",
135+
"apple-tool:,apple:,codesign:",
136+
"-s",
137+
"-k",
138+
KEYCHAIN_PWD,
139+
KEYCHAIN_ID,
140+
])
141+
.stdout(Stdio::piped())
142+
.stderr(Stdio::piped())
143+
.status()?;
158144

159-
Ok(())
160-
}
161-
// skip it
162-
_ => Ok(()),
145+
if !partition_list.success() {
146+
return Err(anyhow::anyhow!("failed to set keychain settings",).into());
147+
}
148+
149+
let current_keychains = String::from_utf8_lossy(&keychain_list_output.stdout)
150+
.split('\n')
151+
.map(|line| {
152+
line
153+
.trim_matches(|c: char| c.is_whitespace() || c == '"')
154+
.to_string()
155+
})
156+
.filter(|l| !l.is_empty())
157+
.collect::<Vec<String>>();
158+
159+
let set_keychain_list_entry = Command::new("security")
160+
.args(["list-keychain", "-d", "user", "-s"])
161+
.args(current_keychains)
162+
.arg(KEYCHAIN_ID)
163+
.stdout(Stdio::piped())
164+
.stderr(Stdio::piped())
165+
.status()?;
166+
167+
if !set_keychain_list_entry.success() {
168+
return Err(anyhow::anyhow!("failed to list keychain",).into());
163169
}
170+
171+
Ok(())
164172
}
165173

166-
pub fn delete_keychain_if_needed() {
167-
if let (Some(_cert), Some(_password)) = (
174+
pub fn delete_keychain() {
175+
// delete keychain if needed and skip any error
176+
let _ = Command::new("security")
177+
.arg("delete-keychain")
178+
.arg(KEYCHAIN_ID)
179+
.stdout(Stdio::piped())
180+
.stderr(Stdio::piped())
181+
.status();
182+
}
183+
184+
pub fn sign(
185+
path_to_sign: PathBuf,
186+
identity: &str,
187+
settings: &Settings,
188+
is_an_executable: bool,
189+
) -> crate::Result<()> {
190+
let setup_keychain = if let (Some(certificate_encoded), Some(certificate_password)) = (
168191
std::env::var_os("APPLE_CERTIFICATE"),
169192
std::env::var_os("APPLE_CERTIFICATE_PASSWORD"),
170193
) {
171-
let key_chain_id = "tauri-build.keychain";
172-
// delete keychain if needed and skip any error
173-
let _result = Command::new("security")
174-
.arg("delete-keychain")
175-
.arg(key_chain_id)
176-
.stdout(Stdio::piped())
177-
.stderr(Stdio::piped())
178-
.status();
194+
// setup keychain allow you to import your certificate
195+
// for CI build
196+
setup_keychain(certificate_encoded, certificate_password)?;
197+
true
198+
} else {
199+
false
200+
};
201+
202+
let res = try_sign(
203+
path_to_sign,
204+
identity,
205+
settings,
206+
is_an_executable,
207+
setup_keychain,
208+
);
209+
210+
if setup_keychain {
211+
// delete the keychain again after signing
212+
delete_keychain();
179213
}
214+
215+
res
180216
}
181217

182-
pub fn sign(
218+
fn try_sign(
183219
path_to_sign: PathBuf,
184220
identity: &str,
185221
settings: &Settings,
186222
is_an_executable: bool,
223+
tauri_keychain: bool,
187224
) -> crate::Result<()> {
188225
common::print_info(format!(r#"signing with identity "{}""#, identity).as_str())?;
189226
let mut args = vec!["--force", "-s", identity];
227+
228+
if tauri_keychain {
229+
args.push("--keychain");
230+
args.push(KEYCHAIN_ID);
231+
}
232+
190233
if let Some(entitlements_path) = &settings.macos().entitlements {
191234
common::print_info(format!("using entitlements file at {}", entitlements_path).as_str())?;
192235
args.push("--entitlements");

0 commit comments

Comments
 (0)