From b5e4bf94ac1ba1a3772a7fa3d7379a01d5d2b2a9 Mon Sep 17 00:00:00 2001 From: Terrence Owen Date: Thu, 16 Jan 2020 16:01:08 -0800 Subject: [PATCH 1/3] remove expired certs if they exist to address Issue#190 --- .../office-addin-dev-certs/src/uninstall.ts | 20 +++++++++++++++---- packages/office-addin-dev-certs/src/verify.ts | 16 +++++++++++++-- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/packages/office-addin-dev-certs/src/uninstall.ts b/packages/office-addin-dev-certs/src/uninstall.ts index 215d32f86..b000b23d2 100644 --- a/packages/office-addin-dev-certs/src/uninstall.ts +++ b/packages/office-addin-dev-certs/src/uninstall.ts @@ -7,7 +7,7 @@ import * as path from "path"; import * as defaults from "./defaults"; import { isCaCertificateInstalled } from "./verify"; -function getUninstallCommand(machine: boolean = false): string { +export function getUninstallCommand(machine: boolean = false): string { switch (process.platform) { case "win32": const script = path.resolve(__dirname, "..\\scripts\\uninstall.ps1"); @@ -32,13 +32,24 @@ export function deleteCertificateFiles(certificateDirectory: string = defaults.c } } -export async function uninstallCaCertificate(machine: boolean = false, verbose: boolean = true) { - if (isCaCertificateInstalled()) { +export async function uninstallCaCertificate(machine: boolean = false, verbose: boolean = true, expiredCert = false) { + if (expiredCert) { + const command = getUninstallCommand(machine); + + try { + console.log(`Uninstalling expired CA certificate "Developer CA for Microsoft Office Add-ins"...`); + execSync(command, { stdio: "pipe" }); + console.log(`You no longer have trusted access to https://localhost.`); + } catch (error) { + throw new Error(`Unable to uninstall expired the CA certificate.\n${error.stderr.toString()}`); + } + + } else if (isCaCertificateInstalled()) { const command = getUninstallCommand(machine); try { console.log(`Uninstalling CA certificate "Developer CA for Microsoft Office Add-ins"...`); - execSync(command, {stdio : "pipe" }); + execSync(command, { stdio: "pipe" }); console.log(`You no longer have trusted access to https://localhost.`); } catch (error) { throw new Error(`Unable to uninstall the CA certificate.\n${error.stderr.toString()}`); @@ -49,3 +60,4 @@ export async function uninstallCaCertificate(machine: boolean = false, verbose: } } } + diff --git a/packages/office-addin-dev-certs/src/verify.ts b/packages/office-addin-dev-certs/src/verify.ts index 3471185c2..b09a3b88a 100644 --- a/packages/office-addin-dev-certs/src/verify.ts +++ b/packages/office-addin-dev-certs/src/verify.ts @@ -6,6 +6,7 @@ import * as crypto from "crypto"; import * as fs from "fs"; import * as path from "path"; import * as defaults from "./defaults"; +import { deleteCertificateFiles, uninstallCaCertificate } from './uninstall' function getVerifyCommand(): string { switch (process.platform) { @@ -13,7 +14,7 @@ function getVerifyCommand(): string { const script = path.resolve(__dirname, "..\\scripts\\verify.ps1"); return `powershell -ExecutionPolicy Bypass -File "${script}" "${defaults.certificateName}"`; case "darwin": // macOS - return `security find-certificate -c '${defaults.certificateName}' -p | openssl x509 -checkend 86400 -noout`; + return `security find-certificate -c '${defaults.certificateName}' -p | openssl x509 -noout`; default: throw new Error(`Platform not supported: ${process.platform}`); } @@ -25,7 +26,18 @@ export function isCaCertificateInstalled(): boolean { try { const output = execSync(command, {stdio : "pipe" }).toString(); if (process.platform === "darwin") { - return true; + // Ceritificate exists. Now check and see if it's expired and remove it if is. + try { + const command = `security find-certificate -c '${defaults.certificateName}' -p | openssl x509 -checkend 86400 -noout`; + execSync(command, {stdio : "pipe" }).toString(); + return true; + } + catch { + uninstallCaCertificate(false /* machine */, true /* verbose */, true /* expiredCert */); + deleteCertificateFiles(defaults.certificateDirectory); + return false; + } + } else if (output.length !== 0) { return true; // powershell command return empty string if the certificate not-found/expired } From 7c9ebff0bbddd491760bc5bb119a695279c77e59 Mon Sep 17 00:00:00 2001 From: Terrence Owen Date: Thu, 16 Jan 2020 16:05:47 -0800 Subject: [PATCH 2/3] revert exported getUninstallCommand method --- packages/office-addin-dev-certs/src/uninstall.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/office-addin-dev-certs/src/uninstall.ts b/packages/office-addin-dev-certs/src/uninstall.ts index b000b23d2..a8c35b155 100644 --- a/packages/office-addin-dev-certs/src/uninstall.ts +++ b/packages/office-addin-dev-certs/src/uninstall.ts @@ -7,7 +7,7 @@ import * as path from "path"; import * as defaults from "./defaults"; import { isCaCertificateInstalled } from "./verify"; -export function getUninstallCommand(machine: boolean = false): string { +function getUninstallCommand(machine: boolean = false): string { switch (process.platform) { case "win32": const script = path.resolve(__dirname, "..\\scripts\\uninstall.ps1"); From 81dcf0a7549a0557442dca922c261abd7f74e8e0 Mon Sep 17 00:00:00 2001 From: Terrence Owen Date: Fri, 17 Jan 2020 12:32:37 -0800 Subject: [PATCH 3/3] add new uninstallExpiredCaCertificate method; update test to account for code changes --- .../office-addin-dev-certs/src/uninstall.ts | 47 ++++++++++--------- packages/office-addin-dev-certs/src/verify.ts | 35 +++++++------- packages/office-addin-dev-certs/test/test.ts | 2 +- 3 files changed, 43 insertions(+), 41 deletions(-) diff --git a/packages/office-addin-dev-certs/src/uninstall.ts b/packages/office-addin-dev-certs/src/uninstall.ts index a8c35b155..1a709c972 100644 --- a/packages/office-addin-dev-certs/src/uninstall.ts +++ b/packages/office-addin-dev-certs/src/uninstall.ts @@ -1,12 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. - + import { execSync } from "child_process"; import * as fsExtra from "fs-extra"; import * as path from "path"; import * as defaults from "./defaults"; import { isCaCertificateInstalled } from "./verify"; - + function getUninstallCommand(machine: boolean = false): string { switch (process.platform) { case "win32": @@ -18,37 +18,26 @@ function getUninstallCommand(machine: boolean = false): string { throw new Error(`Platform not supported: ${process.platform}`); } } - + // Deletes the generated certificate files and delete the certificate directory if its empty export function deleteCertificateFiles(certificateDirectory: string = defaults.certificateDirectory): void { if (fsExtra.existsSync(certificateDirectory)) { fsExtra.removeSync(path.join(certificateDirectory, defaults.localhostCertificateFileName)); fsExtra.removeSync(path.join(certificateDirectory, defaults.localhostKeyFileName)); fsExtra.removeSync(path.join(certificateDirectory, defaults.caCertificateFileName)); - + if (fsExtra.readdirSync(certificateDirectory).length === 0) { fsExtra.removeSync(certificateDirectory); } } } - -export async function uninstallCaCertificate(machine: boolean = false, verbose: boolean = true, expiredCert = false) { - if (expiredCert) { + +export async function uninstallCaCertificate(machine: boolean = false, verbose: boolean = true) { + if (isCaCertificateInstalled()) { const command = getUninstallCommand(machine); - + try { - console.log(`Uninstalling expired CA certificate "Developer CA for Microsoft Office Add-ins"...`); - execSync(command, { stdio: "pipe" }); - console.log(`You no longer have trusted access to https://localhost.`); - } catch (error) { - throw new Error(`Unable to uninstall expired the CA certificate.\n${error.stderr.toString()}`); - } - - } else if (isCaCertificateInstalled()) { - const command = getUninstallCommand(machine); - - try { - console.log(`Uninstalling CA certificate "Developer CA for Microsoft Office Add-ins"...`); + console.log(`Uninstalling CA certificate "Developer CA for Microsoft Office Add-ins"...`); execSync(command, { stdio: "pipe" }); console.log(`You no longer have trusted access to https://localhost.`); } catch (error) { @@ -56,8 +45,22 @@ export async function uninstallCaCertificate(machine: boolean = false, verbose: } } else { if (verbose) { - console.log(`The CA certificate is not installed.`); + console.log(`The CA certificate is not installed.`); } } } - + +// Currently this method is only intended to be used for Mac due to problems on Mac that occur when CA certificates expire +export async function uninstallExpiredCaCertificate() { + if (process.platform === "darwin") { + const command = getUninstallCommand(); + + try { + console.log(`Uninstalling expired CA certificate "Developer CA for Microsoft Office Add-ins".`); + execSync(command, { stdio: "pipe" }); + console.log(`You no longer have trusted access to https://localhost.`); + } catch (error) { + throw new Error(`Unable to uninstall expired the CA certificate.\n${error.stderr.toString()}`); + } + } +} \ No newline at end of file diff --git a/packages/office-addin-dev-certs/src/verify.ts b/packages/office-addin-dev-certs/src/verify.ts index b09a3b88a..6f7b83016 100644 --- a/packages/office-addin-dev-certs/src/verify.ts +++ b/packages/office-addin-dev-certs/src/verify.ts @@ -1,13 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. - + import { execSync } from "child_process"; import * as crypto from "crypto"; import * as fs from "fs"; import * as path from "path"; import * as defaults from "./defaults"; -import { deleteCertificateFiles, uninstallCaCertificate } from './uninstall' - +import { uninstallExpiredCaCertificate } from './uninstall' + function getVerifyCommand(): string { switch (process.platform) { case "win32": @@ -19,10 +19,10 @@ function getVerifyCommand(): string { throw new Error(`Platform not supported: ${process.platform}`); } } - + export function isCaCertificateInstalled(): boolean { const command = getVerifyCommand(); - + try { const output = execSync(command, {stdio : "pipe" }).toString(); if (process.platform === "darwin") { @@ -33,52 +33,51 @@ export function isCaCertificateInstalled(): boolean { return true; } catch { - uninstallCaCertificate(false /* machine */, true /* verbose */, true /* expiredCert */); - deleteCertificateFiles(defaults.certificateDirectory); + uninstallExpiredCaCertificate() return false; } - + } else if (output.length !== 0) { return true; // powershell command return empty string if the certificate not-found/expired } } catch (error) { - // Mac security command throws error if the certifcate is not-found/expired + // Mac security command throws error if the certifcate is not found } - + return false; } - + function validateCertificateAndKey(certificatePath: string, keyPath: string) { let certificate: string = ""; let key: string = ""; - + try { certificate = fs.readFileSync(certificatePath).toString(); } catch (err) { throw new Error(`Unable to read the certificate.\n${err}`); } - + try { key = fs.readFileSync(keyPath).toString(); } catch (err) { throw new Error(`Unable to read the certificate key.\n${err}`); } - + let encrypted; - + try { encrypted = crypto.publicEncrypt(certificate, Buffer.from("test")); } catch (err) { throw new Error(`The certificate is not valid.\n${err}`); } - + try { crypto.privateDecrypt(key, encrypted); } catch (err) { throw new Error(`The certificate key is not valid.\n${err}`); } } - + export function verifyCertificates(certificatePath: string = defaults.localhostCertificatePath, keyPath: string = defaults.localhostKeyPath): boolean { let isCertificateValid: boolean = true; try { @@ -87,4 +86,4 @@ export function verifyCertificates(certificatePath: string = defaults.localhostC isCertificateValid = false; } return isCertificateValid && isCaCertificateInstalled(); -} +} \ No newline at end of file diff --git a/packages/office-addin-dev-certs/test/test.ts b/packages/office-addin-dev-certs/test/test.ts index 40a0a39d7..5b8914971 100644 --- a/packages/office-addin-dev-certs/test/test.ts +++ b/packages/office-addin-dev-certs/test/test.ts @@ -249,7 +249,7 @@ describe("office-addin-dev-certs", function() { sandbox.stub(childProcess, "execSync").callsFake(execSync); try { const ret = await verify.isCaCertificateInstalled(); - assert.strictEqual(execSync.callCount, 1); + assert.strictEqual(execSync.callCount, (process.platform === "darwin") ? 2 : 1); assert.strictEqual(ret, true); } catch (err) { // not expecting any exception