-
-
Notifications
You must be signed in to change notification settings - Fork 178
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add base Signer and ExternalSigner implementations using PKI.js
- Loading branch information
Showing
39 changed files
with
1,303 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": "../../babel.config.json" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"extends": [ | ||
"@signpdf/eslint-config" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Signer base implementation with PKI.js | ||
|
||
for [![@signpdf](https://raw.githubusercontent.com/vbuch/node-signpdf/master/resources/logo-horizontal.svg?sanitize=true)](https://github.com/vbuch/node-signpdf/) | ||
|
||
[![npm version](https://badge.fury.io/js/@signpdf%2Fsigner.svg)](https://badge.fury.io/js/@signpdf%2Fsigner) | ||
[![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) | ||
|
||
Uses `PKI.js` to create a detached signature of a given `Buffer`. | ||
|
||
## Usage | ||
|
||
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. | ||
|
||
## Notes | ||
|
||
* Make sure to have a look at the docs of the [@signpdf family of packages](https://github.com/vbuch/node-signpdf/). | ||
* Feel free to copy and paste any part of this code. See its defined [Purpose](https://github.com/vbuch/node-signpdf#purpose). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/** | ||
* Abstract ExternalSigner class taking care of creating a suitable signature for a given pdf | ||
* using an external signature provider. | ||
* Subclasses should specify the required signature and hashing algorithms used by the external | ||
* provider (either through the `signAlgorithm` and `hashAlgorithm` attributes, or by overriding | ||
* the `getSignAlgorithm` and `getHashAlgorithm` methods), as well as provide the used signing | ||
* certificate and final signature (by implementing the `getCertificate` and `getSignature` | ||
* methods). | ||
*/ | ||
export class ExternalSigner extends Signer { | ||
/** | ||
* Method to retrieve the signature of the given hash (of the given data) from the external | ||
* service. The original data is included in case the external signature provider computes | ||
* the hash automatically before signing. | ||
* To be implemented by subclasses. | ||
* @param {Uint8Array} hash | ||
* @param {Uint8Array} data | ||
* @returns {Promise<Uint8Array>} | ||
*/ | ||
getSignature(hash: Uint8Array, data: Uint8Array): Promise<Uint8Array>; | ||
} | ||
import { Signer } from './Signer'; | ||
//# sourceMappingURL=ExternalSigner.d.ts.map |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
"use strict"; | ||
|
||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.ExternalSigner = void 0; | ||
var pkijs = _interopRequireWildcard(require("pkijs")); | ||
var _utils = require("@signpdf/utils"); | ||
var _Signer = require("./Signer"); | ||
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); } | ||
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; } | ||
/* eslint-disable no-unused-vars */ | ||
|
||
/** | ||
* Abstract ExternalSigner class taking care of creating a suitable signature for a given pdf | ||
* using an external signature provider. | ||
* Subclasses should specify the required signature and hashing algorithms used by the external | ||
* provider (either through the `signAlgorithm` and `hashAlgorithm` attributes, or by overriding | ||
* the `getSignAlgorithm` and `getHashAlgorithm` methods), as well as provide the used signing | ||
* certificate and final signature (by implementing the `getCertificate` and `getSignature` | ||
* methods). | ||
*/ | ||
class ExternalSigner extends _Signer.Signer { | ||
/** | ||
* Method to retrieve the signature of the given hash (of the given data) from the external | ||
* service. The original data is included in case the external signature provider computes | ||
* the hash automatically before signing. | ||
* To be implemented by subclasses. | ||
* @param {Uint8Array} hash | ||
* @param {Uint8Array} data | ||
* @returns {Promise<Uint8Array>} | ||
*/ | ||
async getSignature(hash, data) { | ||
throw new _utils.SignPdfError(`getSignature() is not implemented on ${this.constructor.name}`, _utils.SignPdfError.TYPE_INPUT); | ||
} | ||
|
||
/** | ||
* Get a "crypto" extension and override the function used by SignedData.sign to support | ||
* external signing. | ||
* @returns {pkijs.ICryptoEngine} | ||
*/ | ||
getCrypto() { | ||
const crypto = super.getCrypto(); | ||
crypto.sign = async (_algo, _key, data) => { | ||
// Calculate hash | ||
const hash = await crypto.digest({ | ||
name: this.hashAlgorithm | ||
}, data); | ||
// And pass it to the external signature provider | ||
const signature = await this.getSignature(Buffer.from(hash), Buffer.from(data)); | ||
return signature; | ||
}; | ||
return crypto; | ||
} | ||
|
||
/** | ||
* Obtain a dummy private key to pass the correct signing parameters to the sign function. | ||
* @returns {CryptoKey} | ||
*/ | ||
async obtainKey() { | ||
// The algorithm parameters cannot be passed directly to the SignedData.sign function, so we | ||
// need to generate a dummy private key with the required parameters and pass that to the | ||
// sign function. The private key is not actually used for signing, as we override the | ||
// crypto.sign function in the getCrypto method. | ||
const algorithmParams = this.crypto.getAlgorithmParameters(this.signAlgorithm, 'generatekey').algorithm; | ||
const keypair = await this.crypto.generateKey({ | ||
name: this.signAlgorithm, | ||
...algorithmParams, | ||
hash: { | ||
name: this.hashAlgorithm | ||
} | ||
}, false, ['sign', 'verify']); | ||
return keypair.privateKey; | ||
} | ||
} | ||
exports.ExternalSigner = ExternalSigner; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/** | ||
* Abstract Signer class taking care of creating a suitable signature for a given pdf. | ||
* Subclasses should specify the required signature and hashing algorithms (either through | ||
* the `signAlgorithm` and `hashAlgorithm` attributes, or by overriding the `getSignAlgorithm` | ||
* and `getHashAlgorithm` methods), as well as provide the signing certificate and private key | ||
* used for signing (by implementing the `getCertificate` and `getKey` methods). | ||
*/ | ||
export class Signer extends ISigner { | ||
/** Signature algorithm used for PDF signing | ||
* @type {string} | ||
*/ | ||
signAlgorithm: string; | ||
/** Hash algorithm used for PDF signing | ||
* @type {string} | ||
*/ | ||
hashAlgorithm: string; | ||
/** | ||
* Method to retrieve the signature algorithm used for PDF signing. | ||
* To be implemented by subclasses or set in the `signAlgorithm` attribute. | ||
* @returns {Promise<string>} | ||
*/ | ||
getSignAlgorithm(): Promise<string>; | ||
/** | ||
* Method to retrieve the hashing algorithm used for PDF signing. | ||
* To be implemented by subclasses or set in the `hashAlgorithm` attribute. | ||
* @returns {Promise<string>} | ||
*/ | ||
getHashAlgorithm(): Promise<string>; | ||
/** | ||
* Method to retrieve the signing certificate. If multiple certificates are returned, the first | ||
* one is used for the actual signing, while the others are added for verification purposes. | ||
* To be implemented by subclasses. | ||
* @returns {Promise<Uint8Array | Uint8Array[]>} | ||
*/ | ||
getCertificate(): Promise<Uint8Array | Uint8Array[]>; | ||
/** | ||
* Method to retrieve the private key used for signing. | ||
* The returned private key should be in its PKCS#8 binary representation. | ||
* To be implemented by subclasses. | ||
* @returns {Promise<Uint8Array>} | ||
*/ | ||
getKey(): Promise<Uint8Array>; | ||
/** | ||
* Get a "crypto" extension. | ||
* @returns {pkijs.ICryptoEngine} | ||
*/ | ||
getCrypto(): pkijs.ICryptoEngine; | ||
/** | ||
* Obtain the certificates used for signing (first one) and verification (whole list). | ||
* @returns {pkijs.Certificate[]} | ||
*/ | ||
obtainCertificates(): pkijs.Certificate[]; | ||
/** | ||
* Obtain the private key used for signing. | ||
* @returns {CryptoKey} | ||
*/ | ||
obtainKey(): CryptoKey; | ||
/** | ||
* Obtain the signed attributes, which are the actual content that is signed in detached mode. | ||
* @returns {pkijs.Attribute[]} | ||
*/ | ||
obtainSignedAttributes(signingTime: any, data: any, signCert: any): pkijs.Attribute[]; | ||
/** | ||
* Obtain the unsigned attributes. | ||
* @returns {pkijs.Attribute[]} | ||
*/ | ||
obtainUnsignedAttributes(signature: any): pkijs.Attribute[]; | ||
crypto: pkijs.ICryptoEngine; | ||
/** | ||
* Verify whether the signature generated by the sign function is correct. | ||
* @param {Buffer} cmsSignedBuffer | ||
* @param {Buffer} pdfBuffer | ||
* @returns {boolean} | ||
*/ | ||
verify(cmsSignedBuffer: Buffer, pdfBuffer: Buffer): boolean; | ||
} | ||
import { ISigner } from '@signpdf/utils'; | ||
import * as pkijs from 'pkijs'; | ||
//# sourceMappingURL=Signer.d.ts.map |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.