-
-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Can install SSL certificate to a simulator
* Parses subject, fingerprint and der data from a PEM file using OpenSSL and adds it to TrustStore certificate to SQLite DB in tsettings * Exposed methods in index.js as 'installSSLCert' and 'uninstallSSLCert' that install/uninstall a certificate to a Simulator with given udid * Added test server that creates a dummy SSL test server
- Loading branch information
Showing
20 changed files
with
533 additions
and
111 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ node_modules | |
build | ||
*.log | ||
.DS_Store | ||
test/assets/certificate-test-server/random-pem.pem |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
// transpile:main | ||
|
||
import { getSimulator, getDeviceString } from './lib/simulator'; | ||
import { killAllSimulators, endAllSimulatorDaemons, simExists } from './lib/utils'; | ||
import { killAllSimulators, endAllSimulatorDaemons, simExists, installSSLCert, uninstallSSLCert } from './lib/utils'; | ||
|
||
export { getSimulator, getDeviceString, killAllSimulators, endAllSimulatorDaemons, simExists }; | ||
export { getSimulator, getDeviceString, killAllSimulators, endAllSimulatorDaemons, simExists, installSSLCert, uninstallSSLCert }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import crypto from 'crypto'; | ||
import B from 'bluebird'; | ||
import path from 'path'; | ||
|
||
const sqlite3 = B.promisifyAll(require('sqlite3')); | ||
const openssl = B.promisify(require('openssl-wrapper').exec); | ||
|
||
let tset = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n | ||
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"> | ||
<plist version=\"1.0\"> | ||
<array/> | ||
</plist>`; | ||
|
||
/** | ||
* Library for programatically adding certificates | ||
*/ | ||
class Certificate { | ||
|
||
constructor (pemFilename) { | ||
this.pemFilename = pemFilename; | ||
} | ||
|
||
/** | ||
* Add a certificate to the TrustStore | ||
*/ | ||
async add (dir) { | ||
let data = await this.getDerData(this.pemFilename); | ||
let subject = await this.getSubject(this.pemFilename); | ||
let fingerprint = await this.getFingerPrint(this.data); | ||
|
||
let trustStore = new TrustStore(dir); | ||
return trustStore.addRecord(fingerprint, tset, subject, data); | ||
} | ||
|
||
/** | ||
* Checks if keychain at given directory has this certificate | ||
*/ | ||
async has (dir) { | ||
let subject = await this.getSubject(this.pemFilename); | ||
|
||
let trustStore = new TrustStore(dir); | ||
let records = await trustStore.getRecords(subject); | ||
return records.length > 0; | ||
} | ||
|
||
/** | ||
* Remove certificate from the TrustStore | ||
*/ | ||
async remove (dir) { | ||
let subject = await this.getSubject(this.pemFilename); | ||
let trustStore = new TrustStore(dir); | ||
return trustStore.removeRecord(subject); | ||
} | ||
|
||
/** | ||
* Translate PEM file to DER buffer | ||
*/ | ||
async getDerData () { | ||
if (this.data) { | ||
return this.data; | ||
} | ||
|
||
// Convert 'pem' file to 'der' | ||
this.data = await openssl('x509', { | ||
outform: 'der', | ||
in: this.pemFilename | ||
}); | ||
|
||
return this.data; | ||
} | ||
|
||
/** | ||
* Get SHA1 fingerprint from der data before | ||
*/ | ||
async getFingerPrint () { | ||
if (this.fingerprint) { | ||
return this.fingerprint; | ||
} | ||
|
||
let data = await this.getDerData(); | ||
let shasum = crypto.createHash('sha1'); | ||
shasum.update(data); | ||
this.fingerprint = shasum.digest(); | ||
return this.fingerprint; | ||
} | ||
|
||
/** | ||
* Parse the subject from the der data | ||
*/ | ||
async getSubject () { | ||
if (this.subject) { | ||
return this.subject; | ||
} | ||
|
||
// Convert 'pem' file to 'der' | ||
let subject = await openssl('x509', { | ||
noout: true, | ||
subject: true, | ||
in: this.pemFilename | ||
}); | ||
let subRegex = /^subject[\w\W]*\/CN=([\w\W]*)(\n)?/; | ||
this.subject = subject.toString().match(subRegex)[1]; | ||
return this.subject; | ||
} | ||
|
||
} | ||
|
||
/** | ||
* Interface for adding and removing records to TrustStore.sqlite3 databases that Keychains use | ||
*/ | ||
class TrustStore { | ||
constructor (sharedResourceDir) { | ||
this.sqliteDBPath = path.resolve(sharedResourceDir, 'Library/Keychains/TrustStore.sqlite3'); | ||
this.db = new sqlite3.Database(this.sqliteDBPath); | ||
} | ||
|
||
/** | ||
* Add record to tsettings | ||
*/ | ||
async addRecord (sha1, tset, subj, data) { | ||
let existingRecords = await this.getRecords(subj); | ||
if (existingRecords.length > 0) { | ||
return await this.db.runAsync(`UPDATE tsettings SET sha1=?, tset=?, data=? WHERE subj=?`, [sha1, tset, data, subj]); | ||
} else { | ||
return await this.db.runAsync(`INSERT INTO tsettings (sha1, subj, tset, data) VALUES (?, ?, ?, ?)`, [sha1, subj, tset, data]); | ||
} | ||
} | ||
|
||
/** | ||
* Remove record from tsettings | ||
*/ | ||
async removeRecord (subj) { | ||
return this.db.runAsync(`DELETE FROM tsettings WHERE subj = ?`, [subj]); | ||
} | ||
|
||
/** | ||
* Get a record from tsettings | ||
*/ | ||
async getRecords (subj) { | ||
return this.db.allAsync(`SELECT * FROM tsettings WHERE subj = ?`, [subj]); | ||
} | ||
} | ||
|
||
export default Certificate; | ||
export { Certificate, TrustStore }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
��n��\8�ys2�/ w%� |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Charles Proxy CA (15 Nov 2016, Daniels-MacBook-Pro.local)/OU=https://charlesproxy.com/ssl/O=XK72 Ltd/L=Auckland/ST=Auckland/C=NZ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* eslint-disable no-console */ | ||
import https from 'https'; | ||
import inquirer from 'inquirer'; | ||
|
||
import { installSSLCert, uninstallSSLCert } from '../../../lib/utils'; | ||
import { getDevices } from 'node-simctl'; | ||
import B from 'bluebird'; | ||
|
||
/* jshint ignore:start */ /* eslint-disable no-unused-vars */ | ||
import colors from 'colors'; | ||
/* eslint-enable */ /* jshint ignore:end */ | ||
|
||
const pem = B.promisifyAll(require('pem')); | ||
|
||
(async () => { | ||
|
||
// Create an HTTPS server with a randomly generated certificate | ||
let key = await pem.createPrivateKeyAsync(); | ||
let keys = await pem.createCertificateAsync({days:1, selfSigned: true, serviceKey: key.key}); | ||
|
||
let server = https.createServer({key: keys.serviceKey, cert: keys.certificate}, function (req, res) { | ||
res.end('If you are seeing this the certificate has been installed'); | ||
}).listen(9758); | ||
|
||
console.log('Make sure you have at least one IOS Simulator running'.yellow); | ||
let devices = await getDevices(); | ||
|
||
// Get currently booted devices | ||
let bootedDevices = []; | ||
for (let osName in devices) { | ||
let os = devices[osName]; | ||
for (let deviceName in os) { | ||
let device = os[deviceName]; | ||
if (device.state === 'Booted') { | ||
bootedDevices.push(device); | ||
} | ||
} | ||
} | ||
|
||
if (bootedDevices.length === 0) { | ||
return console.log('You must have at least one IOS Simulator running to do this test'.red); | ||
} | ||
|
||
// Get info for first device | ||
let bootedDevice = bootedDevices[0]; | ||
let udid = bootedDevice.udid; | ||
let deviceName = bootedDevice.name; | ||
|
||
console.log('HTTPS server is running at localhost:9758 and has created a new certificate at "random-pem.pem"'.yellow); | ||
console.log(`Navigate to https://localhost:9758 in '${deviceName} Simulator'`.yellow); | ||
console.log('DO NOT PUSH THE CONTINUE BUTTON. PUSH CANCEL.'.red); | ||
|
||
// Call this if the user answers 'No' to any prompts | ||
async function done () { | ||
await uninstallSSLCert(keys.certificate, udid); | ||
server.close(); | ||
console.log('Incomplete/failed test'.red); | ||
} | ||
|
||
let result = await inquirer.prompt([{ | ||
type: 'confirm', | ||
name: 'confirmOpenSite', | ||
message: `Is https//localhost:9758 on '${deviceName} Simulator' unaccessible?`, | ||
}]); | ||
|
||
console.log('Certificate', keys.certificate, udid); | ||
|
||
await installSSLCert(keys.certificate, udid); | ||
|
||
if (!result.confirmOpenSite) return done(); | ||
|
||
// Apply certificate to Simulator | ||
console.log('Installing certificate'.yellow); | ||
console.log(`Certificate installed to '${deviceName} ${udid}'. Navigate back to https://localhost:9758.`.yellow); | ||
|
||
result = await inquirer.prompt([{ | ||
type: 'confirm', | ||
name: 'confirmOpenedSite', | ||
message: 'Now is https://localhost:9758 accessible?', | ||
}]); | ||
|
||
if (!result.confirmOpenedSite) { | ||
return done(); | ||
} | ||
|
||
// Uninstall cert | ||
console.log(`Uninstalling SSL cert`.yellow); | ||
await uninstallSSLCert(keys.certificate, udid); | ||
console.log(`SSL cert removed.`.yellow); | ||
console.log(`Close the simulator, re-open it and then navigate back to https://localhost:9758`.yellow); | ||
|
||
result = await inquirer.prompt([{ | ||
type: 'confirm', | ||
name: 'confirmUninstallCert', | ||
message: `Is https://localhost:9758 unaccessible?`, | ||
}]); | ||
|
||
if (result.confirmUninstallCert) { | ||
console.log('Test passed'.green); | ||
} | ||
|
||
return server.close(); | ||
})(); | ||
|
||
/* eslint-enable */ |
Oops, something went wrong.