Skip to content

Commit b3372df

Browse files
committed
Simplify P12Signer implementation
1 parent e9b7d74 commit b3372df

6 files changed

Lines changed: 110 additions & 158 deletions

File tree

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ export class P12Signer extends Signer {
1414
asn1StrictParsing: boolean;
1515
};
1616
cert: any;
17+
certBags: any;
18+
keyBags: any;
1719
/**
18-
* @param {Buffer} pdfBuffer
19-
* @param {Date | undefined} signingTime
20-
* @returns {Buffer}
20+
* Retrieve all certificates from the safe bag and determine the signing certificate.
21+
* @returns {Promise<Uint8Array[]>}
2122
*/
22-
sign(pdfBuffer: Buffer, signingTime?: Date | undefined): Buffer;
23+
getCertificate(): Promise<Uint8Array[]>;
2324
}
2425
export type SignerOptions = {
2526
passphrase?: string;

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: 38 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
1414
*/
1515

1616
class P12Signer extends _utils.Signer {
17+
hashAlgorithm = 'SHA-256';
18+
signAlgorithm = 'RSASSA-PKCS1-v1_5';
19+
1720
/**
1821
* @param {Buffer | Uint8Array | string} p12Buffer
1922
* @param {SignerOptions} additionalOptions
@@ -26,82 +29,64 @@ class P12Signer extends _utils.Signer {
2629
passphrase: '',
2730
...additionalOptions
2831
};
29-
this.cert = _nodeForge.default.util.createBuffer(buffer.toString('binary'));
30-
}
31-
32-
/**
33-
* @param {Buffer} pdfBuffer
34-
* @param {Date | undefined} signingTime
35-
* @returns {Buffer}
36-
*/
37-
async sign(pdfBuffer, signingTime = undefined) {
38-
if (!(pdfBuffer instanceof Buffer)) {
39-
throw new _utils.SignPdfError('PDF expected as Buffer.', _utils.SignPdfError.TYPE_INPUT);
40-
}
4132

4233
// Convert Buffer P12 to a forge implementation.
34+
this.cert = _nodeForge.default.util.createBuffer(buffer.toString('binary'));
4335
const p12Asn1 = _nodeForge.default.asn1.fromDer(this.cert);
4436
const p12 = _nodeForge.default.pkcs12.pkcs12FromAsn1(p12Asn1, this.options.asn1StrictParsing, this.options.passphrase);
4537

4638
// Extract safe bags by type.
4739
// We will need all the certificates and the private key.
48-
const certBags = p12.getBags({
40+
this.certBags = p12.getBags({
4941
bagType: _nodeForge.default.pki.oids.certBag
5042
})[_nodeForge.default.pki.oids.certBag];
51-
const keyBags = p12.getBags({
43+
this.keyBags = p12.getBags({
5244
bagType: _nodeForge.default.pki.oids.pkcs8ShroudedKeyBag
5345
})[_nodeForge.default.pki.oids.pkcs8ShroudedKeyBag];
54-
const privateKey = keyBags[0].key;
55-
// Here comes the actual PKCS#7 signing.
56-
const p7 = _nodeForge.default.pkcs7.createSignedData();
57-
// Start off by setting the content.
58-
p7.content = _nodeForge.default.util.createBuffer(pdfBuffer.toString('binary'));
46+
}
5947

60-
// Then add all the certificates (-cacerts & -clcerts)
61-
// Keep track of the last found client certificate.
48+
/**
49+
* Retrieve all certificates from the safe bag and determine the signing certificate.
50+
* @returns {Promise<Uint8Array[]>}
51+
*/
52+
async getCertificate() {
53+
const privateKey = this.keyBags[0].key;
54+
// Retrieve all the certificates (-cacerts & -clcerts)
55+
// Keep track of the last found client certificate matching the private key.
6256
// This will be the public key that will be bundled in the signature.
63-
let certificate;
64-
Object.keys(certBags).forEach(i => {
57+
let signCertFound = false;
58+
const certificates = [];
59+
Object.values(this.certBags).forEach(bag => {
6560
const {
6661
publicKey
67-
} = certBags[i].cert;
68-
p7.addCertificate(certBags[i].cert);
62+
} = bag.cert;
6963

7064
// Try to find the certificate that matches the private key.
7165
if (privateKey.n.compareTo(publicKey.n) === 0 && privateKey.e.compareTo(publicKey.e) === 0) {
72-
certificate = certBags[i].cert;
66+
// Insert signing certificate in front
67+
certificates.unshift(bag.cert);
68+
signCertFound = true;
69+
} else {
70+
// Insert all other certificates at the end
71+
certificates.push(bag.cert);
7372
}
7473
});
75-
if (typeof certificate === 'undefined') {
74+
if (!signCertFound) {
7675
throw new _utils.SignPdfError('Failed to find a certificate that matches the private key.', _utils.SignPdfError.TYPE_INPUT);
7776
}
77+
// Convert certificates to their binary representation
78+
return certificates.map(cert => Buffer.from(_nodeForge.default.asn1.toDer(_nodeForge.default.pki.certificateToAsn1(cert)).getBytes(), 'binary'));
79+
}
7880

79-
// Add a sha256 signer. That's what Adobe.PPKLite adbe.pkcs7.detached expects.
80-
// Note that the authenticatedAttributes order is relevant for correct
81-
// EU signature validation:
82-
// https://ec.europa.eu/digital-building-blocks/DSS/webapp-demo/validation
83-
p7.addSigner({
84-
key: privateKey,
85-
certificate,
86-
digestAlgorithm: _nodeForge.default.pki.oids.sha256,
87-
authenticatedAttributes: [{
88-
type: _nodeForge.default.pki.oids.contentType,
89-
value: _nodeForge.default.pki.oids.data
90-
}, {
91-
type: _nodeForge.default.pki.oids.signingTime,
92-
// value can also be auto-populated at signing time
93-
value: signingTime !== null && signingTime !== void 0 ? signingTime : new Date()
94-
}, {
95-
type: _nodeForge.default.pki.oids.messageDigest
96-
// value will be auto-populated at signing time
97-
}]
98-
});
99-
100-
// Sign in detached mode.
101-
p7.sign({
102-
detached: true
103-
});
104-
return Buffer.from(_nodeForge.default.asn1.toDer(p7.toAsn1()).getBytes(), 'binary');
81+
/**
82+
* Retrieve the private key from the safe bag.
83+
* @returns {Promise<Uint8Array>}
84+
*/
85+
async getKey() {
86+
const privateKey = this.keyBags[0].key;
87+
// Convert private key to its pkcs8 binary representation
88+
const pkcs8 = _nodeForge.default.pki.wrapRsaPrivateKey(_nodeForge.default.pki.privateKeyToAsn1(privateKey));
89+
return Buffer.from(_nodeForge.default.asn1.toDer(pkcs8).getBytes(), 'binary');
10590
}
10691
}
10792
exports.P12Signer = P12Signer;

packages/signer-p12/src/P12Signer.js

Lines changed: 39 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import {convertBuffer, SignPdfError, Signer} from '@signpdf/utils';
88
*/
99

1010
export class P12Signer extends Signer {
11+
hashAlgorithm = 'SHA-256';
12+
13+
signAlgorithm = 'RSASSA-PKCS1-v1_5';
14+
1115
/**
1216
* @param {Buffer | Uint8Array | string} p12Buffer
1317
* @param {SignerOptions} additionalOptions
@@ -22,23 +26,9 @@ export class P12Signer extends Signer {
2226
passphrase: '',
2327
...additionalOptions,
2428
};
25-
this.cert = forge.util.createBuffer(buffer.toString('binary'));
26-
}
27-
28-
/**
29-
* @param {Buffer} pdfBuffer
30-
* @param {Date | undefined} signingTime
31-
* @returns {Buffer}
32-
*/
33-
async sign(pdfBuffer, signingTime = undefined) {
34-
if (!(pdfBuffer instanceof Buffer)) {
35-
throw new SignPdfError(
36-
'PDF expected as Buffer.',
37-
SignPdfError.TYPE_INPUT,
38-
);
39-
}
4029

4130
// Convert Buffer P12 to a forge implementation.
31+
this.cert = forge.util.createBuffer(buffer.toString('binary'));
4232
const p12Asn1 = forge.asn1.fromDer(this.cert);
4333
const p12 = forge.pkcs12.pkcs12FromAsn1(
4434
p12Asn1,
@@ -48,69 +38,59 @@ export class P12Signer extends Signer {
4838

4939
// Extract safe bags by type.
5040
// We will need all the certificates and the private key.
51-
const certBags = p12.getBags({
41+
this.certBags = p12.getBags({
5242
bagType: forge.pki.oids.certBag,
5343
})[forge.pki.oids.certBag];
54-
const keyBags = p12.getBags({
44+
this.keyBags = p12.getBags({
5545
bagType: forge.pki.oids.pkcs8ShroudedKeyBag,
5646
})[forge.pki.oids.pkcs8ShroudedKeyBag];
47+
}
5748

58-
const privateKey = keyBags[0].key;
59-
// Here comes the actual PKCS#7 signing.
60-
const p7 = forge.pkcs7.createSignedData();
61-
// Start off by setting the content.
62-
p7.content = forge.util.createBuffer(pdfBuffer.toString('binary'));
63-
64-
// Then add all the certificates (-cacerts & -clcerts)
65-
// Keep track of the last found client certificate.
49+
/**
50+
* Retrieve all certificates from the safe bag and determine the signing certificate.
51+
* @returns {Promise<Uint8Array[]>}
52+
*/
53+
async getCertificate() {
54+
const privateKey = this.keyBags[0].key;
55+
// Retrieve all the certificates (-cacerts & -clcerts)
56+
// Keep track of the last found client certificate matching the private key.
6657
// This will be the public key that will be bundled in the signature.
67-
let certificate;
68-
Object.keys(certBags).forEach((i) => {
69-
const {publicKey} = certBags[i].cert;
70-
71-
p7.addCertificate(certBags[i].cert);
58+
let signCertFound = false;
59+
const certificates = [];
60+
Object.values(this.certBags).forEach((bag) => {
61+
const {publicKey} = bag.cert;
7262

7363
// Try to find the certificate that matches the private key.
7464
if (privateKey.n.compareTo(publicKey.n) === 0
7565
&& privateKey.e.compareTo(publicKey.e) === 0
7666
) {
77-
certificate = certBags[i].cert;
67+
// Insert signing certificate in front
68+
certificates.unshift(bag.cert);
69+
signCertFound = true;
70+
} else {
71+
// Insert all other certificates at the end
72+
certificates.push(bag.cert);
7873
}
7974
});
8075

81-
if (typeof certificate === 'undefined') {
76+
if (!signCertFound) {
8277
throw new SignPdfError(
8378
'Failed to find a certificate that matches the private key.',
8479
SignPdfError.TYPE_INPUT,
8580
);
8681
}
82+
// Convert certificates to their binary representation
83+
return certificates.map((cert) => Buffer.from(forge.asn1.toDer(forge.pki.certificateToAsn1(cert)).getBytes(), 'binary'));
84+
}
8785

88-
// Add a sha256 signer. That's what Adobe.PPKLite adbe.pkcs7.detached expects.
89-
// Note that the authenticatedAttributes order is relevant for correct
90-
// EU signature validation:
91-
// https://ec.europa.eu/digital-building-blocks/DSS/webapp-demo/validation
92-
p7.addSigner({
93-
key: privateKey,
94-
certificate,
95-
digestAlgorithm: forge.pki.oids.sha256,
96-
authenticatedAttributes: [
97-
{
98-
type: forge.pki.oids.contentType,
99-
value: forge.pki.oids.data,
100-
}, {
101-
type: forge.pki.oids.signingTime,
102-
// value can also be auto-populated at signing time
103-
value: signingTime ?? new Date(),
104-
}, {
105-
type: forge.pki.oids.messageDigest,
106-
// value will be auto-populated at signing time
107-
},
108-
],
109-
});
110-
111-
// Sign in detached mode.
112-
p7.sign({detached: true});
113-
114-
return Buffer.from(forge.asn1.toDer(p7.toAsn1()).getBytes(), 'binary');
86+
/**
87+
* Retrieve the private key from the safe bag.
88+
* @returns {Promise<Uint8Array>}
89+
*/
90+
async getKey() {
91+
const privateKey = this.keyBags[0].key;
92+
// Convert private key to its pkcs8 binary representation
93+
const pkcs8 = forge.pki.wrapRsaPrivateKey(forge.pki.privateKeyToAsn1(privateKey));
94+
return Buffer.from(forge.asn1.toDer(pkcs8).getBytes(), 'binary');
11595
}
11696
}

0 commit comments

Comments
 (0)