Added time stamping feature to authenticode.js
Ylian Saint-Hilaire committed
Jun 17, 2022 at 20:56 UTC
e8a67c4c704eb8d515ec30f400f3a4ae3964daeb
1 file changed
+342
-70
authenticode.js
+342
-70
@@ -288,12 +288,10 @@ function createAuthenticodeHandler(path) {
288
var derlen = forge.asn1.getBerValueLength(forge.util.createBuffer(pkcs7raw.slice(1, 5))) + 4;
289
if (derlen != pkcs7raw.length) { pkcs7raw = pkcs7raw.slice(0, derlen); }
290
291
- //console.log('pkcs7raw', Buffer.from(pkcs7raw, 'binary').toString('base64'));
292
-
291
// Decode the signature block
292
var pkcs7der = forge.asn1.fromDer(forge.util.createBuffer(pkcs7raw));
293
296
- // To work around ForgeJS PKCS#7 limitation, this may break PKCS7 verify if ForjeJS adds support for it in the future
294
+ // To work around ForgeJS PKCS#7 limitation, this may break PKCS7 verify if ForgeJS adds support for it in the future
295
// Switch content type from "1.3.6.1.4.1.311.2.1.4" to "1.2.840.113549.1.7.1"
296
pkcs7der.value[1].value[0].value[2].value[0].value = forge.asn1.oidToDer(forge.pki.oids.data).data;
297
@@ -330,7 +328,7 @@ function createAuthenticodeHandler(path) {
328
) {
329
var v = pkcs7.rawCapture.authenticatedAttributes[i].value[1].value[0].value[j].value[0].value;
330
if (v.startsWith('http://') || v.startsWith('https://') || ((v.length % 2) == 1)) { obj.signingAttribs.push(v); } else {
333
- var r = ""; // This string value is in UCS2 format, convert it to a normal string.
331
+ var r = ''; // This string value is in UCS2 format, convert it to a normal string.
332
for (var k = 0; k < v.length; k += 2) { r += String.fromCharCode((v.charCodeAt(k + 8) << 8) + v.charCodeAt(k + 1)); }
333
obj.signingAttribs.push(r);
334
}
@@ -366,7 +364,7 @@ function createAuthenticodeHandler(path) {
364
}
365
366
// Make a timestamp signature request
369
- obj.timeStampRequest = function (url, func) {
367
+ obj.timeStampRequest = function (args, func) {
368
// Create the timestamp request in DER format
369
const asn1 = forge.asn1;
370
const pkcs7dataOid = asn1.oidToDer('1.2.840.113549.1.7.1').data;
@@ -398,20 +396,90 @@ function createAuthenticodeHandler(path) {
396
}
397
};
398
401
- console.log('options', options);
402
- console.log('requestBody', requestBody);
403
-
404
- // Debug
405
- const sampleResponse = "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";
406
- func(null, sampleResponse);
407
- return;
408
-
399
// Set up the request
400
var responseAccumulator = '';
411
- var req = http.request(url, options, function (res) {
401
+ var req = http.request(args.time, options, function (res) {
402
res.setEncoding('utf8');
403
res.on('data', function (chunk) { responseAccumulator += chunk; });
414
- res.on('end', function () { func(null, Buffer.from(responseAccumulator, 'base64').toString('base64')); });
404
+ res.on('end', function () {
405
+ // Decode the timestamp signature block
406
+ const timepkcs7der = forge.asn1.fromDer(forge.util.createBuffer(Buffer.from(responseAccumulator, 'base64').toString('binary')));
407
+
408
+ // Decode the executable signature block
409
+ const pkcs7der = forge.asn1.fromDer(forge.util.createBuffer(Buffer.from(obj.getRawSignatureBlock(), 'base64').toString('binary')));
410
+
411
+ // Get the ASN1 certificates used to sign the timestamp and add them to the certs in the PKCS7 of the executable
412
+ // TODO: We could look to see if the certificate is already present in the executable
413
+ const timeasn1Certs = timepkcs7der.value[1].value[0].value[3].value;
414
+ for (var i in timeasn1Certs) { pkcs7der.value[1].value[0].value[3].value.push(timeasn1Certs[i]); }
415
+
416
+ // Get the time signature and add it to the executables PKCS7
417
+ const timeasn1Signature = timepkcs7der.value[1].value[0].value[4];
418
+ const countersignatureOid = asn1.oidToDer('1.2.840.113549.1.9.6').data;
419
+ const asn1obj2 =
420
+ asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
421
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
422
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, countersignatureOid),
423
+ timeasn1Signature
424
+ ])
425
+ ]);
426
+ pkcs7der.value[1].value[0].value[4].value[0].value.push(asn1obj2);
427
+
428
+ // Re-encode the executable signature block
429
+ const p7signature = Buffer.from(forge.asn1.toDer(pkcs7der).data, 'binary');
430
+
431
+ // Open the output file
432
+ var output = null;
433
+ try { output = fs.openSync(args.out, 'w+'); } catch (ex) { }
434
+ if (output == null) return false;
435
+ var tmp, written = 0;
436
+ var executableSize = obj.header.sigpos ? obj.header.sigpos : this.filesize;
437
+
438
+ // Compute pre-header length and copy that to the new file
439
+ var preHeaderLen = (obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16));
440
+ var tmp = readFileSlice(written, preHeaderLen);
441
+ fs.writeSync(output, tmp);
442
+ written += tmp.length;
443
+
444
+ // Quad Align the results, adding padding if necessary
445
+ var len = executableSize + p7signature.length;
446
+ var padding = (8 - ((len) % 8)) % 8;
447
+
448
+ // Write the signature header
449
+ var addresstable = Buffer.alloc(8);
450
+ addresstable.writeUInt32LE(executableSize);
451
+ addresstable.writeUInt32LE(8 + p7signature.length + padding, 4);
452
+ fs.writeSync(output, addresstable);
453
+ written += addresstable.length;
454
+
455
+ // Copy the rest of the file until the start of the signature block
456
+ while ((executableSize - written) > 0) {
457
+ tmp = readFileSlice(written, Math.min(executableSize - written, 65536));
458
+ fs.writeSync(output, tmp);
459
+ written += tmp.length;
460
+ }
461
+
462
+ // Write the signature block header and signature
463
+ var win = Buffer.alloc(8); // WIN CERTIFICATE Structure
464
+ win.writeUInt32LE(p7signature.length + padding + 8); // DWORD length
465
+ win.writeUInt16LE(512, 4); // WORD revision
466
+ win.writeUInt16LE(2, 6); // WORD type
467
+ fs.writeSync(output, win);
468
+ fs.writeSync(output, p7signature);
469
+ if (padding > 0) { fs.writeSync(output, Buffer.alloc(padding, 0)); }
470
+ written += (p7signature.length + padding + 8);
471
+
472
+ // Compute the checksum and write it in the PE header checksum location
473
+ var tmp = Buffer.alloc(4);
474
+ tmp.writeUInt32LE(runChecksumOnFile(output, written, ((obj.header.peOptionalHeaderLocation + 64) / 4)));
475
+ fs.writeSync(output, tmp, 0, 4, obj.header.peOptionalHeaderLocation + 64);
476
+
477
+ // Close the file
478
+ fs.closeSync(output);
479
+
480
+ // Indicate we are done
481
+ func(null);
482
+ });
483
});
484
485
// Post the data
@@ -1110,7 +1178,7 @@ function createAuthenticodeHandler(path) {
1178
}
1179
1180
// Sign the file using the certificate and key. If none is specified, generate a dummy one
1113
- obj.sign = function (cert, args) {
1181
+ obj.sign = function (cert, args, func) {
1182
if (cert == null) { cert = createSelfSignedCert({ cn: 'Test' }); }
1183
1184
// Set the hash algorithm hash OID
@@ -1121,16 +1189,16 @@ function createAuthenticodeHandler(path) {
1189
if (args.hash == 'sha512') { hashOid = forge.pki.oids.sha512; fileHash = obj.getHash('sha512'); }
1190
if (args.hash == 'sha224') { hashOid = forge.pki.oids.sha224; fileHash = obj.getHash('sha224'); }
1191
if (args.hash == 'md5') { hashOid = forge.pki.oids.md5; fileHash = obj.getHash('md5'); }
1124
- if (hashOid == null) return false;
1192
+ if (hashOid == null) { func(false); return; };
1193
1194
// Create the signature block
1127
- var p7 = forge.pkcs7.createSignedData();
1195
+ var xp7 = forge.pkcs7.createSignedData();
1196
var content = { 'tagClass': 0, 'type': 16, 'constructed': true, 'composed': true, 'value': [{ 'tagClass': 0, 'type': 16, 'constructed': true, 'composed': true, 'value': [{ 'tagClass': 0, 'type': 6, 'constructed': false, 'composed': false, 'value': forge.asn1.oidToDer('1.3.6.1.4.1.311.2.1.15').data }, { 'tagClass': 0, 'type': 16, 'constructed': true, 'composed': true, 'value': [{ 'tagClass': 0, 'type': 3, 'constructed': false, 'composed': false, 'value': '\u0000', 'bitStringContents': '\u0000', 'original': { 'tagClass': 0, 'type': 3, 'constructed': false, 'composed': false, 'value': '\u0000' } }, { 'tagClass': 128, 'type': 0, 'constructed': true, 'composed': true, 'value': [{ 'tagClass': 128, 'type': 2, 'constructed': true, 'composed': true, 'value': [{ 'tagClass': 128, 'type': 0, 'constructed': false, 'composed': false, 'value': '' }] }] }] }] }, { 'tagClass': 0, 'type': 16, 'constructed': true, 'composed': true, 'value': [{ 'tagClass': 0, 'type': 16, 'constructed': true, 'composed': true, 'value': [{ 'tagClass': 0, 'type': 6, 'constructed': false, 'composed': false, 'value': forge.asn1.oidToDer(hashOid).data }, { 'tagClass': 0, 'type': 5, 'constructed': false, 'composed': false, 'value': '' }] }, { 'tagClass': 0, 'type': 4, 'constructed': false, 'composed': false, 'value': fileHash.toString('binary') }] }] };
1129
- p7.contentInfo = forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, true, [forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.OID, false, forge.asn1.oidToDer('1.3.6.1.4.1.311.2.1.4').getBytes())]);
1130
- p7.contentInfo.value.push(forge.asn1.create(forge.asn1.Class.CONTEXT_SPECIFIC, 0, true, [content]));
1131
- p7.content = {}; // We set .contentInfo and have .content empty to bypass node-forge limitation on the type of content it can sign.
1132
- p7.addCertificate(cert.cert);
1133
- if (cert.extraCerts) { for (var i = 0; i < cert.extraCerts.length; i++) { p7.addCertificate(cert.extraCerts[0]); } } // Add any extra certificates that form the cert chain
1197
+ xp7.contentInfo = forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, true, [forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.OID, false, forge.asn1.oidToDer('1.3.6.1.4.1.311.2.1.4').getBytes())]);
1198
+ xp7.contentInfo.value.push(forge.asn1.create(forge.asn1.Class.CONTEXT_SPECIFIC, 0, true, [content]));
1199
+ xp7.content = {}; // We set .contentInfo and have .content empty to bypass node-forge limitation on the type of content it can sign.
1200
+ xp7.addCertificate(cert.cert);
1201
+ if (cert.extraCerts) { for (var i = 0; i < cert.extraCerts.length; i++) { xp7.addCertificate(cert.extraCerts[0]); } } // Add any extra certificates that form the cert chain
1202
1203
// Build authenticated attributes
1204
var authenticatedAttributes = [
@@ -1149,22 +1217,109 @@ function createAuthenticodeHandler(path) {
1217
}
1218
1219
// Add the signer and sign
1152
- p7.addSigner({
1220
+ xp7.addSigner({
1221
key: cert.key,
1222
certificate: cert.cert,
1223
digestAlgorithm: forge.pki.oids.sha384,
1224
authenticatedAttributes: authenticatedAttributes
1225
});
1158
- p7.sign();
1159
- var p7signature = Buffer.from(forge.pkcs7.messageToPem(p7).split('-----BEGIN PKCS7-----')[1].split('-----END PKCS7-----')[0], 'base64');
1160
- //console.log('Signature', Buffer.from(p7signature, 'binary').toString('base64'));
1226
+ xp7.sign();
1227
+ var p7signature = Buffer.from(forge.pkcs7.messageToPem(xp7).split('-----BEGIN PKCS7-----')[1].split('-----END PKCS7-----')[0], 'base64');
1228
+
1229
+ if (args.time == null) {
1230
+ // Sign the executable without timestamp
1231
+ signEx(args, p7signature, obj.filesize, func);
1232
+ } else {
1233
+ // Decode the signature block
1234
+ var pkcs7der = forge.asn1.fromDer(forge.util.createBuffer(p7signature));
1235
+
1236
+ // To work around ForgeJS PKCS#7 limitation, this may break PKCS7 verify if ForgeJS adds support for it in the future
1237
+ // Switch content type from "1.3.6.1.4.1.311.2.1.4" to "1.2.840.113549.1.7.1"
1238
+ pkcs7der.value[1].value[0].value[2].value[0].value = forge.asn1.oidToDer(forge.pki.oids.data).data;
1239
+
1240
+ // Decode the PKCS7 message
1241
+ var pkcs7 = p7.messageFromAsn1(pkcs7der);
1242
1243
+ // Create the timestamp request in DER format
1244
+ const asn1 = forge.asn1;
1245
+ const pkcs7dataOid = asn1.oidToDer('1.2.840.113549.1.7.1').data;
1246
+ const microsoftCodeSigningOid = asn1.oidToDer('1.3.6.1.4.1.311.3.2.1').data;
1247
+ const asn1obj =
1248
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
1249
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, microsoftCodeSigningOid),
1250
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
1251
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, pkcs7dataOid),
1252
+ asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
1253
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, pkcs7.rawCapture.signature.toString('binary')) // Signature here
1254
+ ])
1255
+ ])
1256
+ ]);
1257
+
1258
+ // Re-decode the PKCS7 from the executable, this time, no workaround needed
1259
+ pkcs7der = forge.asn1.fromDer(forge.util.createBuffer(p7signature));
1260
+
1261
+ // Serialize an ASN.1 object to DER format in Base64
1262
+ const requestBody = Buffer.from(asn1.toDer(asn1obj).data, 'binary').toString('base64');
1263
+
1264
+ // Make an HTTP request
1265
+ const http = require('http');
1266
+ var options = {
1267
+ method: 'POST',
1268
+ headers: {
1269
+ 'accept': 'application/octet-stream',
1270
+ 'cache-control': 'no-cache',
1271
+ 'user-agent': 'Transport',
1272
+ 'content-type': 'application/octet-stream',
1273
+ 'content-length': Buffer.byteLength(requestBody)
1274
+ }
1275
+ };
1276
+
1277
+ // Set up the request
1278
+ var responseAccumulator = '';
1279
+ var req = http.request(args.time, options, function (res) {
1280
+ res.setEncoding('utf8');
1281
+ res.on('data', function (chunk) { responseAccumulator += chunk; });
1282
+ res.on('end', function () {
1283
+ // Decode the timestamp signature block
1284
+ const timepkcs7der = forge.asn1.fromDer(forge.util.createBuffer(Buffer.from(responseAccumulator, 'base64').toString('binary')));
1285
+
1286
+ // Get the ASN1 certificates used to sign the timestamp and add them to the certs in the PKCS7 of the executable
1287
+ // TODO: We could look to see if the certificate is already present in the executable
1288
+ const timeasn1Certs = timepkcs7der.value[1].value[0].value[3].value;
1289
+ for (var i in timeasn1Certs) { pkcs7der.value[1].value[0].value[3].value.push(timeasn1Certs[i]); }
1290
+
1291
+ // Get the time signature and add it to the executables PKCS7
1292
+ const timeasn1Signature = timepkcs7der.value[1].value[0].value[4];
1293
+ const countersignatureOid = asn1.oidToDer('1.2.840.113549.1.9.6').data;
1294
+ const asn1obj2 =
1295
+ asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
1296
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
1297
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, countersignatureOid),
1298
+ timeasn1Signature
1299
+ ])
1300
+ ]);
1301
+ pkcs7der.value[1].value[0].value[4].value[0].value.push(asn1obj2);
1302
+
1303
+ // Re-encode the executable signature block
1304
+ const p7signature = Buffer.from(forge.asn1.toDer(pkcs7der).data, 'binary');
1305
+
1306
+ // Write the file with the signature block
1307
+ signEx(args, p7signature, obj.filesize, func);
1308
+ });
1309
+ });
1310
+
1311
+ // Post the data
1312
+ req.write(requestBody);
1313
+ req.end();
1314
+ }
1315
+ }
1316
+
1317
+ function signEx(args, p7signature, filesize, func) {
1318
// Open the output file
1319
var output = null;
1320
try { output = fs.openSync(args.out, 'w+'); } catch (ex) { }
1165
- if (output == null) return false;
1166
- var tmp, written = 0;
1167
- var executableSize = obj.header.sigpos ? obj.header.sigpos : this.filesize;
1321
+ if (output == null) { func(false); return; }
1322
+ var tmp, written = 0, executableSize = obj.header.sigpos ? obj.header.sigpos : filesize;
1323
1324
// Compute pre-header length and copy that to the new file
1325
var preHeaderLen = (obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16));
@@ -1207,7 +1362,7 @@ function createAuthenticodeHandler(path) {
1362
1363
// Close the file
1364
fs.closeSync(output);
1210
- return true;
1365
+ func(null);
1366
}
1367
1368
// Save an executable without the signature
@@ -1242,7 +1397,7 @@ function createAuthenticodeHandler(path) {
1397
}
1398
1399
// Save the executable
1245
- obj.writeExecutable = function (args, cert) {
1400
+ obj.writeExecutable = function (args, cert, func) {
1401
// Open the file
1402
var output = fs.openSync(args.out, 'w+');
1403
var tmp, written = 0;
@@ -1340,13 +1495,13 @@ function createAuthenticodeHandler(path) {
1495
if (hashOid == null) return false;
1496
1497
// Create the signature block
1343
- var p7 = forge.pkcs7.createSignedData();
1498
+ var xp7 = forge.pkcs7.createSignedData();
1499
var content = { 'tagClass': 0, 'type': 16, 'constructed': true, 'composed': true, 'value': [{ 'tagClass': 0, 'type': 16, 'constructed': true, 'composed': true, 'value': [{ 'tagClass': 0, 'type': 6, 'constructed': false, 'composed': false, 'value': forge.asn1.oidToDer('1.3.6.1.4.1.311.2.1.15').data }, { 'tagClass': 0, 'type': 16, 'constructed': true, 'composed': true, 'value': [{ 'tagClass': 0, 'type': 3, 'constructed': false, 'composed': false, 'value': '\u0000', 'bitStringContents': '\u0000', 'original': { 'tagClass': 0, 'type': 3, 'constructed': false, 'composed': false, 'value': '\u0000' } }, { 'tagClass': 128, 'type': 0, 'constructed': true, 'composed': true, 'value': [{ 'tagClass': 128, 'type': 2, 'constructed': true, 'composed': true, 'value': [{ 'tagClass': 128, 'type': 0, 'constructed': false, 'composed': false, 'value': '' }] }] }] }] }, { 'tagClass': 0, 'type': 16, 'constructed': true, 'composed': true, 'value': [{ 'tagClass': 0, 'type': 16, 'constructed': true, 'composed': true, 'value': [{ 'tagClass': 0, 'type': 6, 'constructed': false, 'composed': false, 'value': forge.asn1.oidToDer(hashOid).data }, { 'tagClass': 0, 'type': 5, 'constructed': false, 'composed': false, 'value': '' }] }, { 'tagClass': 0, 'type': 4, 'constructed': false, 'composed': false, 'value': fileHash.toString('binary') }] }] };
1345
- p7.contentInfo = forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, true, [forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.OID, false, forge.asn1.oidToDer('1.3.6.1.4.1.311.2.1.4').getBytes())]);
1346
- p7.contentInfo.value.push(forge.asn1.create(forge.asn1.Class.CONTEXT_SPECIFIC, 0, true, [content]));
1347
- p7.content = {}; // We set .contentInfo and have .content empty to bypass node-forge limitation on the type of content it can sign.
1348
- p7.addCertificate(cert.cert);
1349
- if (cert.extraCerts) { for (var i = 0; i < cert.extraCerts.length; i++) { p7.addCertificate(cert.extraCerts[0]); } } // Add any extra certificates that form the cert chain
1500
+ xp7.contentInfo = forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, true, [forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.OID, false, forge.asn1.oidToDer('1.3.6.1.4.1.311.2.1.4').getBytes())]);
1501
+ xp7.contentInfo.value.push(forge.asn1.create(forge.asn1.Class.CONTEXT_SPECIFIC, 0, true, [content]));
1502
+ xp7.content = {}; // We set .contentInfo and have .content empty to bypass node-forge limitation on the type of content it can sign.
1503
+ xp7.addCertificate(cert.cert);
1504
+ if (cert.extraCerts) { for (var i = 0; i < cert.extraCerts.length; i++) { xp7.addCertificate(cert.extraCerts[0]); } } // Add any extra certificates that form the cert chain
1505
1506
// Build authenticated attributes
1507
var authenticatedAttributes = [
@@ -1365,45 +1520,144 @@ function createAuthenticodeHandler(path) {
1520
}
1521
1522
// Add the signer and sign
1368
- p7.addSigner({
1523
+ xp7.addSigner({
1524
key: cert.key,
1525
certificate: cert.cert,
1526
digestAlgorithm: forge.pki.oids.sha384,
1527
authenticatedAttributes: authenticatedAttributes
1528
});
1374
- p7.sign();
1375
- var p7signature = Buffer.from(forge.pkcs7.messageToPem(p7).split('-----BEGIN PKCS7-----')[1].split('-----END PKCS7-----')[0], 'base64');
1529
+ xp7.sign();
1530
+ var p7signature = Buffer.from(forge.pkcs7.messageToPem(xp7).split('-----BEGIN PKCS7-----')[1].split('-----END PKCS7-----')[0], 'base64');
1531
//console.log('Signature', Buffer.from(p7signature, 'binary').toString('base64'));
1532
1378
- // Quad Align the results, adding padding if necessary
1379
- var len = written + p7signature.length;
1380
- var padding = (8 - ((len) % 8)) % 8;
1381
-
1382
- // Write the signature block header and signature
1383
- var win = Buffer.alloc(8); // WIN CERTIFICATE Structure
1384
- win.writeUInt32LE(p7signature.length + padding + 8); // DWORD length
1385
- win.writeUInt16LE(512, 4); // WORD revision
1386
- win.writeUInt16LE(2, 6); // WORD type
1387
- fs.writeSync(output, win);
1388
- fs.writeSync(output, p7signature);
1389
- if (padding > 0) { fs.writeSync(output, Buffer.alloc(padding, 0)); }
1390
-
1391
- // Write the signature header
1392
- var addresstable = Buffer.alloc(8);
1393
- addresstable.writeUInt32LE(written);
1394
- addresstable.writeUInt32LE(8 + p7signature.length + padding, 4);
1395
- var signatureHeaderLocation = (obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16));
1396
- fs.writeSync(output, addresstable, 0, 8, signatureHeaderLocation);
1397
- written += (p7signature.length + padding + 8); // Add the signature block to written counter
1398
-
1399
- // Compute the checksum and write it in the PE header checksum location
1400
- var tmp = Buffer.alloc(4);
1401
- tmp.writeUInt32LE(runChecksumOnFile(output, written, ((obj.header.peOptionalHeaderLocation + 64) / 4)));
1402
- fs.writeSync(output, tmp, 0, 4, obj.header.peOptionalHeaderLocation + 64);
1533
+ if (args.time == null) {
1534
+ // Write the signature block to the output executable without time stamp
1535
+ writeExecutableEx(output, p7signature, written, func);
1536
+ } else {
1537
+ // Decode the signature block
1538
+ var pkcs7der = forge.asn1.fromDer(forge.util.createBuffer(p7signature));
1539
+
1540
+ // To work around ForgeJS PKCS#7 limitation, this may break PKCS7 verify if ForgeJS adds support for it in the future
1541
+ // Switch content type from "1.3.6.1.4.1.311.2.1.4" to "1.2.840.113549.1.7.1"
1542
+ pkcs7der.value[1].value[0].value[2].value[0].value = forge.asn1.oidToDer(forge.pki.oids.data).data;
1543
+
1544
+ // Decode the PKCS7 message
1545
+ var pkcs7 = p7.messageFromAsn1(pkcs7der);
1546
+
1547
+ // Create the timestamp request in DER format
1548
+ const asn1 = forge.asn1;
1549
+ const pkcs7dataOid = asn1.oidToDer('1.2.840.113549.1.7.1').data;
1550
+ const microsoftCodeSigningOid = asn1.oidToDer('1.3.6.1.4.1.311.3.2.1').data;
1551
+ const asn1obj =
1552
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
1553
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, microsoftCodeSigningOid),
1554
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
1555
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, pkcs7dataOid),
1556
+ asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
1557
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, pkcs7.rawCapture.signature.toString('binary')) // Signature here
1558
+ ])
1559
+ ])
1560
+ ]);
1561
+
1562
+ // Re-decode the PKCS7 from the executable, this time, no workaround needed
1563
+ pkcs7der = forge.asn1.fromDer(forge.util.createBuffer(p7signature));
1564
+
1565
+ // Serialize an ASN.1 object to DER format in Base64
1566
+ const requestBody = Buffer.from(asn1.toDer(asn1obj).data, 'binary').toString('base64');
1567
+
1568
+ // Make an HTTP request
1569
+ const http = require('http');
1570
+ var options = {
1571
+ method: 'POST',
1572
+ headers: {
1573
+ 'accept': 'application/octet-stream',
1574
+ 'cache-control': 'no-cache',
1575
+ 'user-agent': 'Transport',
1576
+ 'content-type': 'application/octet-stream',
1577
+ 'content-length': Buffer.byteLength(requestBody)
1578
+ }
1579
+ };
1580
+
1581
+ // Set up the request
1582
+ var responseAccumulator = '';
1583
+ var req = http.request(args.time, options, function (res) {
1584
+ res.setEncoding('utf8');
1585
+ res.on('data', function (chunk) { responseAccumulator += chunk; });
1586
+ res.on('end', function () {
1587
+ // Decode the timestamp signature block
1588
+ const timepkcs7der = forge.asn1.fromDer(forge.util.createBuffer(Buffer.from(responseAccumulator, 'base64').toString('binary')));
1589
+
1590
+ // Get the ASN1 certificates used to sign the timestamp and add them to the certs in the PKCS7 of the executable
1591
+ // TODO: We could look to see if the certificate is already present in the executable
1592
+ const timeasn1Certs = timepkcs7der.value[1].value[0].value[3].value;
1593
+ for (var i in timeasn1Certs) { pkcs7der.value[1].value[0].value[3].value.push(timeasn1Certs[i]); }
1594
+
1595
+ // Get the time signature and add it to the executables PKCS7
1596
+ const timeasn1Signature = timepkcs7der.value[1].value[0].value[4];
1597
+ const countersignatureOid = asn1.oidToDer('1.2.840.113549.1.9.6').data;
1598
+ const asn1obj2 =
1599
+ asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
1600
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
1601
+ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, countersignatureOid),
1602
+ timeasn1Signature
1603
+ ])
1604
+ ]);
1605
+ pkcs7der.value[1].value[0].value[4].value[0].value.push(asn1obj2);
1606
+
1607
+ // Re-encode the executable signature block
1608
+ const p7signature = Buffer.from(forge.asn1.toDer(pkcs7der).data, 'binary');
1609
+
1610
+ // Write the file with the signature block
1611
+ writeExecutableEx(output, p7signature, written, func);
1612
+ });
1613
+ });
1614
+
1615
+ // Post the data
1616
+ req.write(requestBody);
1617
+ req.end();
1618
+ }
1619
+ return;
1620
}
1621
1622
// Close the file
1623
fs.closeSync(output);
1624
+
1625
+ // Indicate success
1626
+ func(null);
1627
+ }
1628
+
1629
+ function writeExecutableEx(output, p7signature, written, func) {
1630
+ // Quad Align the results, adding padding if necessary
1631
+ var len = written + p7signature.length;
1632
+ var padding = (8 - ((len) % 8)) % 8;
1633
+
1634
+ // Write the signature block header and signature
1635
+ var win = Buffer.alloc(8); // WIN CERTIFICATE Structure
1636
+ win.writeUInt32LE(p7signature.length + padding + 8); // DWORD length
1637
+ win.writeUInt16LE(512, 4); // WORD revision
1638
+ win.writeUInt16LE(2, 6); // WORD type
1639
+ fs.writeSync(output, win);
1640
+ fs.writeSync(output, p7signature);
1641
+ if (padding > 0) { fs.writeSync(output, Buffer.alloc(padding, 0)); }
1642
+
1643
+ // Write the signature header
1644
+ var addresstable = Buffer.alloc(8);
1645
+ addresstable.writeUInt32LE(written);
1646
+ addresstable.writeUInt32LE(8 + p7signature.length + padding, 4);
1647
+ var signatureHeaderLocation = (obj.header.peHeaderLocation + 152 + (obj.header.pe32plus * 16));
1648
+ fs.writeSync(output, addresstable, 0, 8, signatureHeaderLocation);
1649
+ written += (p7signature.length + padding + 8); // Add the signature block to written counter
1650
+
1651
+ // Compute the checksum and write it in the PE header checksum location
1652
+ var tmp = Buffer.alloc(4);
1653
+ tmp.writeUInt32LE(runChecksumOnFile(output, written, ((obj.header.peOptionalHeaderLocation + 64) / 4)));
1654
+ fs.writeSync(output, tmp, 0, 4, obj.header.peOptionalHeaderLocation + 64);
1655
+
1656
+ // Close the file
1657
+ fs.closeSync(output);
1658
+
1659
+ // Indicate success
1660
+ func(null);
1661
}
1662
1663
// Return null if we could not open the file
@@ -1430,6 +1684,7 @@ function start() {
1684
console.log(" --desc [description] Description string to embbed into signature.");
1685
console.log(" --url [url] URL to embbed into signature.");
1686
console.log(" --hash [method] Default is SHA384, possible value: MD5, SHA224, SHA256, SHA384 or SHA512.");
1687
+ console.log(" --time [url] The time signing server URL.");
1688
console.log(" unsign: Remove the signature from the executable.");
1689
console.log(" --exe [file] Required executable to un-sign.");
1690
console.log(" --out [file] Resulting executable with signature removed.");
@@ -1442,6 +1697,10 @@ function start() {
1697
console.log(" --org [value] Certificate organization name.");
1698
console.log(" --ou [value] Certificate organization unit name.");
1699
console.log(" --serial [value] Certificate serial number.");
1700
+ console.log(" timestamp: Add a signed timestamp to an already signed executable.");
1701
+ console.log(" --exe [file] Required executable to sign.");
1702
+ console.log(" --out [file] Resulting signed executable.");
1703
+ console.log(" --time [url] The time signing server URL.");
1704
console.log("");
1705
console.log("Note that certificate PEM files must first have the signing certificate,");
1706
console.log("followed by all certificates that form the trust chain.");
@@ -1532,10 +1791,18 @@ function start() {
1791
if (cert == null) { console.log("Unable to load certificate and/or private key, generating test certificate."); cert = createSelfSignedCert({ cn: 'Test' }); }
1792
if (resChanges == false) {
1793
console.log("Signing to " + args.out);
1535
- exe.sign(cert, args); // Simple signing, copy most of the original file.
1794
+ exe.sign(cert, args, function (err) { // Simple signing, copy most of the original file.
1795
+ if (err == null) { console.log("Done."); } else { console.log(err); }
1796
+ if (exe != null) { exe.close(); }
1797
+ });
1798
+ return;
1799
} else {
1800
console.log("Changing resources and signing to " + args.out);
1538
- exe.writeExecutable(args, cert); // Signing with resources decoded and re-encoded.
1801
+ exe.writeExecutable(args, cert, function (err) { // Signing with resources decoded and re-encoded.
1802
+ if (err == null) { console.log("Done."); } else { console.log(err); }
1803
+ if (exe != null) { exe.close(); }
1804
+ });
1805
+ return;
1806
}
1807
console.log("Done.");
1808
}
@@ -1612,9 +1879,14 @@ function start() {
1879
if (command == 'timestamp') {
1880
if (exe == null) { console.log("Missing --exe [filename]"); return; }
1881
if (exe.signature == null) { console.log("Executable is not signed."); return; }
1615
- if (typeof args.url != 'string') { console.log("Missing --url [url]"); return; }
1882
+ if (typeof args.time != 'string') { console.log("Missing --time [url]"); return; }
1883
+ createOutFile(args, args.exe);
1884
console.log("Requesting time signature...");
1617
- exe.timeStampRequest(args.url, function (err, response) { console.log("Done: RSP: " + response); })
1885
+ exe.timeStampRequest(args, function (err) {
1886
+ if (err == null) { console.log("Done."); } else { console.log(err); }
1887
+ if (exe != null) { exe.close(); }
1888
+ })
1889
+ return;
1890
}
1891
1892
// Close the file