Skip to content

Commit

Permalink
feat(windows): Possible to delegate code signing
Browse files Browse the repository at this point in the history
Close #2106
  • Loading branch information
develar committed Sep 23, 2017
1 parent 8a83577 commit 64f18a6
Show file tree
Hide file tree
Showing 34 changed files with 299 additions and 163 deletions.
4 changes: 2 additions & 2 deletions docs/api/electron-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ Developer API only. See [Configuration](/configuration/configuration.md) for use
* <code id="LinuxTargetSpecificOptions-category">category</code> String - The [application category](https://specifications.freedesktop.org/menu-spec/latest/apa.html#main-category-registry).
* <code id="LinuxTargetSpecificOptions-desktop">desktop</code> any - The [Desktop file](https://developer.gnome.org/integration-guide/stable/desktop-files.html.en) entries (name to value).
* <code id="LinuxTargetSpecificOptions-artifactName">artifactName</code> String - The [artifact file name template](/configuration/configuration.md#artifact-file-name-template).
* <code id="LinuxTargetSpecificOptions-publish">publish</code> String | [GithubOptions](/configuration/publish.md#githuboptions) | [S3Options](/configuration/publish.md#s3options) | [GenericServerOptions](/configuration/publish.md#genericserveroptions) | [BintrayOptions](/configuration/publish.md#bintrayoptions) | Array
* <code id="LinuxTargetSpecificOptions-publish">publish</code> String | [GithubOptions](/configuration/publish.md#githuboptions) | [S3Options](/configuration/publish.md#s3options) | [SpacesOptions](/configuration/publish.md#spacesoptions) | [GenericServerOptions](/configuration/publish.md#genericserveroptions) | [BintrayOptions](/configuration/publish.md#bintrayoptions) | Array

<a name="PlatformSpecificBuildOptions"></a>
## `PlatformSpecificBuildOptions` ⇐ <code>[TargetSpecificOptions](#TargetSpecificOptions)</code>
Expand All @@ -143,7 +143,7 @@ Developer API only. See [Configuration](/configuration/configuration.md) for use
* <code id="PlatformSpecificBuildOptions-fileAssociations">fileAssociations</code> Array&lt;[FileAssociation](#FileAssociation)&gt; | [FileAssociation](#FileAssociation)
* <code id="PlatformSpecificBuildOptions-forceCodeSigning">forceCodeSigning</code> Boolean
* <code id="PlatformSpecificBuildOptions-artifactName">artifactName</code> String - The [artifact file name template](/configuration/configuration.md#artifact-file-name-template).
* <code id="PlatformSpecificBuildOptions-publish">publish</code> String | [GithubOptions](/configuration/publish.md#githuboptions) | [S3Options](/configuration/publish.md#s3options) | [GenericServerOptions](/configuration/publish.md#genericserveroptions) | [BintrayOptions](/configuration/publish.md#bintrayoptions) | Array
* <code id="PlatformSpecificBuildOptions-publish">publish</code> String | [GithubOptions](/configuration/publish.md#githuboptions) | [S3Options](/configuration/publish.md#s3options) | [SpacesOptions](/configuration/publish.md#spacesoptions) | [GenericServerOptions](/configuration/publish.md#genericserveroptions) | [BintrayOptions](/configuration/publish.md#bintrayoptions) | Array

<a name="SourceRepositoryInfo"></a>
## `SourceRepositoryInfo`
Expand Down
2 changes: 1 addition & 1 deletion docs/auto-update.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ Start downloading update manually. You can use this method if `autoDownload` opt
Configure update provider. If value is `string`, [GenericServerOptions](/configuration/publish.md#genericserveroptions) will be set with value as `url`.
- options <code>[PublishConfiguration](/configuration/publish.md#publishconfiguration)</code> | <code>[GenericServerOptions](/configuration/publish.md#genericserveroptions)</code> | <code>[S3Options](/configuration/publish.md#s3options)</code> | <code>[BintrayOptions](/configuration/publish.md#bintrayoptions)</code> | <code>[GithubOptions](/configuration/publish.md#githuboptions)</code> | <code>String</code> - If you want to override configuration in the `app-update.yml`.
- options <code>[PublishConfiguration](/configuration/publish.md#publishconfiguration)</code> | <code>[GenericServerOptions](/configuration/publish.md#genericserveroptions)</code> | <code>[S3Options](/configuration/publish.md#s3options)</code> | <code>[SpacesOptions](/configuration/publish.md#spacesoptions)</code> | <code>[BintrayOptions](/configuration/publish.md#bintrayoptions)</code> | <code>[GithubOptions](/configuration/publish.md#githuboptions)</code> | <code>String</code> - If you want to override configuration in the `app-update.yml`.
<a name="module_electron-updater.AppUpdater+quitAndInstall"></a>
#### `appUpdater.quitAndInstall(isSilent, isForceRunAfter)`
Expand Down
4 changes: 2 additions & 2 deletions docs/configuration/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ Env file `electron-builder.env` in the current dir ([example](https://github.com

---

* <code id="Configuration-afterPack">afterPack</code> callback - *programmatic API only* The function to be run after pack (but before pack into distributable format and sign). Promise must be returned.
* <code id="Configuration-beforeBuild">beforeBuild</code> callback - *programmatic API only* The function to be run before dependencies are installed or rebuilt. Works when `npmRebuild` is set to `true`. Promise must be returned. Resolving to `false` will skip dependencies install or rebuild.
* <code id="Configuration-afterPack">afterPack</code> (context: AfterPackContext) => Promise | null - The function (or path to file or module id) to be run after pack (but before pack into distributable format and sign).
* <code id="Configuration-beforeBuild">beforeBuild</code> (context: BeforeBuildContext) => Promise | null - The function (or path to file or module id) to be run before dependencies are installed or rebuilt. Works when `npmRebuild` is set to `true`. Resolving to `false` will skip dependencies install or rebuild.

## Metadata
Some standard fields should be defined in the `package.json`.
Expand Down
1 change: 1 addition & 0 deletions docs/configuration/nsis.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ You can explicitly set guid using option [nsis.guid](#NsisOptions-guid), but it

It is also important to set the Application User Model ID (AUMID) to the [appId](configuration.md#Configuration-appId) of the application, in order for notifications on Windows 8/8.1 to function and for Window 10 notifications to display the app icon within the notifications by default. The AUMID should be set within the Main process and before any BrowserWindows have been opened, it is normally the first piece of code executed: `app.setAppUserModelId(appId)`

---

## Common Questions
#### How do change the default installation directory to custom?
Expand Down
31 changes: 29 additions & 2 deletions docs/configuration/win.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,44 @@ The top-level [win](configuration.md#Configuration-win) key contains set of opti
To use Squirrel.Windows please install `electron-builder-squirrel-windows` dependency.

For `portable` app, `PORTABLE_EXECUTABLE_DIR` env is set (dir where portable executable located).
* <code id="WindowsConfiguration-signingHashAlgorithms">signingHashAlgorithms</code> = `['sha1', 'sha256']` Array&lt;"sha1" | "sha256"&gt; - Array of signing algorithms used. For AppX `sha256` is always used.
* <code id="WindowsConfiguration-icon">icon</code> = `build/icon.ico` String - The path to application icon.
* <code id="WindowsConfiguration-legalTrademarks">legalTrademarks</code> String - The trademarks and registered trademarks.

---

* <code id="WindowsConfiguration-signingHashAlgorithms">signingHashAlgorithms</code> = `['sha1', 'sha256']` Array&lt;"sha1" | "sha256"&gt; - Array of signing algorithms used. For AppX `sha256` is always used.
* <code id="WindowsConfiguration-sign">sign</code> String | (configuration: CustomWindowsSignTaskConfiguration) => Promise - The custom function (or path to file or module id) to sign Windows executable.
* <code id="WindowsConfiguration-certificateFile">certificateFile</code> String - The path to the *.pfx certificate you want to sign with. Please use it only if you cannot use env variable `CSC_LINK` (`WIN_CSC_LINK`) for some reason. Please see [Code Signing](../code-signing.md).
* <code id="WindowsConfiguration-certificatePassword">certificatePassword</code> String - The password to the certificate provided in `certificateFile`. Please use it only if you cannot use env variable `CSC_KEY_PASSWORD` (`WIN_CSC_KEY_PASSWORD`) for some reason. Please see [Code Signing](../code-signing.md).
* <code id="WindowsConfiguration-certificateSubjectName">certificateSubjectName</code> String - The name of the subject of the signing certificate. Required only for EV Code Signing and works only on Windows.
* <code id="WindowsConfiguration-certificateSha1">certificateSha1</code> String - The SHA1 hash of the signing certificate. The SHA1 hash is commonly specified when multiple certificates satisfy the criteria specified by the remaining switches. Works only on Windows.
* <code id="WindowsConfiguration-additionalCertificateFile">additionalCertificateFile</code> String - The path to an additional certificate file you want to add to the signature block.
* <code id="WindowsConfiguration-rfc3161TimeStampServer">rfc3161TimeStampServer</code> = `http://timestamp.comodoca.com/rfc3161` String - The URL of the RFC 3161 time stamp server.
* <code id="WindowsConfiguration-timeStampServer">timeStampServer</code> = `http://timestamp.verisign.com/scripts/timstamp.dll` String - The URL of the time stamp server.

---

* <code id="WindowsConfiguration-publisherName">publisherName</code> String | Array&lt;String&gt; - [The publisher name](https://github.com/electron-userland/electron-builder/issues/1187#issuecomment-278972073), exactly as in your code signed certificate. Several names can be provided. Defaults to common name from your code signing certificate.
* <code id="WindowsConfiguration-verifyUpdateCodeSignature">verifyUpdateCodeSignature</code> = `true` Boolean - Whether to verify the signature of an available update before installation. The [publisher name](#publisherName) will be used for the signature verification.
* <code id="WindowsConfiguration-requestedExecutionLevel">requestedExecutionLevel</code> = `asInvoker` "asInvoker" | "highestAvailable" | "requireAdministrator" - The [security level](https://msdn.microsoft.com/en-us/library/6ad1fshk.aspx#Anchor_9) at which the application requests to be executed. Cannot be specified per target, allowed only in the `win`.
<!-- end of generated block -->
<!-- end of generated block -->

---

## Common Questions
#### How do delegate code signing?

Use [sign](#WindowsConfiguration-sign) option.

```json
"win": {
"sign": "./customSign.js"
}
```

File `customSign.js` in the project root directory:
```js
exports.default = async function(configuration) {
// your custom code
}
```
2 changes: 1 addition & 1 deletion packages/builder-util-runtime/src/publishOptions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export type PublishProvider = "github" | "bintray" | "s3" | "spaces" | "generic"

// typescript-json-schema generates only PublishConfiguration if it is specified in the list, so, it is not added here
export type AllPublishOptions = string | GithubOptions | S3Options | GenericServerOptions | BintrayOptions
export type AllPublishOptions = string | GithubOptions | S3Options | SpacesOptions | GenericServerOptions | BintrayOptions
// https://github.com/YousefED/typescript-json-schema/issues/80
export type Publish = AllPublishOptions | Array<AllPublishOptions> | null

Expand Down
4 changes: 2 additions & 2 deletions packages/electron-builder/src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,11 @@ export interface Configuration extends PlatformSpecificBuildOptions {
readonly releaseInfo?: ReleaseInfo

/**
* *programmatic API only* The function to be run after pack (but before pack into distributable format and sign). Promise must be returned.
* The function (or path to file or module id) to be run after pack (but before pack into distributable format and sign).
*/
readonly afterPack?: (context: AfterPackContext) => Promise<any> | null
/**
* *programmatic API only* The function to be run before dependencies are installed or rebuilt. Works when `npmRebuild` is set to `true`. Promise must be returned. Resolving to `false` will skip dependencies install or rebuild.
* The function (or path to file or module id) to be run before dependencies are installed or rebuilt. Works when `npmRebuild` is set to `true`. Resolving to `false` will skip dependencies install or rebuild.
*/
readonly beforeBuild?: (context: BeforeBuildContext) => Promise<any> | null

Expand Down
3 changes: 2 additions & 1 deletion packages/electron-builder/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export { SnapOptions } from "./options/SnapOptions"
export { buildForge, ForgeOptions } from "./forge/forge-maker"
export { Metadata, AuthorMetadata, RepositoryInfo } from "./options/metadata"
export { AppInfo } from "./appInfo"
export { SquirrelWindowsOptions } from "./options/SquirrelWindowsOptions"
export { SquirrelWindowsOptions } from "./options/SquirrelWindowsOptions"
export { WindowsSignOptions, CustomWindowsSignTaskConfiguration, WindowsSignTaskConfiguration, CustomWindowsSign } from "./windowsCodeSign"
22 changes: 10 additions & 12 deletions packages/electron-builder/src/options/winOptions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { PlatformSpecificBuildOptions } from "../configuration"
import { TargetConfigType, TargetSpecificOptions } from "../core"
import { CustomWindowsSign } from "../windowsCodeSign"

export interface WindowsConfiguration extends PlatformSpecificBuildOptions {
/**
Expand All @@ -14,12 +15,6 @@ export interface WindowsConfiguration extends PlatformSpecificBuildOptions {
*/
readonly target?: TargetConfigType

/**
* Array of signing algorithms used. For AppX `sha256` is always used.
* @default ['sha1', 'sha256']
*/
readonly signingHashAlgorithms?: Array<"sha1" | "sha256"> | null

/**
* The path to application icon.
* @default build/icon.ico
Expand All @@ -31,39 +26,42 @@ export interface WindowsConfiguration extends PlatformSpecificBuildOptions {
*/
readonly legalTrademarks?: string | null

/**
* Array of signing algorithms used. For AppX `sha256` is always used.
* @default ['sha1', 'sha256']
*/
readonly signingHashAlgorithms?: Array<"sha1" | "sha256"> | null
/**
* The custom function (or path to file or module id) to sign Windows executable.
*/
readonly sign?: CustomWindowsSign| string | null
/**
* The path to the *.pfx certificate you want to sign with. Please use it only if you cannot use env variable `CSC_LINK` (`WIN_CSC_LINK`) for some reason.
* Please see [Code Signing](../code-signing.md).
*/
readonly certificateFile?: string | null

/**
* The password to the certificate provided in `certificateFile`. Please use it only if you cannot use env variable `CSC_KEY_PASSWORD` (`WIN_CSC_KEY_PASSWORD`) for some reason.
* Please see [Code Signing](../code-signing.md).
*/
readonly certificatePassword?: string | null

/**
* The name of the subject of the signing certificate. Required only for EV Code Signing and works only on Windows.
*/
readonly certificateSubjectName?: string | null

/**
* The SHA1 hash of the signing certificate. The SHA1 hash is commonly specified when multiple certificates satisfy the criteria specified by the remaining switches. Works only on Windows.
*/
readonly certificateSha1?: string | null

/**
* The path to an additional certificate file you want to add to the signature block.
*/
readonly additionalCertificateFile?: string | null

/**
* The URL of the RFC 3161 time stamp server.
* @default http://timestamp.comodoca.com/rfc3161
*/
readonly rfc3161TimeStampServer?: string | null

/**
* The URL of the time stamp server.
* @default http://timestamp.verisign.com/scripts/timstamp.dll
Expand Down
8 changes: 4 additions & 4 deletions packages/electron-builder/src/packager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { Platform, SourceRepositoryInfo, Target } from "./core"
import MacPackager from "./macPackager"
import { Metadata } from "./options/metadata"
import { ArtifactCreated, PackagerOptions } from "./packagerApi"
import { PlatformPackager } from "./platformPackager"
import { PlatformPackager, resolveFunction } from "./platformPackager"
import { computeArchToTargetNamesMap, createTargets, NoOpTarget } from "./targets/targetFactory"
import { computeDefaultAppDirectory, getConfig, validateConfig } from "./util/config"
import { computeElectronVersion, getElectronVersionFromInstalled } from "./util/electronVersion"
Expand Down Expand Up @@ -143,7 +143,7 @@ export class Packager {
if (debug.enabled) {
debug(`Effective config:\n${safeDump(JSON.parse(safeStringifyJson(config)))}`)
}
await validateConfig(config)
await validateConfig(config, this.debugLogger)
this._configuration = config

this.appDir = await computeDefaultAppDirectory(projectDir, use(config.directories, it => it!.app))
Expand Down Expand Up @@ -318,7 +318,7 @@ export class Packager {
return
}

const beforeBuild = config.beforeBuild
const beforeBuild = resolveFunction(config.beforeBuild)
if (beforeBuild != null) {
const performDependenciesInstallOrRebuild = await beforeBuild({
appDir: this.appDir,
Expand All @@ -345,7 +345,7 @@ export class Packager {
}

afterPack(context: AfterPackContext): Promise<any> {
const afterPack = this.config.afterPack
const afterPack = resolveFunction(this.config.afterPack)
const handlers = this.afterPackHandlers.slice()
if (afterPack != null) {
// user handler should be last
Expand Down
23 changes: 22 additions & 1 deletion packages/electron-builder/src/platformPackager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { computeData } from "asar-integrity"
import BluebirdPromise from "bluebird-lst"
import { Arch, asArray, AsyncTaskManager, DebugLogger, getArchSuffix, isEmptyOrSpaces, log, use, warn } from "builder-util"
import { Arch, asArray, AsyncTaskManager, debug, DebugLogger, getArchSuffix, isEmptyOrSpaces, log, use, warn } from "builder-util"
import { PackageBuilder } from "builder-util/out/api"
import { statOrNull, unlinkIfExists } from "builder-util/out/fs"
import { orIfFileNotExist } from "builder-util/out/promise"
Expand Down Expand Up @@ -552,3 +552,24 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
export function normalizeExt(ext: string) {
return ext.startsWith(".") ? ext.substring(1) : ext
}

export function resolveFunction<T>(executor: T | string): T {
if (typeof executor !== "string") {
return executor
}

let p = executor as string
if (p.startsWith(".")) {
p = path.resolve(p)
}
try {
p = require.resolve(p)
}
catch (e) {
debug(e)
p = path.resolve(p)
}

const m = require(p)
return m.default || m
}
8 changes: 5 additions & 3 deletions packages/electron-builder/src/publish/updateUnfoBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ export async function writeUpdateInfo(event: ArtifactCreated, _publishConfigs: A
}

// spaces is a new publish provider, no need to keep backward compatibility
if (isMac && publishConfig.provider !== "spaces") {
const isElectronUpdater1xCompatibility = publishConfig.provider !== "spaces"

if (isMac && isElectronUpdater1xCompatibility) {
await writeOldMacInfo(publishConfig, outDir, dir, channel, createdFiles, version, packager)
}

Expand All @@ -69,12 +71,12 @@ export async function writeUpdateInfo(event: ArtifactCreated, _publishConfigs: A
createdFiles.add(updateInfoFile)

// noinspection JSDeprecatedSymbols
if (packager.platform === Platform.WINDOWS && info.sha2 == null) {
if (isElectronUpdater1xCompatibility && packager.platform === Platform.WINDOWS && info.sha2 == null) {
// backward compatibility
(info as any).sha2 = await sha2.value
}

if (event.safeArtifactName != null) {
if (event.safeArtifactName != null && publishConfig.provider === "github") {
info = {
...info,
githubArtifactName: event.safeArtifactName,
Expand Down
8 changes: 4 additions & 4 deletions packages/electron-builder/src/util/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { asArray, debug, log, warn } from "builder-util"
import { asArray, DebugLogger, log, warn } from "builder-util"
import { statOrNull } from "builder-util/out/fs"
import { readJson } from "fs-extra-p"
import { Lazy } from "lazy-val"
Expand Down Expand Up @@ -84,7 +84,7 @@ export async function getConfig(projectDir: string, configPath: string | null, c
const schemeDataPromise = new Lazy(() => readJson(path.join(__dirname, "..", "..", "scheme.json")))

/** @internal */
export async function validateConfig(config: Configuration) {
export async function validateConfig(config: Configuration, debugLogger: DebugLogger) {
const extraMetadata = config.extraMetadata
if (extraMetadata != null) {
if (extraMetadata.build != null) {
Expand All @@ -101,8 +101,8 @@ export async function validateConfig(config: Configuration) {
}

await _validateConfig(config, schemeDataPromise, (message, errors) => {
if (debug.enabled) {
debug(JSON.stringify(errors, null, 2))
if (debugLogger.enabled) {
debugLogger.add("invalidConfig", JSON.stringify(errors, null, 2))
}

return `${message}
Expand Down
Loading

0 comments on commit 64f18a6

Please sign in to comment.