You need to sign in or sign up before continuing.
Newer
Older
/*
* Based on PDFSign v1.0.0
* https://github.com/Communication-Systems-Group/pdfsign.js
*
* Copyright 2015, Thomas Bocek, University of Zurich
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*/
import {
ContentInfo,
SignedData,
Attribute,
SignerInfo,
IssuerAndSerialNumber,
SignedAndUnsignedAttributes,
EncapsulatedContentInfo,
getCrypto
} from './signingUtilities';
//keep this import to include the patched version of pdfjs library
import {PDFJS} from "../lib/pdfjs.parser.js";
function createXrefTable(xrefEntries) {
xrefEntries = sortOnKeys(xrefEntries);
var retVal ='xref\n';
var last = -2;
for(var i in xrefEntries) {
i = parseInt(i);
if(typeof xrefEntries[i].offset === 'undefined') { continue; }
retVal += calcFlow(i, last, xrefEntries);
var offset = xrefEntries[i].offset;
retVal += pad10(offset)+' '+pad5(xrefEntries[i].gen)+' '+(xrefEntries[i].free?'f':'n')+' \n';
last = i;
}
return retVal;
}
function calcFlow(i, last, xrefEntries) {
if(last + 1 === i) {return '';}
var count = 1;
while(typeof xrefEntries[(i+count)] !== 'undefined'
&& typeof xrefEntries[(i+count)].offset !== 'undefined') {count ++;}
return i + ' '+count+'\n';
}
function createTrailer(topDict, startxref, sha256Hex, size, prev) {
var retVal ='trailer <<\n';
retVal +=' /Size '+(size)+'\n';
var refRoot = topDict.getRaw('Root');
if(typeof refRoot !== 'undefined') {
retVal +=' /Root '+refRoot.num+' '+refRoot.gen+' R\n';
}
var refInfo = topDict.getRaw('Info');
if(typeof refInfo !== 'undefined') {
retVal +=' /Info '+refInfo.num+' '+refInfo.gen+' R\n';
}
retVal +=' /ID [<'+sha256Hex.substring(0,32)+'><'+sha256Hex.substring(32,64)+'>]\n';
if(typeof prev !== 'undefined' ) {
retVal +=' /Prev '+prev+'\n';
}
retVal +='>>\n';
retVal +='startxref\n';
retVal +=startxref + '\n';
retVal +='%%EOF\n';
return retVal;
}
function createXrefTableAppend(xrefEntries) {
xrefEntries = sortOnKeys(xrefEntries);
var retVal ='xref\n';
var last = -2;
for(var i in xrefEntries) {
i = parseInt(i);
if(typeof xrefEntries[i].offset === 'undefined') { continue; }
retVal += calcFlow(i, last, xrefEntries);
var offset = xrefEntries[i].offset;
retVal += pad10(offset)+' '+pad5(xrefEntries[i].gen)+' '+(xrefEntries[i].free?'f':'n')+' \n';
last = i;
}
return retVal;
}
//http://stackoverflow.com/questions/10946880/sort-a-dictionary-or-whatever-key-value-data-structure-in-js-on-word-number-ke
function sortOnKeys(dict) {
var sorted = [];
for(var key in dict) {
sorted[sorted.length] = key;
}
sorted.sort();
var tempDict = {};
for(var i = 0; i < sorted.length; i++) {
tempDict[sorted[i]] = dict[sorted[i]];
}
}
function removeFromArray(array, from, to) {
var cutlen = to - from;
var buf = new Uint8Array(array.length - cutlen);
for (var i = 0; i < from; i++) {
buf[i] = array[i];
}
for (var i = to, len = array.length; i < len; i++) {
buf[i-cutlen] = array[i];
}
return buf;
function findXrefBlocks(xrefBlocks) {
var num = xrefBlocks.length / 2;
var retVal = [];
for (var i=0;i<num;i++) {
retVal.push({start: xrefBlocks[i], end: xrefBlocks[i+num]});
}
return retVal;
}
function convertUint8ArrayToBinaryString(u8Array) {
var i, len = u8Array.length, b_str = "";
for (i=0; i<len; i++) {
b_str += String.fromCharCode(u8Array[i]);
}
return b_str;
}
function arrayObjectIndexOf(array, start, end, orig) {
for(var i = 0, len = array.length; i < len; i++) {
if ((array[i].start === start) && (array[i].end === end) && (array[i].orig === orig)) {
return i;
}
var s = "000000000" + num;
return s.substr(s.length-10);
var s = "0000" + num;
return s.substr(s.length-5);
var s = "0" + num;
return s.substr(s.length-2);
}
function findRootEntry(xref) {
var rootNr = xref.root.objId.substring(0, xref.root.objId.length - 1);
return xref.entries[rootNr];
function findSuccessorEntry(xrefEntries, current) {
//find it first
var currentOffset = current.offset;
var currentMin = Number.MAX_SAFE_INTEGER;
var currentMinIndex = -1;
for(var i in xrefEntries) {
if(xrefEntries[i].offset > currentOffset) {
if(xrefEntries[i].offset < currentMin) {
currentMin = xrefEntries[i].offset;
currentMinIndex = i;
}
}
}
if(currentMinIndex === -1) {
return current;
}
return xrefEntries[currentMinIndex];
}
function updateArray(array, pos, str) {
var upd = stringToUint8Array(str);
for (var i = 0, len=upd.length; i < len; i++) {
array[i+pos] = upd[i];
}
return array;
}
function copyToEnd(array, from, to) {
var buf = new Uint8Array(array.length + (to - from));
for (var i = 0, len=array.length; i < len; i++) {
buf[i] = array[i];
}
for (var i = 0, len=(to - from); i < len; i++) {
buf[array.length + i] = array[from + i];
}
return buf;
}
function insertIntoArray(array, pos, str) {
var ins = stringToUint8Array(str);
var buf = new Uint8Array(array.length + ins.length);
for (var i = 0; i < pos; i++) {
buf[i] = array[i];
}
for (var i = 0; i < ins.length; i++) {
buf[pos+i] = ins[i];
}
for (var i = pos; i < array.length; i++) {
buf[ins.length+i] = array[i];
}
return buf;
}
function stringToUint8Array(str) {
var buf = new Uint8Array(str.length);
for (var i=0, strLen=str.length; i<strLen; i++) {
buf[i] = str.charCodeAt(i);
}
return buf;
}
function uint8ArrayToString(buf, from, to) {
if(typeof from !== 'undefined' && typeof to !== 'undefined') {
var s = '';
for (var i=from; i<to; i++) {
s = s + String.fromCharCode(buf[i]);
}
return s;
function findFreeXrefNr(xrefEntries, used) {
used = typeof used !== 'undefined' ? used : [];
var inc = used.length;
for (var i=1;i<xrefEntries.length;i++) {
var index = used.indexOf(i);
var entry = xrefEntries[""+i];
if(index === -1 && (typeof entry === 'undefined' || entry.free)) {
return i;
}
if(index !== -1) {
inc--;
}
}
function find(uint8, needle, start, limit) {
start = typeof start !== 'undefined' ? start : 0;
limit = typeof limit !== 'undefined' ? limit : Number.MAX_SAFE_INTEGER;
var search = stringToUint8Array(needle);
var match = 0;
for(var i=start;i<uint8.length && i<limit;i++) {
if(uint8[i] === search[match]) {
match++;
} else {
match = 0;
if(uint8[i] === search[match]) {
match++;
}
}
if(match === search.length) {
return (i + 1) - match;
}
function findBackwards(uint8, needle, start, limit) {
start = typeof start !== 'undefined' ? start : uint8.length;
limit = typeof limit !== 'undefined' ? limit : Number.MAX_SAFE_INTEGER;
var search = stringToUint8Array(needle);
var match = search.length - 1;
for(var i=start;i>=0 && i<limit;i--) {
if(uint8[i] === search[match]) {
match--;
} else {
match = search.length - 1;
if(uint8[i] === search[match]) {
match--;
}
}
if(match === 0) {
return i - 1;
}
var a = "";
for( var i=0; i<s.length; i++ ) {
a = a + pad2(s.charCodeAt(i).toString(16));
}
return a;
const cryptoLib = getCrypto();
const digestTmpBuf = await cryptoLib.digest({ name: "SHA-256" }, array);
const digestTmpArray = new Uint8Array(digestTmpBuf);
const digestTmpStr = uint8ArrayToString(digestTmpArray);
const sha256Hex = strHex(digestTmpStr);
return sha256Hex;
}
function isSigInRoot(pdf) {
}
function updateXrefOffset(xref, offset, offsetDelta) {
for(var i in xref.entries) {
if(xref.entries[i].offset >= offset) {
xref.entries[i].offset += offsetDelta;
}
for(var i in xref.xrefBlocks) {
if(xref.xrefBlocks[i] >= offset) {
xref.xrefBlocks[i] += offsetDelta;
}
}
}
function updateXrefBlocks(xrefBlocks, offset, offsetDelta) {
for(var i in xrefBlocks) {
if(xrefBlocks[i].start >= offset) {
xrefBlocks[i].start += offsetDelta;
}
if(xrefBlocks[i].end >= offset) {
xrefBlocks[i].end += offsetDelta;
}
}
}
function updateOffset(pos, offset, offsetDelta) {
if(pos >= offset) {
return pos + offsetDelta;
}
return pos;
}
/**
* (D:YYYYMMDDHHmmSSOHH'mm)
* e.g. (D:20151210164400+01'00')
* where:
* YYYY shall be the year
* MM shall be the month (01–12)
* DD shall be the day (01–31)
* HH shall be the hour (00–23)
* mm shall be the minute (00–59)
* SS shall be the second (00–59)
* O shall be the relationship of local time to Universal Time (UT), and shall be denoted by one of the characters PLUS SIGN (U+002B) (+), HYPHEN-MINUS (U+002D) (-), or LATIN CAPITAL LETTER Z (U+005A) (Z) (see below)
* HH followed by APOSTROPHE (U+0027) (') shall be the absolute value of the offset from UT in hours (00–23)
* mm shall be the absolute value of the offset from UT in minutes (00–59)
*/
function now(date) {
//date = typeof date !== 'undefined' ? date : new Date();
var yyyy = date.getFullYear().toString();
var MM = pad2(date.getMonth() + 1);
var dd = pad2(date.getDate());
var hh = pad2(date.getHours());
var mm = pad2(date.getMinutes());
var ss = pad2(date.getSeconds());
return yyyy + MM + dd+ hh + mm + ss + createOffset(date);
}
function createOffset(date) {
var sign = (date.getTimezoneOffset() > 0) ? "-" : "+";
var offset = Math.abs(date.getTimezoneOffset());
var hours = pad2(Math.floor(offset / 60));
var minutes = pad2(offset % 60);
return sign + hours + "'" + minutes;
}
async function newSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey) {
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
try {
// {annotEntry} is the ref to the annot widget. If we enlarge the array, make sure all the offsets
// after the modification will be updated -> xref table and startxref
var annotEntry = findFreeXrefNr(pdf.xref.entries);
// we'll store all the modifications we make, as we need to adjust the offset in the PDF
var offsetForm = find(pdf.stream.bytes, '<<', root.offset, rootSuccessor.offset) + 2;
//first we need to find the root element and add the following:
//
// /AcroForm<</Fields[{annotEntry} 0 R] /SigFlags 3>>
//
var appendAcroForm = '/AcroForm<</Fields[' + annotEntry + ' 0 R] /SigFlags 3>>';
//before we insert the acroform, we find the right place for annotentry
//we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Contents[
var pages = pdf.catalog.catDict.get('Pages');
//get first page, we have hidden sig, so don't bother
var ref = pages.get('Kids')[0];
var xref = pdf.xref.fetch(ref);
var offsetContentEnd = xref.get('#Contents_offset');
//we now search backwards, this is safe as we don't expect user content here
var offsetContent = findBackwards(pdf.stream.bytes, '/Contents', offsetContentEnd);
var appendAnnots = '/Annots[' + annotEntry + ' 0 R]\n ';
//now insert string into stream
var array = insertIntoArray(pdf.stream.bytes, offsetForm, appendAcroForm);
//recalculate the offsets in the xref table, only update those that are affected
updateXrefOffset(pdf.xref, offsetForm, appendAcroForm.length);
offsetContent = updateOffset(offsetContent, offsetForm, appendAcroForm.length);
var array = insertIntoArray(array, offsetContent, appendAnnots);
updateXrefOffset(pdf.xref, offsetContent, appendAnnots.length);
offsetContent = -1; //not needed anymore, don't update when offset changes
//Then add to the next free object (annotEntry)
//add right before the xref table or stream
//if its a table, place element before the xref table
//
// sigEntry is the ref to the signature content. Next we need the signature object
var sigEntry = findFreeXrefNr(pdf.xref.entries, [annotEntry]);
//
// {annotEntry} 0 obj
// <</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature)/V Y 0 R>>
// endobj
//
var append = annotEntry + ' 0 obj\n<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature' + annotEntry + ')/V ' + sigEntry + ' 0 R>>\nendobj\n\n';
// we want the offset just before the last xref table or entry
var blocks = findXrefBlocks(pdf.xref.xrefBlocks);
var offsetAnnot = blocks[0].start;
array = insertIntoArray(array, offsetAnnot, append);
//no updateXrefOffset, as the next entry will be following
//
// {sigEntry} 0 obj
// <</Contents <0481801e6d931d561563fb254e27c846e08325570847ed63d6f9e35 ... b2c8788a5>
// /Type/Sig/SubFilter/adbe.pkcs7.detached/Location(Ghent)/M(D:20120928104114+02'00')
// /ByteRange [A B C D]/Filter/Adobe.PPKLite/Reason(Test)/ContactInfo()>>
// endobj
//
//the next entry goes below the above
var offsetSig = offsetAnnot + append.length;
// Both {annotEntry} and {sigEntry} objects need to be added to the last xref table. The byte range needs
// to be adjusted. Since the signature will always be in a gap, use first an empty sig
// to check the size, add ~25% size, then calculate the signature and place in the empty
// space.
var start = sigEntry + ' 0 obj\n<</Contents <';
var dummy = await sign_pki(signingCert, certificateChain, privateKey, stringToUint8Array('A'), date);
//TODO: Adobe thinks its important to have the right size, no idea why this is the case
var crypto = new Array(round256(dummy.length * 2)).join('0');
var middle = '>\n/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:' + now(date) + '\')\n/ByteRange ';
var byteRange = '[0000000000 0000000000 0000000000 0000000000]';
var end = '/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>\nendobj\n\n';
//all together
var append2 = start + crypto + middle + byteRange + end;
var offsetByteRange = start.length + crypto.length + middle.length;
array = insertIntoArray(array, offsetSig, append2);
updateXrefOffset(pdf.xref, offsetAnnot, append2.length + append.length);
//find the xref tables, remove them and also the EOF, as we'll write a new table
var xrefBlocks = findXrefBlocks(pdf.xref.xrefBlocks);
for (var i in xrefBlocks) {
var oldSize = array.length;
array = removeFromArray(array, xrefBlocks[i].start, xrefBlocks[i].end);
var length = array.length - oldSize;
updateXrefOffset(pdf.xref, xrefBlocks[i].start, length);
//check for %%EOF and remove it as well
var offsetEOF = find(array, '%%EOF', xrefBlocks[i].start, xrefBlocks[i].start + 20);
if (offsetEOF > 0) {
var lengthEOF = '%%EOF'.length;
array = removeFromArray(array, offsetEOF, offsetEOF + lengthEOF);
updateXrefOffset(pdf.xref, offsetEOF, -lengthEOF);
updateXrefBlocks(xrefBlocks, offsetEOF, -lengthEOF);
offsetAnnot = updateOffset(offsetAnnot, offsetEOF, -lengthEOF);
offsetSig = updateOffset(offsetSig, offsetEOF, -lengthEOF);
}
updateXrefBlocks(xrefBlocks, xrefBlocks[i].start, length);
offsetAnnot = updateOffset(offsetAnnot, xrefBlocks[i].start, length);
offsetSig = updateOffset(offsetSig, xrefBlocks[i].start, length);
var sha256Hex = await sha256(array);
//add the new entries to the xref
pdf.xref.entries[annotEntry] = {offset: offsetAnnot, gen: 0, free: false};
pdf.xref.entries[sigEntry] = {offset: offsetSig, gen: 0, free: false};
var xrefTable = createXrefTable(pdf.xref.entries);
//also empty entries count as in the PDF spec, page 720 (example)
xrefTable += createTrailer(pdf.xref.topDict, array.length, sha256Hex, pdf.xref.entries.length);
array = insertIntoArray(array, array.length, xrefTable);
//since we consolidate, no prev! [adjust /Prev -> rawparsing + offset]
var from1 = 0;
var to1 = offsetSig + start.length;
var from2 = to1 + crypto.length;
var to2 = (array.length - from2) - 1;
var byteRange = '[' + pad10(from1) + ' ' + pad10(to1 - 1) + ' ' + pad10(from2 + 1) + ' ' + pad10(to2) + ']';
array = updateArray(array, (offsetSig + offsetByteRange), byteRange);
var data = removeFromArray(array, to1 - 1, from2 + 1);
var crypto2 = await sign_pki(signingCert, certificateChain, privateKey, data.buffer, date);
array = updateArray(array, to1, crypto2);
return array;
} catch (err) {
throw new Error('Error creating new signature in PDF: ' + err);
}
async function appendSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey) {
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
try {
//copy root and the entry with contents to the end
var startRoot = pdf.stream.bytes.length + 1;
var array = copyToEnd(pdf.stream.bytes, root.offset - 1, rootSuccessor.offset);
//since we signed the first one, we know how the pdf has to look like:
var offsetAcroForm = find(array, '/AcroForm<</Fields', startRoot);
var endOffsetAcroForm = find(array, ']', offsetAcroForm);
var annotEntry = findFreeXrefNr(pdf.xref.entries);
var sigEntry = findFreeXrefNr(pdf.xref.entries, [annotEntry]);
var appendAnnot = ' ' + annotEntry + ' 0 R';
array = insertIntoArray(array, endOffsetAcroForm, appendAnnot);
//we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Annots
var pages = pdf.catalog.catDict.get('Pages');
//get first page, we have hidden sig, so don't bother
var contentRef = pages.get('Kids')[0];
var xref = pdf.xref.fetch(contentRef);
var offsetAnnotEnd = xref.get('#Annots_offset');
//we now search ], this is safe as we signed it previously
var endOffsetAnnot = find(array, ']', offsetAnnotEnd);
var xrefEntry = pdf.xref.getEntry(contentRef.num);
var xrefEntrySuccosser = findSuccessorEntry(pdf.xref.entries, xrefEntry);
var offsetAnnotRelative = endOffsetAnnot - xrefEntrySuccosser.offset;
var startContent = array.length;
array = copyToEnd(array, xrefEntry.offset, xrefEntrySuccosser.offset);
array = insertIntoArray(array, array.length + offsetAnnotRelative, appendAnnot);
var startAnnot = array.length;
var append = annotEntry + ' 0 obj\n<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature' + annotEntry + ')/V ' + sigEntry + ' 0 R>>\nendobj\n\n';
array = insertIntoArray(array, startAnnot, append);
var startSig = array.length;
var start = sigEntry + ' 0 obj\n<</Contents <';
var dummy = await sign_pki(signingCert, certificateChain, privateKey, stringToUint8Array('A'), date);
//TODO: Adobe thinks its important to have the right size, no idea why this is the case
var crypto = new Array(round256(dummy.length * 2)).join('0');
var middle = '>\n/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:' + now(date) + '\')\n/ByteRange ';
var byteRange = '[0000000000 0000000000 0000000000 0000000000]';
var end = '/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>\nendobj\n\n';
//all together
var append2 = start + crypto + middle + byteRange + end;
array = insertIntoArray(array, startSig, append2);
var sha256Hex = await sha256(array);
var prev = pdf.xref.xrefBlocks[0];
var startxref = array.length;
var xrefEntries = [];
xrefEntries[0] = {offset: 0, gen: 65535, free: true};
xrefEntries[pdf.xref.topDict.getRaw('Root').num] = {offset: startRoot, gen: 0, free: false};
xrefEntries[contentRef.num] = {offset: startContent, gen: 0, free: false};
xrefEntries[annotEntry] = {offset: startAnnot, gen: 0, free: false};
xrefEntries[sigEntry] = {offset: startSig, gen: 0, free: false};
var xrefTable = createXrefTableAppend(xrefEntries);
xrefTable += createTrailer(pdf.xref.topDict, startxref, sha256Hex, xrefEntries.length, prev);
array = insertIntoArray(array, array.length, xrefTable);
var from1 = 0;
var to1 = startSig + start.length;
var from2 = to1 + crypto.length;
var to2 = (array.length - from2) - 1;
var byteRange = '[' + pad10(from1) + ' ' + pad10(to1 - 1) + ' ' + pad10(from2 + 1) + ' ' + pad10(to2) + ']';
array = updateArray(array, from2 + middle.length, byteRange);
//now sign from1-to1 / from2-to2 and update byterange
var data = removeFromArray(array, to1 - 1, from2 + 1);
var crypto2 = await sign_pki(signingCert, certificateChain, privateKey, data.buffer, date);
array = updateArray(array, to1, crypto2);
return array;
} catch (err) {
throw new Error('Error appending signature in PDF: ' + err);
}
}
function loadPdf(pdfArray) {
try {
var pdf = new pdfjsCoreDocument.PDFDocument(false, pdfArray, '');
pdf.parseStartXRef();
pdf.parse();
return pdf;
}
catch(err) {
throw new Error('Error parsing PDF: ' + err);
}
async function sign_pki(signingCert, certificateChain, privateKey, data, date) {
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
const crypto = getCrypto();
//date = typeof date !== 'undefined' ? date : new Date();
const hashAlg = "SHA-256";
const digest = await crypto.digest({ name: hashAlg }, data);
const signedAttr = [];
signedAttr.push(new Attribute({
type: "1.2.840.113549.1.9.3",
values: [
new ObjectIdentifier({ value: "1.2.840.113549.1.7.1" })
]
})); // contentType
signedAttr.push(new Attribute({
type: "1.2.840.113549.1.9.4",
values: [
new OctetString({ valueHex: digest })
]
})); // messageDigest
signedAttr.push(new Attribute({
type: "1.2.840.113549.1.9.5",
values: [
new UTCTime({ valueDate: date })
]
})); // signingTime
const cmsSignedSimpl = new SignedData({
encapContentInfo: new EncapsulatedContentInfo({
eContentType: "1.2.840.113549.1.7.1" // "data" content type
signerInfos: [
new SignerInfo({
version: 1,
sid: new IssuerAndSerialNumber({
issuer: signingCert.issuer,
serialNumber: signingCert.serialNumber
}),
signedAttrs: new SignedAndUnsignedAttributes({
type: 0,
attributes: signedAttr
})
})
],
certificates: certificateChain
});
const signatureBuffer = await cmsSignedSimpl.sign(privateKey, 0, hashAlg, data.buffer);
const cmsSignedSchema = cmsSignedSimpl.toSchema(true);
const cmsContentSimp = new ContentInfo({
contentType: "1.2.840.113549.1.7.2",
content: cmsSignedSchema
});
const _cmsSignedSchema = cmsContentSimp.toSchema();
//region Make length of some elements in "indefinite form"
_cmsSignedSchema.lenBlock.isIndefiniteForm = true;
const block1 = _cmsSignedSchema.valueBlock.value[1];
block1.lenBlock.isIndefiniteForm = true;
const block2 = block1.valueBlock.value[0];
block2.lenBlock.isIndefiniteForm = true;
let cmsSignedBuffer = _cmsSignedSchema.toBER(false);
const cmsSignedArray = new Uint8Array(cmsSignedBuffer);
const cmsSignedString = uint8ArrayToString(cmsSignedArray);
const hex = strHex(cmsSignedString);
return hex;
async function signPdfObjects (pdfRaw, signingCert, certificateChain, privateKey, date) {
date = ((typeof date !== 'undefined') && (date !== null)) ? date : new Date();
if (pdfRaw instanceof Buffer) {
pdfRaw = new Uint8Array(pdfRaw);
} else
if(pdfRaw instanceof ArrayBuffer) {
pdfRaw = new Uint8Array(pdfRaw);
}
var rootSuccessor = findSuccessorEntry(pdf.xref.entries, root);
return await newSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey);
} else {
return await appendSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey);
}
export async function signPdf(pdfRaw, signingCert, certificateChain, privateKey) {
console.log("Calling signPdf");
const signingCertObj = parseCertificate(signingCert);
const certificateChainObj = [];
certificateChainObj[0] = parseCertificate(signingCert);
for (let i = 0; i < certificateChain.length; i++) {
certificateChainObj[i + 1] = parseCertificate(certificateChain[i])
}
let privateKeyDecoded;
try {
console.log("Calling parsePrivateKey");
privateKeyDecoded = await parsePrivateKey(privateKey);
} catch (e) {
console.log("Error decoding private key: ", e);
throw new Error('Error decoding private key: ' + e);
return await signPdfObjects(pdfRaw, signingCertObj, certificateChainObj, privateKeyDecoded);