Skip to content

Commit e9b7d74

Browse files
committed
Implement default sign method for Signer and add ExternalSigner using PKI.js
1 parent 474152b commit e9b7d74

15 files changed

Lines changed: 932 additions & 10 deletions
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/utils/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 _SignPdfError = require("./SignPdfError");
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 _SignPdfError.SignPdfError(`getSignature() is not implemented on ${this.constructor.name}`, _SignPdfError.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;

packages/utils/dist/Signer.d.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,74 @@
1+
/**
2+
* Abstract Signer class taking care of creating a suitable signature for a given pdf.
3+
* Subclasses should specify the required signature and hashing algorithms (either through
4+
* the `signAlgorithm` and `hashAlgorithm` attributes, or by overriding the `getSignAlgorithm`
5+
* and `getHashAlgorithm` methods), as well as provide the signing certificate and private key
6+
* used for signing (by implementing the `getCertificate` and `getKey` methods).
7+
*/
18
export class Signer {
9+
/** Signature algorithm used for PDF signing
10+
* @type {string}
11+
*/
12+
signAlgorithm: string;
13+
/** Hash algorithm used for PDF signing
14+
* @type {string}
15+
*/
16+
hashAlgorithm: string;
17+
/**
18+
* Method to retrieve the signature algorithm used for PDF signing.
19+
* To be implemented by subclasses or set in the `signAlgorithm` attribute.
20+
* @returns {Promise<string>}
21+
*/
22+
getSignAlgorithm(): Promise<string>;
23+
/**
24+
* Method to retrieve the hashing algorithm used for PDF signing.
25+
* To be implemented by subclasses or set in the `hashAlgorithm` attribute.
26+
* @returns {Promise<string>}
27+
*/
28+
getHashAlgorithm(): Promise<string>;
29+
/**
30+
* Method to retrieve the signing certificate. If multiple certificates are returned, the first
31+
* one is used for the actual signing, while the others are added for verification purposes.
32+
* To be implemented by subclasses.
33+
* @returns {Promise<Uint8Array | Uint8Array[]>}
34+
*/
35+
getCertificate(): Promise<Uint8Array | Uint8Array[]>;
36+
/**
37+
* Method to retrieve the private key used for signing.
38+
* The returned private key should be in its PKCS#8 binary representation.
39+
* To be implemented by subclasses.
40+
* @returns {Promise<Uint8Array>}
41+
*/
42+
getKey(): Promise<Uint8Array>;
43+
/**
44+
* Get a "crypto" extension.
45+
* @returns {pkijs.ICryptoEngine}
46+
*/
47+
getCrypto(): pkijs.ICryptoEngine;
48+
/**
49+
* Obtain the certificates used for signing (first one) and verification (whole list).
50+
* @returns {pkijs.Certificate[]}
51+
*/
52+
obtainCertificates(): pkijs.Certificate[];
53+
/**
54+
* Obtain the private key used for signing.
55+
* @returns {CryptoKey}
56+
*/
57+
obtainKey(): CryptoKey;
258
/**
359
* @param {Buffer} pdfBuffer
460
* @param {Date | undefined} signingTime
561
* @returns {Promise<Buffer>}
662
*/
763
sign(pdfBuffer: Buffer, signingTime?: Date | undefined): Promise<Buffer>;
64+
crypto: pkijs.ICryptoEngine;
65+
/**
66+
* Verify whether the signature generated by the sign function is correct.
67+
* @param {Buffer} cmsSignedBuffer
68+
* @param {Buffer} pdfBuffer
69+
* @returns {boolean}
70+
*/
71+
verify(cmsSignedBuffer: Buffer, pdfBuffer: Buffer): boolean;
872
}
73+
import * as pkijs from 'pkijs';
974
//# sourceMappingURL=Signer.d.ts.map

packages/utils/dist/Signer.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.

0 commit comments

Comments
 (0)