diff --git a/README.md b/README.md index a245e42..930ff94 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,27 @@ Example commandline for privatbank keys: --no-tax \ --tsp all +## Certificate discovery + +For key-6.dat files you also have an option to let agent download certificates from CA server. JKS files don't need this, as certificates are part of jks file itself: + + node index.js --sign \ + --key Key-6.dat:password \ + --cert-fetch http://acskidd.gov.ua/services/cmp/ \ + --cert-fetch http://czo.gov.ua/services/cmp/ \ + --input text.pdf --output text.pdf.p7s \ + --no-tax \ + --tsp all + +Notice, that cert-fetch can be passed more then once, in this case all mentioned URLs will be called in parallel. If you don't know the url of CMP service, you can let jkurwa guess cmp server urls from CA bundle. Please note, that all urls are being queried sequentially in order they are found in CA bundle and loading CA bundle takes some CPU time. Example: + + node index.js --sign \ + --key Key-6.dat:password \ + --cert-fetch \ + --ca_path CACertificates.3322cbdc.p7b \ + --input text.pdf --output text.pdf.p7s \ + --no-tax \ + --tsp all ## Write detached signature diff --git a/agent.js b/agent.js index bc2f7df..5cf8b90 100644 --- a/agent.js +++ b/agent.js @@ -145,6 +145,10 @@ async function do_sc( cert_rcrypt = Certificate.from_asn1(buf).as_pem(); shouldCrypt = true; } + if (!box.keys[0].cert) { + error('No certificate loaded for key 0, use --cert filename or --cert-fetch url'); + return; + } const ipn_ext = box.keys[0].cert.extension.ipn; const subject = box.keys[0].cert.subject; @@ -292,7 +296,7 @@ async function do_parse(inputF, outputF, box, tsp, ocsp) { } }); - if (isErr === false) { + if (isErr === false && outputF !== null) { await output(outputF, textinfo.content, isWin); } @@ -328,6 +332,17 @@ async function main(argv, setIo) { box = await get_local_box(argv.key, argv.cert, argv.ca_path); } + let certFetch = argv['cert-fetch']; + if (certFetch) { + let urls = []; + if (typeof certFetch === 'string') { + urls = [certFetch]; + } else if (Array.isArray(certFetch)) { + urls = certFetch; + } + await box.findCertsCmp(urls); + } + if (argv.sign || argv.crypt) { if (argv.crypt === true && !argv.recipient_cert) { return error( @@ -356,10 +371,10 @@ async function main(argv, setIo) { ); } - if (argv.decrypt) { + if (argv.decrypt || argv.verify) { ret = await do_parse( - argv.input, - argv.output, + argv.input || argv.decrypt || argv.verify, + argv.verify ? null : argv.output, box, tsp_arg(argv.tsp), argv.ocsp diff --git a/lib/frame/client.js b/lib/frame/client.js index aed988a..17ead91 100644 --- a/lib/frame/client.js +++ b/lib/frame/client.js @@ -26,7 +26,7 @@ var connect = function (opts) { }; var RemoteBox = function (cb) { - this.readyCb = cb; + this.readyCB = cb; this.sock = connect({ connected: this.haveLink.bind(this), data: this.haveData.bind(this), @@ -46,7 +46,7 @@ RemoteBox.prototype.haveData = function(contents, type) { }); } if (type === 'printstr' && contents.op === 'READY') { - this.readyCb(this); + this.readyCB(this); } if (type === 'printstr' && contents.op === 'META') { data = this._data; @@ -58,11 +58,16 @@ RemoteBox.prototype.haveData = function(contents, type) { delete this._data; this.rpipeCB(data); } + if (type === 'printstr' && contents.op === 'RCMP') { + this.frame.send({op: 'INFO'}); + } if (type === 'printstr' && contents.op === 'ERROR') { if(contents.code === 'EPIPE') { this.rpipeCB({error: true}); } else if (contents.code === 'EUNWRAP') { this.unwrapCB({error: true}); + } else if (contents.code === 'ECMP') { + this.cmpCB({error: true}); } } @@ -89,6 +94,14 @@ RemoteBox.prototype.unwrap = function(content, content2, opts) { }); }; +RemoteBox.prototype.findCertsCmp = function(urls) { + return new Promise(resolve=> { + this.frame.send({ op: 'CMP', urls }); + this.readyCB = resolve; + this.cmpCB = resolve; + }); +} + var remoteBox = function(cb) { var box = new RemoteBox(cb); }; diff --git a/lib/frame/daemon.js b/lib/frame/daemon.js index 8d155a7..d260a8f 100644 --- a/lib/frame/daemon.js +++ b/lib/frame/daemon.js @@ -163,6 +163,12 @@ var start = function (opts) { }; }); frame.send({ op: "CLEAR", keys }); + } else if (contents.op === 'CMP') { + box.findCertsCmp(contents.urls).then((number)=> { + frame.send({ op: 'RCMP', number }); + }, ()=> { + frame.send({ op: 'ERROR', code: 'ECMP'}); + }); } else if (!box) { frame.send({ op: "ERROR", code: "ENOENT", bid: contents.bid }); } else { diff --git a/package-lock.json b/package-lock.json index 8ab7b4b..0d7fea2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1944,9 +1944,9 @@ }, "dependencies": { "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" } } }, @@ -2227,9 +2227,9 @@ } }, "bn.js": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", - "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==" + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" }, "brace-expansion": { "version": "1.1.11", @@ -6125,9 +6125,9 @@ } }, "jkurwa": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/jkurwa/-/jkurwa-1.12.0.tgz", - "integrity": "sha512-0EZGKiB2992imXkWOWFFDeRsCNIO07v0GBzNm6gtDswZZAnC1m+SIyMX9Hm186grtFjZgI9kmDqjN/xYdpmZPQ==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/jkurwa/-/jkurwa-1.14.0.tgz", + "integrity": "sha512-vUrj+xse/VFu4EqKvVKVgzOnX7UanDhYOq5UaI8MHJlQizAEzd/pGkzn4Q50Hq+lr7joVWX32RCEoyRnpQabrg==", "requires": { "asn1.js": ">= 0.4.0", "bn.js": ">= 0.14", @@ -6493,10 +6493,37 @@ "dev": true }, "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } }, "node-int64": { "version": "0.4.0", diff --git a/package.json b/package.json index e0321ed..6b5d21b 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "encoding": "^0.1.12", "gost89": "^0.1.11", "jksreader": "^1.0.0", - "jkurwa": "^1.12.0", + "jkurwa": "^1.14.0", "yargs": "1.3.x" } }