Skip to content

Instantly share code, notes, and snippets.

@GeorgDangl
Last active August 18, 2021 21:32
Show Gist options
  • Save GeorgDangl/f3f82bd3d865e9c66466c38c247d6584 to your computer and use it in GitHub Desktop.
Save GeorgDangl/f3f82bd3d865e9c66466c38c247d6584 to your computer and use it in GitHub Desktop.
Signing Electron Apps before Bundling with Azure Key Vault and EV Code Signing Certificates: https://blog.dangl.me/archive/signing-electron-apps-before-bundling-with-azure-key-vault-and-ev-code-signing-certificates/
Target BuildElectronApp => _ => _
.DependsOn(Clean)
.DependsOn(GenerateVersion)
.Requires(() => CodeSigningCertificateName != null)
.Executes(() =>
{
// To ensure the tool is always up to date
DotNet("tool update ElectronNET.CLI -g");
BuildElectronAppInternal(
new[] { "Windows_X86", "/target custom win-x86;win /electron-arch ia32" } ,
new[] { "Windows_X64", "/target win" }
);
SignExecutablesInFolder(OutputDirectory / "electron");
});
void BuildElectronAppInternal(params string[][] electronOptions)
{
foreach (var electronOption in electronOptions)
{
EnsureCleanDirectory(SourceDirectory / "Dangl.WebGAEB" / "bin");
var releaseIdentifier = electronOption[0];
var electronArguments = electronOption[1];
// Electron Build
SetVersionInElectronManifest();
var electronNet = ToolResolver.GetPathTool("electronize");
electronNet(arguments: $"build /dotnet-configuration Standalone {electronArguments}",
workingDirectory: SourceDirectory / "Dangl.WebGAEB"
);
var exeFile = GlobFiles(SourceDirectory / "Dangl.WebGAEB" / "bin" / "Desktop", "WebGaeb.Desktop*.exe").Single();
MoveFile(exeFile, OutputDirectory / "electron" / $"WebGaeb.Desktop_Setup_{releaseIdentifier}.exe");
}
}
[PackageExecutable("AzureSign", "tools/net5.0/any/AzureSignTool.dll")]
readonly Tool AzureSign;
Target SignExecutables => _ => _
.Requires(() => ExecutablesToSignFolder)
.Executes(() =>
{
SignExecutablesInFolder(ExecutablesToSignFolder);
});
private void SignExecutablesInFolder(string folderPath)
{
ControlFlow.Assert(!string.IsNullOrWhiteSpace(CodeSigningCertificateKeyVaultBaseUrl), "!string.IsNullOrWhitespace(CodeSigningCertificateKeyVaultBaseUrl)");
ControlFlow.Assert(!string.IsNullOrWhiteSpace(KeyVaultClientId), "!string.IsNullOrWhitespace(KeyVaultClientId)");
ControlFlow.Assert(!string.IsNullOrWhiteSpace(KeyVaultClientSecret), "!string.IsNullOrWhitespace(KeyVaultClientSecret)");
ControlFlow.Assert(!string.IsNullOrWhiteSpace(CodeSigningKeyVaultTenantId), "!string.IsNullOrWhitespace(CodeSigningKeyVaultTenantId)");
ControlFlow.Assert(!string.IsNullOrWhiteSpace(CodeSigningCertificateName), "!string.IsNullOrWhitespace(CodeSigningCertificateName)");
Logger.Normal("Searching for files to sign in " + folderPath);
var inputFiles = GlobFiles(folderPath, "*.exe");
if (!inputFiles.Any())
{
Logger.Normal("No files to sign found");
return;
}
Logger.Normal("Signing " + inputFiles.Join(", "));
var filesListPath = OutputDirectory / $"{Guid.NewGuid()}.txt";
WriteAllText(filesListPath, inputFiles.Join(Environment.NewLine) + Environment.NewLine);
try
{
var azureSignArguments = new Arguments()
.Add("sign")
.Add("--azure-key-vault-url {0}", CodeSigningCertificateKeyVaultBaseUrl)
.Add("--azure-key-vault-client-id {0}", KeyVaultClientId)
.Add("--azure-key-vault-client-secret {0}", KeyVaultClientSecret)
.Add("--azure-key-vault-tenant-id {0}", CodeSigningKeyVaultTenantId)
.Add("--azure-key-vault-certificate {0}", CodeSigningCertificateName)
.Add("--input-file-list {0}", filesListPath)
.Add("--timestamp-rfc3161 {0}", "http://timestamp.digicert.com")
.ToString();
AzureSign(azureSignArguments);
}
finally
{
DeleteFile(filesListPath);
}
}
{
"$schema": "http://json.schemastore.org/electron-builder",
"executable": "Dangl.WebGaeb",
"splashscreen": {
"imageFile": "splashscreen.png"
},
"name": "webgaeb-desktop",
"Author": "Dangl IT GmbH",
"build": {
"copyright": "Copyright Dangl IT GmbH © 2021",
"afterSign": "../../../electronAfterPackHook.js",
"directories": {
"output": "../../../bin/Desktop"
},
"win": {
"target": "nsis"
}
}
}
const { exec } = require('child_process');
const path = require('path');
exports.default = async function (context) {
// We're calling NUKE to sign the output files before they're being packaged
const nukeCommand = `"./build.cmd" SignExecutables -ExecutablesToSignFolder "${context.appOutDir}"`;
const rootDirectory = path.join(__dirname, '../../');
const signTask = new Promise((resolve, reject) => {
exec(nukeCommand, {
cwd: rootDirectory
}, (error, stdout, sdterr) => {
console.log(stdout);
console.log(sdterr);
if (error) {
console.log(error);
reject(error);
} else {
resolve();
}
});
});
await signTask;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment