Skip to content

Commit e38c63d

Browse files
authored
Create needed Cert during build (#29)
1 parent fa799d4 commit e38c63d

File tree

7 files changed

+125
-21
lines changed

7 files changed

+125
-21
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ out/
55
.env
66
assets/cert/cert.key
77
assets/cert/cert.crt
8+
assets/cert/cert.pem
9+
assets/cert/key.pem

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ echo '{"command":"passcode", "message":"'$PASSCODE'"}' | websocat -k wss://local
4646

4747
This will create a single executable in the `dist` folder for the specific platform you're running on.
4848

49+
## But MacOS says it's damaged
50+
51+
- Because MacOS is often (wisely) configured only to run apps siged by authors registered with Apple, it often won't even ask if you'd like to run a perfectly valid application. If you built an apple app distribution and it says
52+
53+
"gpg-bridge.app" is damaged and can't be opened. You should move it to the Trash.
54+
55+
when you try to run it, don't worry, the build probably worked, MacOS is just trying to keep you safe. Open a terminal and run:
56+
57+
xattr -d com.apple.quarantine /path/to/gpg-bridge.app
58+
59+
60+
4961
## TODO
5062

5163
- [x] Should show itself in the system tray.

certs.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
const path = require("path");
2+
const fs = require("fs");
3+
4+
const keyNameRegex = /^cert(?:ificate)?\.key$|^key\.pem$/i;
5+
const certNameRegex = /^cert(?:ificate)?\.(?:crt|pem)$/i;
6+
7+
const assetsDir = path.join(__dirname, 'assets');
8+
9+
function findCertificates() {
10+
const certDir = path.join(assetsDir, 'cert');
11+
const candidates = fs.readdirSync(certDir);
12+
const keyNames = candidates.filter(name => keyNameRegex.test(name));
13+
const certNames = candidates.filter(name => certNameRegex.test(name));
14+
15+
const errors = [];
16+
for (const [a, b] of [[keyNames, 'key'], [certNames, 'certificate']]) {
17+
if (a.length === 0) {
18+
errors.push(`No ${b} file.`);
19+
} else if (a.length > 1) {
20+
errors.push(`Multiple ${b} files.`);
21+
}
22+
}
23+
24+
if (keyNames.length === 0 && certNames.length === 0) {
25+
return {noCertFiles: true, badCertFiles: false, certDir, keyFilename: null, certFilename: null, errors}
26+
}
27+
if (errors.length > 0) {
28+
return {noCertFiles: false, badCertFiles: true, certDir, keyFilename: null, certFilename: null, errors}
29+
}
30+
31+
const keyFilename = path.join(certDir, keyNames[0]);
32+
const certFilename = path.join(certDir, certNames[0]);
33+
return {keyFilename, certFilename, noCertFiles: false, badCertFiles: false, certDir, errors};
34+
}
35+
36+
function loadCertificates() {
37+
const result = findCertificates();
38+
if (result.noCertFiles) {
39+
throw new Error(`No certificate files found in '${result.certDir}'.`);
40+
}
41+
if (result.badCertFiles) {
42+
throw new Error(`Ambiguous or incomplete certificate files found in '${result.certDir}'. ${result.errors.join(' ')}`);
43+
}
44+
const [privateKey, certificate] = [
45+
fs.readFileSync(result.keyFilename, 'utf8'),
46+
fs.readFileSync(result.certFilename, 'utf8'),
47+
];
48+
return {privateKey, certificate};
49+
}
50+
51+
function createCertificates() {
52+
const result = findCertificates();
53+
if (result.badCertFiles) {
54+
const msg = `Ambiguous or incomplete certificate files found in '${result.certDir}'. ${result.errors.join(' ')}`;
55+
console.error(msg);
56+
console.error('Throwing error because the built app will likely fail.')
57+
throw new Error(msg);
58+
}
59+
if (!result.noCertFiles) {
60+
console.log('INFO Certificate files already present. NOT creating certificates.');
61+
return;
62+
}
63+
console.log('INFO Creating self signed certificate.');
64+
const selfsigned = require('selfsigned');
65+
const pems = selfsigned.generate([
66+
{ shortName: 'ST', value: 'Texas' },
67+
{ name: 'countryName', value: 'US' },
68+
{ name: 'localityName', value: 'Austin' },
69+
{ name: 'organizationName', value: 'Unchained' },
70+
{ name: 'commonName', value: 'GPG-Bridge' },
71+
], {});
72+
73+
const certDir = path.join(assetsDir, 'cert');
74+
fs.writeFileSync(path.join(certDir, 'key.pem'), pems.private);
75+
fs.writeFileSync(path.join(certDir, 'cert.pem'), pems.cert);
76+
}
77+
78+
module.exports = {loadCertificates, createCertificates};
79+

forge.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ const pkg = require("./package.json")
44
const { FusesPlugin } = require("@electron-forge/plugin-fuses");
55
const { FuseV1Options, FuseVersion } = require("@electron/fuses");
66

7+
const {createCertificates } = require('./certs.js');
8+
9+
createCertificates();
10+
711
module.exports = {
812
packagerConfig: {
913
asar: false,

main.js

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const { createServer } = require('https');
99
const tmp = require("tmp-promise")
1010
const isMac = process.platform === 'darwin'
1111

12+
const { loadCertificates } = require('./certs.js');
13+
1214
tmp.setGracefulCleanup()
1315

1416
// Temporary directory location
@@ -152,26 +154,6 @@ let httpsServer;
152154
const assetsDir = path.join(__dirname, 'assets');
153155
console.log('INFO assetsDir =', assetsDir);
154156

155-
const keyNameRegex = /^cert(?:ificate)?\.key$|^key\.pem$/i;
156-
const certNameRegex = /^cert(?:ificate)?\.(?:crt|pem)$/i;
157-
async function loadCertificates() {
158-
const certDir = path.join(assetsDir, 'cert');
159-
const candidates = await fs.readdir(certDir);
160-
const keyname = candidates.find(name => keyNameRegex.test(name));
161-
const certname = candidates.find(name => certNameRegex.test(name));
162-
if (!keyname) {
163-
throw new Error('Expected file `assets/cert/cert.key` not found.');
164-
}
165-
if (!certname) {
166-
throw new Error('Expected file `assets/cert/cert.crt` not found.');
167-
}
168-
const [privateKey, certificate] = await Promise.all([
169-
fs.readFile(path.join(certDir, keyname), 'utf8'),
170-
fs.readFile(path.join(certDir, certname), 'utf8'),
171-
]);
172-
return {privateKey, certificate};
173-
}
174-
175157
async function setupWebSocketServer() {
176158
try {
177159
const {privateKey, certificate} = await loadCertificates();

package-lock.json

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"dependencies": {
1818
"electron-squirrel-startup": "^1.0.0",
1919
"kill-port": "^2.0.1",
20+
"selfsigned": "^3.0.1",
2021
"tmp-promise": "^3.0.3",
2122
"ws": "^8.18.0",
2223
"zod": "^3.23.8"
@@ -36,5 +37,7 @@
3637
"dotenv": "^16.4.5",
3738
"electron": "^33.0.1"
3839
},
39-
"files": ["assets"]
40+
"files": [
41+
"assets"
42+
]
4043
}

0 commit comments

Comments
 (0)