UNPKG

10.7 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.isOldWin6 = exports.doSign = exports.getCertificateFromStoreInfo = exports.getCertInfo = exports.sign = exports.getSignVendorPath = void 0;
4const util_1 = require("builder-util/out/util");
5const binDownload_1 = require("../binDownload");
6const appBuilder_1 = require("../util/appBuilder");
7const bundledTool_1 = require("../util/bundledTool");
8const fs_extra_1 = require("fs-extra");
9const os = require("os");
10const path = require("path");
11const platformPackager_1 = require("../platformPackager");
12const flags_1 = require("../util/flags");
13const vm_1 = require("../vm/vm");
14function getSignVendorPath() {
15 return (0, binDownload_1.getBin)("winCodeSign");
16}
17exports.getSignVendorPath = getSignVendorPath;
18async function sign(options, packager) {
19 let hashes = options.options.signingHashAlgorithms;
20 // msi does not support dual-signing
21 if (options.path.endsWith(".msi")) {
22 hashes = [hashes != null && !hashes.includes("sha1") ? "sha256" : "sha1"];
23 }
24 else if (options.path.endsWith(".appx")) {
25 hashes = ["sha256"];
26 }
27 else if (hashes == null) {
28 hashes = ["sha1", "sha256"];
29 }
30 else {
31 hashes = Array.isArray(hashes) ? hashes : [hashes];
32 }
33 const executor = (await (0, platformPackager_1.resolveFunction)(packager.appInfo.type, options.options.sign, "sign")) || doSign;
34 let isNest = false;
35 for (const hash of hashes) {
36 const taskConfiguration = { ...options, hash, isNest };
37 await Promise.resolve(executor({
38 ...taskConfiguration,
39 computeSignToolArgs: isWin => computeSignToolArgs(taskConfiguration, isWin),
40 }, packager));
41 isNest = true;
42 if (taskConfiguration.resultOutputPath != null) {
43 await (0, fs_extra_1.rename)(taskConfiguration.resultOutputPath, options.path);
44 }
45 }
46 return true;
47}
48exports.sign = sign;
49async function getCertInfo(file, password) {
50 let result = null;
51 const errorMessagePrefix = "Cannot extract publisher name from code signing certificate. As workaround, set win.publisherName. Error: ";
52 try {
53 result = await (0, appBuilder_1.executeAppBuilderAsJson)(["certificate-info", "--input", file, "--password", password]);
54 }
55 catch (e) {
56 throw new Error(`${errorMessagePrefix}${e.stack || e}`);
57 }
58 if (result.error != null) {
59 // noinspection ExceptionCaughtLocallyJS
60 throw new util_1.InvalidConfigurationError(`${errorMessagePrefix}${result.error}`);
61 }
62 return result;
63}
64exports.getCertInfo = getCertInfo;
65async function getCertificateFromStoreInfo(options, vm) {
66 const certificateSubjectName = options.certificateSubjectName;
67 const certificateSha1 = options.certificateSha1 ? options.certificateSha1.toUpperCase() : options.certificateSha1;
68 // ExcludeProperty doesn't work, so, we cannot exclude RawData, it is ok
69 // powershell can return object if the only item
70 const rawResult = await vm.exec("powershell.exe", [
71 "-NoProfile",
72 "-NonInteractive",
73 "-Command",
74 "Get-ChildItem -Recurse Cert: -CodeSigningCert | Select-Object -Property Subject,PSParentPath,Thumbprint | ConvertTo-Json -Compress",
75 ]);
76 const certList = rawResult.length === 0 ? [] : (0, util_1.asArray)(JSON.parse(rawResult));
77 for (const certInfo of certList) {
78 if ((certificateSubjectName != null && !certInfo.Subject.includes(certificateSubjectName)) ||
79 (certificateSha1 != null && certInfo.Thumbprint.toUpperCase() !== certificateSha1)) {
80 continue;
81 }
82 const parentPath = certInfo.PSParentPath;
83 const store = parentPath.substring(parentPath.lastIndexOf("\\") + 1);
84 util_1.log.debug({ store, PSParentPath: parentPath }, "auto-detect certificate store");
85 // https://github.com/electron-userland/electron-builder/issues/1717
86 const isLocalMachineStore = parentPath.includes("Certificate::LocalMachine");
87 util_1.log.debug(null, "auto-detect using of LocalMachine store");
88 return {
89 thumbprint: certInfo.Thumbprint,
90 subject: certInfo.Subject,
91 store,
92 isLocalMachineStore,
93 };
94 }
95 throw new Error(`Cannot find certificate ${certificateSubjectName || certificateSha1}, all certs: ${rawResult}`);
96}
97exports.getCertificateFromStoreInfo = getCertificateFromStoreInfo;
98async function doSign(configuration, packager) {
99 // https://github.com/electron-userland/electron-builder/pull/1944
100 const timeout = parseInt(process.env.SIGNTOOL_TIMEOUT, 10) || 10 * 60 * 1000;
101 // decide runtime argument by cases
102 let args;
103 let env = process.env;
104 let vm;
105 const vmRequired = configuration.path.endsWith(".appx") || !("file" in configuration.cscInfo); /* certificateSubjectName and other such options */
106 const isWin = process.platform === "win32" || vmRequired;
107 const toolInfo = await getToolPath(isWin);
108 const tool = toolInfo.path;
109 if (vmRequired) {
110 vm = await packager.vm.value;
111 args = computeSignToolArgs(configuration, isWin, vm);
112 }
113 else {
114 vm = new vm_1.VmManager();
115 args = configuration.computeSignToolArgs(isWin);
116 if (toolInfo.env != null) {
117 env = toolInfo.env;
118 }
119 }
120 try {
121 await vm.exec(tool, args, { timeout, env });
122 }
123 catch (e) {
124 if (e.message.includes("The file is being used by another process") || e.message.includes("The specified timestamp server either could not be reached")) {
125 util_1.log.warn(`First attempt to code sign failed, another attempt will be made in 15 seconds: ${e.message}`);
126 await new Promise((resolve, reject) => {
127 setTimeout(() => {
128 vm.exec(tool, args, { timeout, env }).then(resolve).catch(reject);
129 }, 15000);
130 });
131 }
132 throw e;
133 }
134}
135exports.doSign = doSign;
136// on windows be aware of http://stackoverflow.com/a/32640183/1910191
137function computeSignToolArgs(options, isWin, vm = new vm_1.VmManager()) {
138 const inputFile = vm.toVmFile(options.path);
139 const outputPath = isWin ? inputFile : getOutputPath(inputFile, options.hash);
140 if (!isWin) {
141 options.resultOutputPath = outputPath;
142 }
143 const args = isWin ? ["sign"] : ["-in", inputFile, "-out", outputPath];
144 if (process.env.ELECTRON_BUILDER_OFFLINE !== "true") {
145 const timestampingServiceUrl = options.options.timeStampServer || "http://timestamp.digicert.com";
146 if (isWin) {
147 args.push(options.isNest || options.hash === "sha256" ? "/tr" : "/t", options.isNest || options.hash === "sha256" ? options.options.rfc3161TimeStampServer || "http://timestamp.digicert.com" : timestampingServiceUrl);
148 }
149 else {
150 args.push("-t", timestampingServiceUrl);
151 }
152 }
153 const certificateFile = options.cscInfo.file;
154 if (certificateFile == null) {
155 const cscInfo = options.cscInfo;
156 const subjectName = cscInfo.thumbprint;
157 if (!isWin) {
158 throw new Error(`${subjectName == null ? "certificateSha1" : "certificateSubjectName"} supported only on Windows`);
159 }
160 args.push("/sha1", cscInfo.thumbprint);
161 args.push("/s", cscInfo.store);
162 if (cscInfo.isLocalMachineStore) {
163 args.push("/sm");
164 }
165 }
166 else {
167 const certExtension = path.extname(certificateFile);
168 if (certExtension === ".p12" || certExtension === ".pfx") {
169 args.push(isWin ? "/f" : "-pkcs12", vm.toVmFile(certificateFile));
170 }
171 else {
172 throw new Error(`Please specify pkcs12 (.p12/.pfx) file, ${certificateFile} is not correct`);
173 }
174 }
175 if (!isWin || options.hash !== "sha1") {
176 args.push(isWin ? "/fd" : "-h", options.hash);
177 if (isWin && process.env.ELECTRON_BUILDER_OFFLINE !== "true") {
178 args.push("/td", "sha256");
179 }
180 }
181 if (options.name) {
182 args.push(isWin ? "/d" : "-n", options.name);
183 }
184 if (options.site) {
185 args.push(isWin ? "/du" : "-i", options.site);
186 }
187 // msi does not support dual-signing
188 if (options.isNest) {
189 args.push(isWin ? "/as" : "-nest");
190 }
191 const password = options.cscInfo == null ? null : options.cscInfo.password;
192 if (password) {
193 args.push(isWin ? "/p" : "-pass", password);
194 }
195 if (options.options.additionalCertificateFile) {
196 args.push(isWin ? "/ac" : "-ac", vm.toVmFile(options.options.additionalCertificateFile));
197 }
198 const httpsProxyFromEnv = process.env.HTTPS_PROXY;
199 if (!isWin && httpsProxyFromEnv != null && httpsProxyFromEnv.length) {
200 args.push("-p", httpsProxyFromEnv);
201 }
202 if (isWin) {
203 // https://github.com/electron-userland/electron-builder/issues/2875#issuecomment-387233610
204 args.push("/debug");
205 // must be last argument
206 args.push(inputFile);
207 }
208 return args;
209}
210function getOutputPath(inputPath, hash) {
211 const extension = path.extname(inputPath);
212 return path.join(path.dirname(inputPath), `${path.basename(inputPath, extension)}-signed-${hash}${extension}`);
213}
214/** @internal */
215function isOldWin6() {
216 const winVersion = os.release();
217 return winVersion.startsWith("6.") && !winVersion.startsWith("6.3");
218}
219exports.isOldWin6 = isOldWin6;
220function getWinSignTool(vendorPath) {
221 // use modern signtool on Windows Server 2012 R2 to be able to sign AppX
222 if (isOldWin6()) {
223 return path.join(vendorPath, "windows-6", "signtool.exe");
224 }
225 else {
226 return path.join(vendorPath, "windows-10", process.arch, "signtool.exe");
227 }
228}
229async function getToolPath(isWin = process.platform === "win32") {
230 if ((0, flags_1.isUseSystemSigncode)()) {
231 return { path: "osslsigncode" };
232 }
233 const result = process.env.SIGNTOOL_PATH;
234 if (result) {
235 return { path: result };
236 }
237 const vendorPath = await getSignVendorPath();
238 if (isWin) {
239 // use modern signtool on Windows Server 2012 R2 to be able to sign AppX
240 return { path: getWinSignTool(vendorPath) };
241 }
242 else if (process.platform === "darwin") {
243 const toolDirPath = path.join(vendorPath, process.platform, "10.12");
244 return {
245 path: path.join(toolDirPath, "osslsigncode"),
246 env: (0, bundledTool_1.computeToolEnv)([path.join(toolDirPath, "lib")]),
247 };
248 }
249 else {
250 return { path: path.join(vendorPath, process.platform, "osslsigncode") };
251 }
252}
253//# sourceMappingURL=windowsCodeSign.js.map
\No newline at end of file