11import forge from 'node-forge' ;
2- import { convertBuffer , SignPdfError , Signer } from '@signpdf/utils' ;
2+ import { convertBuffer , SignPdfError } from '@signpdf/utils' ;
3+ import { Signer } from '@signpdf/signer' ;
34
45/**
56 * @typedef {object } SignerOptions
@@ -8,6 +9,10 @@ import {convertBuffer, SignPdfError, Signer} from '@signpdf/utils';
89 */
910
1011export class P12Signer extends Signer {
12+ hashAlgorithm = 'SHA-256' ;
13+
14+ signAlgorithm = 'RSASSA-PKCS1-v1_5' ;
15+
1116 /**
1217 * @param {Buffer | Uint8Array | string } p12Buffer
1318 * @param {SignerOptions } additionalOptions
@@ -22,23 +27,9 @@ export class P12Signer extends Signer {
2227 passphrase : '' ,
2328 ...additionalOptions ,
2429 } ;
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- }
4030
4131 // Convert Buffer P12 to a forge implementation.
32+ this . cert = forge . util . createBuffer ( buffer . toString ( 'binary' ) ) ;
4233 const p12Asn1 = forge . asn1 . fromDer ( this . cert ) ;
4334 const p12 = forge . pkcs12 . pkcs12FromAsn1 (
4435 p12Asn1 ,
@@ -48,69 +39,59 @@ export class P12Signer extends Signer {
4839
4940 // Extract safe bags by type.
5041 // We will need all the certificates and the private key.
51- const certBags = p12 . getBags ( {
42+ this . certBags = p12 . getBags ( {
5243 bagType : forge . pki . oids . certBag ,
5344 } ) [ forge . pki . oids . certBag ] ;
54- const keyBags = p12 . getBags ( {
45+ this . keyBags = p12 . getBags ( {
5546 bagType : forge . pki . oids . pkcs8ShroudedKeyBag ,
5647 } ) [ forge . pki . oids . pkcs8ShroudedKeyBag ] ;
48+ }
5749
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.
50+ /**
51+ * Retrieve all certificates from the safe bag and determine the signing certificate .
52+ * @returns { Promise<Uint8Array[]> }
53+ */
54+ async getCertificate ( ) {
55+ const privateKey = this . keyBags [ 0 ] . key ;
56+ // Retrieve all the certificates (-cacerts & -clcerts)
57+ // Keep track of the last found client certificate matching the private key .
6658 // 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 ) ;
59+ let signCertFound = false ;
60+ const certificates = [ ] ;
61+ Object . values ( this . certBags ) . forEach ( ( bag ) => {
62+ const { publicKey} = bag . cert ;
7263
7364 // Try to find the certificate that matches the private key.
7465 if ( privateKey . n . compareTo ( publicKey . n ) === 0
7566 && privateKey . e . compareTo ( publicKey . e ) === 0
7667 ) {
77- certificate = certBags [ i ] . cert ;
68+ // Insert signing certificate in front
69+ certificates . unshift ( bag . cert ) ;
70+ signCertFound = true ;
71+ } else {
72+ // Insert all other certificates at the end
73+ certificates . push ( bag . cert ) ;
7874 }
7975 } ) ;
8076
81- if ( typeof certificate === 'undefined' ) {
77+ if ( ! signCertFound ) {
8278 throw new SignPdfError (
8379 'Failed to find a certificate that matches the private key.' ,
8480 SignPdfError . TYPE_INPUT ,
8581 ) ;
8682 }
83+ // Convert certificates to their binary representation
84+ return certificates . map ( ( cert ) => Buffer . from ( forge . asn1 . toDer ( forge . pki . certificateToAsn1 ( cert ) ) . getBytes ( ) , 'binary' ) ) ;
85+ }
8786
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' ) ;
87+ /**
88+ * Retrieve the private key from the safe bag.
89+ * @returns {Promise<Uint8Array> }
90+ */
91+ async getKey ( ) {
92+ const privateKey = this . keyBags [ 0 ] . key ;
93+ // Convert private key to its pkcs8 binary representation
94+ const pkcs8 = forge . pki . wrapRsaPrivateKey ( forge . pki . privateKeyToAsn1 ( privateKey ) ) ;
95+ return Buffer . from ( forge . asn1 . toDer ( pkcs8 ) . getBytes ( ) , 'binary' ) ;
11596 }
11697}
0 commit comments