-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathsignTest.mjs
136 lines (125 loc) · 3.39 KB
/
signTest.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import * as path from 'path';
import * as fs from 'fs';
import * as crypto from 'crypto';
import * as https from 'https';
import * as url from 'url';
import { Buffer } from 'buffer';
import * as process from 'process';
import * as ResEdit from '../../dist/index.js';
/** @typedef {import('../../dist').SignerObject} SignerObject */
const THIS_DIR = url.fileURLToPath(new URL('.', import.meta.url));
const INPUT_DIR = path.resolve(THIS_DIR, '../../test/input');
const WORK_DIR = path.resolve(THIS_DIR, '../../.work');
const SRC_EXE = path.resolve(
INPUT_DIR,
'LoadIconApp_HasIcon/x64/LoadIconApp_HasIcon.exe'
);
const DEST_EXE = path.resolve(WORK_DIR, 'LoadIconApp_HasIcon.sign.exe');
const CERT = path.resolve(INPUT_DIR, 'certs/test.cer');
const PRIVATE_KEY_PEM = path.resolve(INPUT_DIR, 'certs/test.priv.pem');
// TSA server to make timestamp data
const TIMESTAMP_SERVER = 'https://freetsa.org/tsr';
/**
* Requests to TSA.
* @param {ArrayBuffer} data binary data of TSQ
* @return {Promise<Buffer>} response from TSA
*/
function requestTimestamp(data) {
return new Promise((resolve, reject) => {
const bin = Buffer.from(data);
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/timestamp-query',
'Content-Length': bin.byteLength,
},
};
const req = https.request(TIMESTAMP_SERVER, options, (res) => {
const results = [];
res.on('data', (chunk) => {
results.push(chunk);
});
res.on('end', () => {
resolve(Buffer.concat(results));
});
});
req.write(bin);
req.end();
req.on('error', (e) => reject(e));
});
}
function main() {
const executable = ResEdit.NtExecutable.from(fs.readFileSync(SRC_EXE), {
ignoreCert: true,
});
/** @type {SignerObject} */
const signerObject = {
getDigestAlgorithm() {
return 'sha1';
},
getEncryptionAlgorithm() {
return 'rsa';
},
getCertificateData() {
return fs.readFileSync(CERT);
},
// digestData and encryptData must return PromiseLike object, so
// this example uses async method
async digestData(dataIterator) {
const hash = crypto.createHash('sha1');
while (true) {
const it = dataIterator.next();
if (it.done) {
break;
}
// NOTE: hash.update is not an async method, but use 'await' here for an example only.
await hash.update(Buffer.from(it.value));
}
return hash.digest();
},
async signData(dataIterator) {
const pkey = {
key: fs.readFileSync(PRIVATE_KEY_PEM, 'utf8'),
};
const binArray = [];
let totalLength = 0;
while (true) {
const it = dataIterator.next();
if (it.done) {
break;
}
binArray.push(Buffer.from(it.value));
totalLength += it.value.byteLength;
// NOTE: Use 'await' here for an example only.
await 0;
}
return crypto.sign(
'sha1',
Buffer.concat(binArray, totalLength),
pkey
);
},
// If timestamp is not necessary, this method can be omitted.
timestampData(data) {
// Promise with 'Buffer' object can be returned here
return requestTimestamp(data);
},
};
console.log('Make sign...');
ResEdit.generateExecutableWithSign(executable, signerObject).then(
(newBin) => {
const dir = path.dirname(DEST_EXE);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
fs.writeFileSync(DEST_EXE, Buffer.from(newBin));
console.log(`Done. (exe = '${DEST_EXE}')`);
process.exit(0);
},
(e) => {
console.error('Error:', e);
process.exit(1);
}
);
}
main();