Skip to content

Commit 8a7bbb3

Browse files
committed
Add base Signer and ExternalSigner implementations using PKI.js
1 parent d07f655 commit 8a7bbb3

39 files changed

Lines changed: 1303 additions & 49 deletions

packages/signer-p12/dist/P12Signer.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* @prop {string} [passphrase]
44
* @prop {boolean} [asn1StrictParsing]
55
*/
6-
export class P12Signer extends Signer {
6+
export class P12Signer extends ISigner {
77
/**
88
* @param {Buffer | Uint8Array | string} p12Buffer
99
* @param {SignerOptions} additionalOptions
@@ -19,5 +19,5 @@ export type SignerOptions = {
1919
passphrase?: string;
2020
asn1StrictParsing?: boolean;
2121
};
22-
import { Signer } from '@signpdf/utils';
22+
import { ISigner } from '@signpdf/utils';
2323
//# sourceMappingURL=P12Signer.d.ts.map

packages/signer-p12/dist/P12Signer.d.ts.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/signer-p12/dist/P12Signer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
1313
* @prop {boolean} [asn1StrictParsing]
1414
*/
1515

16-
class P12Signer extends _utils.Signer {
16+
class P12Signer extends _utils.ISigner {
1717
/**
1818
* @param {Buffer | Uint8Array | string} p12Buffer
1919
* @param {SignerOptions} additionalOptions

packages/signer-p12/src/P12Signer.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import forge from 'node-forge';
2-
import {convertBuffer, SignPdfError, Signer} from '@signpdf/utils';
2+
import {convertBuffer, SignPdfError, ISigner} from '@signpdf/utils';
33

44
/**
55
* @typedef {object} SignerOptions
66
* @prop {string} [passphrase]
77
* @prop {boolean} [asn1StrictParsing]
88
*/
99

10-
export class P12Signer extends Signer {
10+
export class P12Signer extends ISigner {
1111
/**
1212
* @param {Buffer | Uint8Array | string} p12Buffer
1313
* @param {SignerOptions} additionalOptions

packages/signer/.babelrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "../../babel.config.json"
3+
}

packages/signer/.eslintrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"extends": [
3+
"@signpdf/eslint-config"
4+
]
5+
}

packages/signer/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Signer base implementation with PKI.js
2+
3+
for [![@signpdf](https://raw.githubusercontent.com/vbuch/node-signpdf/master/resources/logo-horizontal.svg?sanitize=true)](https://github.com/vbuch/node-signpdf/)
4+
5+
[![npm version](https://badge.fury.io/js/@signpdf%2Fsigner.svg)](https://badge.fury.io/js/@signpdf%2Fsigner)
6+
[![Donate to this project using Buy Me A Coffee](https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg)](https://buymeacoffee.com/vbuch)
7+
8+
Uses `PKI.js` to create a detached signature of a given `Buffer`.
9+
10+
## Usage
11+
12+
This is an abstract base implementation of the `Signer` and `ExternalSigner` classes. Use any of the available implementations (or subclass any of these classes yourself) to sign an actual PDF file. See for example the [P12Signer package](/packages/signer-p12) for signing with a PKCS#12 certificate bundle.
13+
14+
## Notes
15+
16+
* Make sure to have a look at the docs of the [@signpdf family of packages](https://github.com/vbuch/node-signpdf/).
17+
* Feel free to copy and paste any part of this code. See its defined [Purpose](https://github.com/vbuch/node-signpdf#purpose).
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Abstract ExternalSigner class taking care of creating a suitable signature for a given pdf
3+
* using an external signature provider.
4+
* Subclasses should specify the required signature and hashing algorithms used by the external
5+
* provider (either through the `signAlgorithm` and `hashAlgorithm` attributes, or by overriding
6+
* the `getSignAlgorithm` and `getHashAlgorithm` methods), as well as provide the used signing
7+
* certificate and final signature (by implementing the `getCertificate` and `getSignature`
8+
* methods).
9+
*/
10+
export class ExternalSigner extends Signer {
11+
/**
12+
* Method to retrieve the signature of the given hash (of the given data) from the external
13+
* service. The original data is included in case the external signature provider computes
14+
* the hash automatically before signing.
15+
* To be implemented by subclasses.
16+
* @param {Uint8Array} hash
17+
* @param {Uint8Array} data
18+
* @returns {Promise<Uint8Array>}
19+
*/
20+
getSignature(hash: Uint8Array, data: Uint8Array): Promise<Uint8Array>;
21+
}
22+
import { Signer } from './Signer';
23+
//# sourceMappingURL=ExternalSigner.d.ts.map

packages/signer/dist/ExternalSigner.d.ts.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"use strict";
2+
3+
Object.defineProperty(exports, "__esModule", {
4+
value: true
5+
});
6+
exports.ExternalSigner = void 0;
7+
var pkijs = _interopRequireWildcard(require("pkijs"));
8+
var _utils = require("@signpdf/utils");
9+
var _Signer = require("./Signer");
10+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
11+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
12+
/* eslint-disable no-unused-vars */
13+
14+
/**
15+
* Abstract ExternalSigner class taking care of creating a suitable signature for a given pdf
16+
* using an external signature provider.
17+
* Subclasses should specify the required signature and hashing algorithms used by the external
18+
* provider (either through the `signAlgorithm` and `hashAlgorithm` attributes, or by overriding
19+
* the `getSignAlgorithm` and `getHashAlgorithm` methods), as well as provide the used signing
20+
* certificate and final signature (by implementing the `getCertificate` and `getSignature`
21+
* methods).
22+
*/
23+
class ExternalSigner extends _Signer.Signer {
24+
/**
25+
* Method to retrieve the signature of the given hash (of the given data) from the external
26+
* service. The original data is included in case the external signature provider computes
27+
* the hash automatically before signing.
28+
* To be implemented by subclasses.
29+
* @param {Uint8Array} hash
30+
* @param {Uint8Array} data
31+
* @returns {Promise<Uint8Array>}
32+
*/
33+
async getSignature(hash, data) {
34+
throw new _utils.SignPdfError(`getSignature() is not implemented on ${this.constructor.name}`, _utils.SignPdfError.TYPE_INPUT);
35+
}
36+
37+
/**
38+
* Get a "crypto" extension and override the function used by SignedData.sign to support
39+
* external signing.
40+
* @returns {pkijs.ICryptoEngine}
41+
*/
42+
getCrypto() {
43+
const crypto = super.getCrypto();
44+
crypto.sign = async (_algo, _key, data) => {
45+
// Calculate hash
46+
const hash = await crypto.digest({
47+
name: this.hashAlgorithm
48+
}, data);
49+
// And pass it to the external signature provider
50+
const signature = await this.getSignature(Buffer.from(hash), Buffer.from(data));
51+
return signature;
52+
};
53+
return crypto;
54+
}
55+
56+
/**
57+
* Obtain a dummy private key to pass the correct signing parameters to the sign function.
58+
* @returns {CryptoKey}
59+
*/
60+
async obtainKey() {
61+
// The algorithm parameters cannot be passed directly to the SignedData.sign function, so we
62+
// need to generate a dummy private key with the required parameters and pass that to the
63+
// sign function. The private key is not actually used for signing, as we override the
64+
// crypto.sign function in the getCrypto method.
65+
const algorithmParams = this.crypto.getAlgorithmParameters(this.signAlgorithm, 'generatekey').algorithm;
66+
const keypair = await this.crypto.generateKey({
67+
name: this.signAlgorithm,
68+
...algorithmParams,
69+
hash: {
70+
name: this.hashAlgorithm
71+
}
72+
}, false, ['sign', 'verify']);
73+
return keypair.privateKey;
74+
}
75+
}
76+
exports.ExternalSigner = ExternalSigner;

0 commit comments

Comments
 (0)