refactor(maker): use makeCert from electron-windows-store

* Fix #160
* Use upstream code to interface with `makecert.exe`
This commit is contained in:
Jacob Quant 2017-03-30 00:27:00 -05:00 коммит произвёл Mark Lee
Родитель 45afdfb54d
Коммит c31ceef665
4 изменённых файлов: 100 добавлений и 20 удалений

Просмотреть файл

@ -123,6 +123,7 @@
"nugget": "^2.0.1",
"opn": "^5.0.0",
"ora": "^1.1.0",
"parse-author": "^2.0.0",
"pify": "^3.0.0",
"resolve-package": "^1.0.1",
"s3": "^4.4.0",

Двоичные данные
res/default.pvk

Двоичный файл не отображается.

Просмотреть файл

@ -1,7 +1,10 @@
import fs from 'fs';
import path from 'path';
import { spawnPromise, findActualExecutable } from 'spawn-rx';
import parseAuthor from 'parse-author';
import windowsStore from 'electron-windows-store';
import { isValidPublisherName, makeCert } from 'electron-windows-store/lib/sign.js';
import { findActualExecutable } from 'spawn-rx';
import { ensureDirectory } from '../../util/ensure-output';
import configFn from '../../util/config-fn';
@ -23,40 +26,54 @@ function findSdkTool(exe) {
}
if (!fs.existsSync(sdkTool)) {
throw new Error(`Can't find ${exe} in PATH, you probably need to install the Windows SDK`);
throw new Error(`Can't find ${exe} in PATH. You probably need to install the Windows SDK.`);
}
return sdkTool;
}
function spawnSdkTool(exe, params) {
return spawnPromise(findSdkTool(exe), params);
export async function createDefaultCertificate(publisherName, { certFilePath, certFileName, install, program }) {
const makeCertOptions = {
publisherName,
certFilePath: certFilePath || process.cwd(),
certFileName: certFileName || 'default',
install: typeof install === 'boolean' ? install : false,
program: program || { windowsKit: path.dirname(findSdkTool('makecert.exe')) },
};
if (!isValidPublisherName(publisherName)) {
throw new Error(`Received invalid publisher name: '${publisherName}' did not conform to X.500 distinguished name syntax for MakeCert.`);
}
return await makeCert(makeCertOptions);
}
export async function createDefaultCertificate(publisherName, outPath) {
const defaultPvk = path.resolve(__dirname, '..', '..', '..', 'res', 'default.pvk');
const targetCert = path.join(outPath, 'default.cer');
const targetPfx = path.join(outPath, 'default.pfx');
export function getDistinguishedNameFromAuthor(author) {
let publisher = author || '';
await spawnSdkTool(
'makecert.exe',
['-r', '-h', '0', '-n', `CN=${publisherName}`, '-eku', '1.3.6.1.5.5.7.3.3', '-pe', '-sv', defaultPvk, targetCert]);
if (typeof publisher === 'string') {
publisher = parseAuthor(publisher);
}
await spawnSdkTool('pvk2pfx.exe', ['-pvk', defaultPvk, '-spc', targetCert, '-pfx', targetPfx]);
if (typeof publisher.name === 'string') {
publisher = publisher.name;
}
return targetPfx;
if (typeof publisher !== 'string') {
publisher = '';
}
return `CN=${publisher}`;
}
export default async ({ dir, appName, targetArch, forgeConfig, packageJSON }) => {
const windowsStore = require('electron-windows-store');
const outPath = path.resolve(dir, `../make/appx/${targetArch}`);
await ensureDirectory(outPath);
const userConfig = configFn(forgeConfig.windowsStoreConfig, targetArch);
const opts = Object.assign({
publisher: packageJSON.author,
publisher: getDistinguishedNameFromAuthor(packageJSON.author),
flatten: false,
deploy: false,
packageVersion: `${packageJSON.version}.0`,
@ -70,12 +87,12 @@ export default async ({ dir, appName, targetArch, forgeConfig, packageJSON }) =>
outputDirectory: outPath,
});
if (!opts.devCert) {
opts.devCert = await createDefaultCertificate(opts.publisher, outPath);
if (!opts.publisher) {
throw 'Please set config.forge.windowsStoreConfig.publisher or author.name in package.json for the appx target';
}
if (!opts.publisher.match(/^CN=/)) {
opts.publisher = `CN=${opts.publisher}`;
if (!opts.devCert) {
opts.devCert = await createDefaultCertificate(opts.publisher, { certFilePath: outPath, program: opts });
}
if (opts.packageVersion.match(/-/)) {

Просмотреть файл

@ -0,0 +1,62 @@
import { tmpdir } from 'os';
import { join } from 'path';
import { copy, ensureDir, readFile, remove } from 'fs-promise';
import { expect } from 'chai';
import { getDistinguishedNameFromAuthor, createDefaultCertificate } from '../../src/makers/win32/appx.js';
describe('appx maker', () => {
describe('createDefaultCertificate', () => {
const tmpDir = join(tmpdir(), `electron-forge-maker-appx-test-${Date.now()}`);
before(async () => {
await ensureDir(tmpDir);
});
after(async () => {
await remove(tmpDir);
});
if (process.platform === 'win32') {
it('should create a .pfx file', async () => {
await copy(join(__dirname, '..', '..', 'node_modules',
'electron-windows-store', 'test', 'lib', 'bogus-private-key.pvk'),
join(tmpDir, 'dummy.pvk'));
const outputCertPath = await createDefaultCertificate('CN=Test', {
certFilePath: tmpDir,
certFileName: 'dummy',
install: false,
});
const fileContents = await readFile(outputCertPath);
expect(fileContents).to.be.an.instanceof(Buffer);
expect(fileContents.length).to.be.above(0);
});
}
});
describe('getDistinguishedNameFromAuthor', () => {
[{
author: 'First Last',
expectedReturnValue: 'CN=First Last',
}, {
author: 'First Last <first.last@example.com>',
expectedReturnValue: 'CN=First Last',
}, {
author: {
name: 'First Last',
},
expectedReturnValue: 'CN=First Last',
}, {
author: undefined,
expectedReturnValue: 'CN=',
}, {
author: '',
expectedReturnValue: 'CN=',
}].forEach((scenario) => {
it(`${JSON.stringify(scenario.author)} -> "${scenario.expectedReturnValue}"`, () => {
expect(getDistinguishedNameFromAuthor(scenario.author)).to.equal(scenario.expectedReturnValue);
});
});
});
});