pdf-lite 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EXAMPLES.md +354 -0
- package/README.md +5 -4
- package/dist/core/tokens/number-token.js +11 -6
- package/dist/pdf/pdf-document.d.ts +9 -3
- package/dist/pdf/pdf-document.js +15 -3
- package/dist/signing/index.d.ts +1 -0
- package/dist/signing/index.js +1 -0
- package/dist/signing/signatures/adbe-pkcs7-detached.d.ts +7 -0
- package/dist/signing/signatures/adbe-pkcs7-detached.js +36 -0
- package/dist/signing/signatures/adbe-pkcs7-sha1.d.ts +7 -0
- package/dist/signing/signatures/adbe-pkcs7-sha1.js +54 -0
- package/dist/signing/signatures/adbe-x509-rsa-sha1.d.ts +7 -0
- package/dist/signing/signatures/adbe-x509-rsa-sha1.js +77 -0
- package/dist/signing/signatures/base.d.ts +16 -1
- package/dist/signing/signatures/base.js +18 -0
- package/dist/signing/signatures/etsi-cades-detached.d.ts +7 -0
- package/dist/signing/signatures/etsi-cades-detached.js +36 -0
- package/dist/signing/signatures/etsi-rfc3161.d.ts +7 -0
- package/dist/signing/signatures/etsi-rfc3161.js +102 -0
- package/dist/signing/signer.d.ts +55 -0
- package/dist/signing/signer.js +196 -1
- package/dist/signing/types.d.ts +26 -0
- package/package.json +3 -3
package/EXAMPLES.md
CHANGED
|
@@ -1510,3 +1510,357 @@ for (const obj of preservingDecoder) {
|
|
|
1510
1510
|
console.log('Match:', simpleDict === reconstructed)
|
|
1511
1511
|
}
|
|
1512
1512
|
```
|
|
1513
|
+
|
|
1514
|
+
## PDF verify signatures example
|
|
1515
|
+
|
|
1516
|
+
```typescript
|
|
1517
|
+
import { PdfArray } from 'pdf-lite/core/objects/pdf-array'
|
|
1518
|
+
import { PdfDictionary } from 'pdf-lite/core/objects/pdf-dictionary'
|
|
1519
|
+
import { PdfIndirectObject } from 'pdf-lite/core/objects/pdf-indirect-object'
|
|
1520
|
+
import { PdfName } from 'pdf-lite/core/objects/pdf-name'
|
|
1521
|
+
import { PdfNumber } from 'pdf-lite/core/objects/pdf-number'
|
|
1522
|
+
import { PdfObjectReference } from 'pdf-lite/core/objects/pdf-object-reference'
|
|
1523
|
+
import { PdfStream } from 'pdf-lite/core/objects/pdf-stream'
|
|
1524
|
+
import { PdfDocument } from 'pdf-lite/pdf/pdf-document'
|
|
1525
|
+
import { PdfString } from 'pdf-lite/core/objects/pdf-string'
|
|
1526
|
+
import {
|
|
1527
|
+
PdfAdbePkcsX509RsaSha1SignatureObject,
|
|
1528
|
+
PdfAdbePkcs7DetachedSignatureObject,
|
|
1529
|
+
PdfAdbePkcs7Sha1SignatureObject,
|
|
1530
|
+
PdfEtsiCadesDetachedSignatureObject,
|
|
1531
|
+
PdfEtsiRfc3161SignatureObject,
|
|
1532
|
+
} from 'pdf-lite'
|
|
1533
|
+
import { rsaSigningKeys } from '../packages/pdf-lite/test/unit/fixtures/rsa-2048/index'
|
|
1534
|
+
|
|
1535
|
+
function createPage(
|
|
1536
|
+
contentStreamRef: PdfObjectReference,
|
|
1537
|
+
): PdfIndirectObject<PdfDictionary> {
|
|
1538
|
+
const pageDict = new PdfDictionary()
|
|
1539
|
+
pageDict.set('Type', new PdfName('Page'))
|
|
1540
|
+
pageDict.set(
|
|
1541
|
+
'MediaBox',
|
|
1542
|
+
new PdfArray([
|
|
1543
|
+
new PdfNumber(0),
|
|
1544
|
+
new PdfNumber(0),
|
|
1545
|
+
new PdfNumber(612),
|
|
1546
|
+
new PdfNumber(792),
|
|
1547
|
+
]),
|
|
1548
|
+
)
|
|
1549
|
+
pageDict.set('Contents', contentStreamRef)
|
|
1550
|
+
return new PdfIndirectObject({ content: pageDict })
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
function createPages(
|
|
1554
|
+
pages: PdfIndirectObject<PdfDictionary>[],
|
|
1555
|
+
): PdfIndirectObject<PdfDictionary> {
|
|
1556
|
+
const pagesDict = new PdfDictionary()
|
|
1557
|
+
pagesDict.set('Type', new PdfName('Pages'))
|
|
1558
|
+
pagesDict.set('Kids', new PdfArray(pages.map((x) => x.reference)))
|
|
1559
|
+
pagesDict.set('Count', new PdfNumber(pages.length))
|
|
1560
|
+
return new PdfIndirectObject({ content: pagesDict })
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
function createCatalog(
|
|
1564
|
+
pagesRef: PdfObjectReference,
|
|
1565
|
+
): PdfIndirectObject<PdfDictionary> {
|
|
1566
|
+
const catalogDict = new PdfDictionary()
|
|
1567
|
+
catalogDict.set('Type', new PdfName('Catalog'))
|
|
1568
|
+
catalogDict.set('Pages', pagesRef)
|
|
1569
|
+
return new PdfIndirectObject({ content: catalogDict })
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
function createFont(): PdfIndirectObject<PdfDictionary> {
|
|
1573
|
+
const fontDict = new PdfDictionary()
|
|
1574
|
+
fontDict.set('Type', new PdfName('Font'))
|
|
1575
|
+
fontDict.set('Subtype', new PdfName('Type1'))
|
|
1576
|
+
fontDict.set('BaseFont', new PdfName('Helvetica'))
|
|
1577
|
+
return new PdfIndirectObject({ content: fontDict })
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
function createResources(
|
|
1581
|
+
fontRef: PdfObjectReference,
|
|
1582
|
+
): PdfIndirectObject<PdfDictionary> {
|
|
1583
|
+
const resourcesDict = new PdfDictionary()
|
|
1584
|
+
const fontDict = new PdfDictionary()
|
|
1585
|
+
fontDict.set('F1', fontRef)
|
|
1586
|
+
resourcesDict.set('Font', fontDict)
|
|
1587
|
+
return new PdfIndirectObject({ content: resourcesDict })
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
function createPageWithSignatureField(
|
|
1591
|
+
contentStreamRef: PdfObjectReference,
|
|
1592
|
+
signatureAnnotRef: PdfObjectReference,
|
|
1593
|
+
): PdfIndirectObject<PdfDictionary> {
|
|
1594
|
+
const pageDict = new PdfDictionary()
|
|
1595
|
+
pageDict.set('Type', new PdfName('Page'))
|
|
1596
|
+
pageDict.set(
|
|
1597
|
+
'MediaBox',
|
|
1598
|
+
new PdfArray([
|
|
1599
|
+
new PdfNumber(0),
|
|
1600
|
+
new PdfNumber(0),
|
|
1601
|
+
new PdfNumber(612),
|
|
1602
|
+
new PdfNumber(792),
|
|
1603
|
+
]),
|
|
1604
|
+
)
|
|
1605
|
+
pageDict.set('Contents', contentStreamRef)
|
|
1606
|
+
pageDict.set('Annots', new PdfArray([signatureAnnotRef]))
|
|
1607
|
+
|
|
1608
|
+
return new PdfIndirectObject({ content: pageDict })
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
function createSignatureAnnotation(
|
|
1612
|
+
signatureRef: PdfObjectReference,
|
|
1613
|
+
appearanceStreamRef: PdfObjectReference,
|
|
1614
|
+
pageRef: PdfObjectReference,
|
|
1615
|
+
signatureName: string,
|
|
1616
|
+
): PdfIndirectObject<PdfDictionary> {
|
|
1617
|
+
const signatureAnnotation = new PdfDictionary()
|
|
1618
|
+
signatureAnnotation.set('Type', new PdfName('Annot'))
|
|
1619
|
+
signatureAnnotation.set('Subtype', new PdfName('Widget'))
|
|
1620
|
+
signatureAnnotation.set('FT', new PdfName('Sig'))
|
|
1621
|
+
signatureAnnotation.set('T', new PdfString(signatureName))
|
|
1622
|
+
signatureAnnotation.set(
|
|
1623
|
+
'Rect',
|
|
1624
|
+
new PdfArray([
|
|
1625
|
+
new PdfNumber(135), // x1: Start after "Signature: " text (~72 + 63)
|
|
1626
|
+
new PdfNumber(640), // y1: Bottom of signature area (652 - 12)
|
|
1627
|
+
new PdfNumber(400), // x2: End of signature line
|
|
1628
|
+
new PdfNumber(665), // y2: Top of signature area (652 + 13)
|
|
1629
|
+
]),
|
|
1630
|
+
)
|
|
1631
|
+
signatureAnnotation.set('F', new PdfNumber(4))
|
|
1632
|
+
signatureAnnotation.set('P', pageRef) // Reference to parent page
|
|
1633
|
+
signatureAnnotation.set('V', signatureRef)
|
|
1634
|
+
|
|
1635
|
+
// Add appearance dictionary
|
|
1636
|
+
const appearanceDict = new PdfDictionary()
|
|
1637
|
+
appearanceDict.set('N', appearanceStreamRef)
|
|
1638
|
+
signatureAnnotation.set('AP', appearanceDict)
|
|
1639
|
+
|
|
1640
|
+
return new PdfIndirectObject({ content: signatureAnnotation })
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
function createSignatureAppearance(): PdfIndirectObject<PdfStream> {
|
|
1644
|
+
// Create font for appearance
|
|
1645
|
+
const appearanceFont = new PdfDictionary()
|
|
1646
|
+
appearanceFont.set('Type', new PdfName('Font'))
|
|
1647
|
+
appearanceFont.set('Subtype', new PdfName('Type1'))
|
|
1648
|
+
appearanceFont.set('BaseFont', new PdfName('Helvetica'))
|
|
1649
|
+
|
|
1650
|
+
const fontDict = new PdfDictionary()
|
|
1651
|
+
fontDict.set('F1', appearanceFont)
|
|
1652
|
+
|
|
1653
|
+
const resourcesDict = new PdfDictionary()
|
|
1654
|
+
resourcesDict.set('Font', fontDict)
|
|
1655
|
+
|
|
1656
|
+
// Create appearance stream header
|
|
1657
|
+
const appearanceHeader = new PdfDictionary()
|
|
1658
|
+
appearanceHeader.set('Type', new PdfName('XObject'))
|
|
1659
|
+
appearanceHeader.set('Subtype', new PdfName('Form'))
|
|
1660
|
+
appearanceHeader.set(
|
|
1661
|
+
'BBox',
|
|
1662
|
+
new PdfArray([
|
|
1663
|
+
new PdfNumber(0),
|
|
1664
|
+
new PdfNumber(0),
|
|
1665
|
+
new PdfNumber(265), // Width: 400 - 135
|
|
1666
|
+
new PdfNumber(25), // Height: 665 - 640
|
|
1667
|
+
]),
|
|
1668
|
+
)
|
|
1669
|
+
appearanceHeader.set('Resources', resourcesDict)
|
|
1670
|
+
|
|
1671
|
+
// Create appearance stream for the signature
|
|
1672
|
+
return new PdfIndirectObject({
|
|
1673
|
+
content: new PdfStream({
|
|
1674
|
+
header: appearanceHeader,
|
|
1675
|
+
original:
|
|
1676
|
+
'BT /F1 10 Tf 5 14 Td (Digitally signed by: Jake Shirley) Tj ET',
|
|
1677
|
+
}),
|
|
1678
|
+
})
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
// Create the document
|
|
1682
|
+
const document = new PdfDocument()
|
|
1683
|
+
|
|
1684
|
+
// Create font
|
|
1685
|
+
const font = createFont()
|
|
1686
|
+
document.add(font)
|
|
1687
|
+
|
|
1688
|
+
// Create resources with the font
|
|
1689
|
+
const resources = createResources(font.reference)
|
|
1690
|
+
document.add(resources)
|
|
1691
|
+
|
|
1692
|
+
// Create content stream for first page
|
|
1693
|
+
const contentStream = new PdfIndirectObject({
|
|
1694
|
+
content: new PdfStream({
|
|
1695
|
+
header: new PdfDictionary(),
|
|
1696
|
+
original: 'BT /F1 24 Tf 100 700 Td (Hello, PDF-Lite!) Tj ET',
|
|
1697
|
+
}),
|
|
1698
|
+
})
|
|
1699
|
+
document.add(contentStream)
|
|
1700
|
+
|
|
1701
|
+
// Create first page
|
|
1702
|
+
const page1 = createPage(contentStream.reference)
|
|
1703
|
+
page1.content.set('Resources', resources.reference)
|
|
1704
|
+
document.add(page1)
|
|
1705
|
+
|
|
1706
|
+
// Array to hold all pages and signature objects
|
|
1707
|
+
const allPages: PdfIndirectObject<PdfDictionary>[] = [page1]
|
|
1708
|
+
const allSignatures: any[] = []
|
|
1709
|
+
const signatureFields: PdfObjectReference[] = []
|
|
1710
|
+
|
|
1711
|
+
// Helper function to create a signature page
|
|
1712
|
+
function createSignaturePage(
|
|
1713
|
+
signatureType: string,
|
|
1714
|
+
signatureObj: any,
|
|
1715
|
+
pageNumber: number,
|
|
1716
|
+
) {
|
|
1717
|
+
const content = new PdfIndirectObject({
|
|
1718
|
+
content: new PdfStream({
|
|
1719
|
+
header: new PdfDictionary(),
|
|
1720
|
+
original: `BT /F1 12 Tf 72 712 Td (Signature Type: ${signatureType}) Tj 0 -60 Td (Signature: ________________________________) Tj ET`,
|
|
1721
|
+
}),
|
|
1722
|
+
})
|
|
1723
|
+
document.add(content)
|
|
1724
|
+
|
|
1725
|
+
const appearance = createSignatureAppearance()
|
|
1726
|
+
document.add(appearance)
|
|
1727
|
+
|
|
1728
|
+
// Create page first to get its reference
|
|
1729
|
+
const page = createPageWithSignatureField(
|
|
1730
|
+
content.reference,
|
|
1731
|
+
new PdfObjectReference(0, 0), // Temporary placeholder
|
|
1732
|
+
)
|
|
1733
|
+
page.content.set('Resources', resources.reference)
|
|
1734
|
+
document.add(page)
|
|
1735
|
+
|
|
1736
|
+
// Now create annotation with page reference
|
|
1737
|
+
const annotation = createSignatureAnnotation(
|
|
1738
|
+
signatureObj.reference,
|
|
1739
|
+
appearance.reference,
|
|
1740
|
+
page.reference,
|
|
1741
|
+
`Signature${pageNumber}`,
|
|
1742
|
+
)
|
|
1743
|
+
document.add(annotation)
|
|
1744
|
+
|
|
1745
|
+
// Update page's Annots array with actual annotation reference
|
|
1746
|
+
page.content.set('Annots', new PdfArray([annotation.reference]))
|
|
1747
|
+
|
|
1748
|
+
signatureFields.push(annotation.reference)
|
|
1749
|
+
return page
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
// Page 2: Adobe PKCS7 Detached
|
|
1753
|
+
const pkcs7DetachedSig = new PdfAdbePkcs7DetachedSignatureObject({
|
|
1754
|
+
privateKey: rsaSigningKeys.privateKey,
|
|
1755
|
+
certificate: rsaSigningKeys.cert,
|
|
1756
|
+
issuerCertificate: rsaSigningKeys.caCert,
|
|
1757
|
+
name: 'Jake Shirley',
|
|
1758
|
+
location: 'Earth',
|
|
1759
|
+
reason: 'PKCS7 Detached Signature',
|
|
1760
|
+
contactInfo: 'test@test.com',
|
|
1761
|
+
revocationInfo: {
|
|
1762
|
+
crls: [rsaSigningKeys.caCrl],
|
|
1763
|
+
ocsps: [rsaSigningKeys.ocspResponse],
|
|
1764
|
+
},
|
|
1765
|
+
})
|
|
1766
|
+
allSignatures.push(pkcs7DetachedSig)
|
|
1767
|
+
allPages.push(createSignaturePage('Adobe PKCS7 Detached', pkcs7DetachedSig, 2))
|
|
1768
|
+
|
|
1769
|
+
// Page 3: Adobe PKCS7 SHA1
|
|
1770
|
+
const pkcs7Sha1Sig = new PdfAdbePkcs7Sha1SignatureObject({
|
|
1771
|
+
privateKey: rsaSigningKeys.privateKey,
|
|
1772
|
+
certificate: rsaSigningKeys.cert,
|
|
1773
|
+
issuerCertificate: rsaSigningKeys.caCert,
|
|
1774
|
+
name: 'Jake Shirley',
|
|
1775
|
+
location: 'Earth',
|
|
1776
|
+
reason: 'PKCS7 SHA1 Signature',
|
|
1777
|
+
contactInfo: 'test@test.com',
|
|
1778
|
+
})
|
|
1779
|
+
allSignatures.push(pkcs7Sha1Sig)
|
|
1780
|
+
allPages.push(createSignaturePage('Adobe PKCS7 SHA1', pkcs7Sha1Sig, 3))
|
|
1781
|
+
|
|
1782
|
+
// Page 4: Adobe X509 RSA SHA1
|
|
1783
|
+
const x509RsaSha1Sig = new PdfAdbePkcsX509RsaSha1SignatureObject({
|
|
1784
|
+
privateKey: rsaSigningKeys.privateKey,
|
|
1785
|
+
certificate: rsaSigningKeys.cert,
|
|
1786
|
+
additionalCertificates: [rsaSigningKeys.caCert],
|
|
1787
|
+
name: 'Jake Shirley',
|
|
1788
|
+
location: 'Earth',
|
|
1789
|
+
reason: 'X509 RSA SHA1 Signature',
|
|
1790
|
+
contactInfo: 'test@test.com',
|
|
1791
|
+
revocationInfo: {
|
|
1792
|
+
crls: [rsaSigningKeys.caCrl],
|
|
1793
|
+
ocsps: [rsaSigningKeys.ocspResponse],
|
|
1794
|
+
},
|
|
1795
|
+
})
|
|
1796
|
+
allSignatures.push(x509RsaSha1Sig)
|
|
1797
|
+
allPages.push(createSignaturePage('Adobe X509 RSA SHA1', x509RsaSha1Sig, 4))
|
|
1798
|
+
|
|
1799
|
+
// Page 5: ETSI CAdES Detached
|
|
1800
|
+
const cadesDetachedSig = new PdfEtsiCadesDetachedSignatureObject({
|
|
1801
|
+
privateKey: rsaSigningKeys.privateKey,
|
|
1802
|
+
certificate: rsaSigningKeys.cert,
|
|
1803
|
+
issuerCertificate: rsaSigningKeys.caCert,
|
|
1804
|
+
name: 'Jake Shirley',
|
|
1805
|
+
location: 'Earth',
|
|
1806
|
+
reason: 'CAdES Detached Signature',
|
|
1807
|
+
contactInfo: 'test@test.com',
|
|
1808
|
+
revocationInfo: {
|
|
1809
|
+
crls: [rsaSigningKeys.caCrl],
|
|
1810
|
+
ocsps: [rsaSigningKeys.ocspResponse],
|
|
1811
|
+
},
|
|
1812
|
+
})
|
|
1813
|
+
allSignatures.push(cadesDetachedSig)
|
|
1814
|
+
allPages.push(createSignaturePage('ETSI CAdES Detached', cadesDetachedSig, 5))
|
|
1815
|
+
|
|
1816
|
+
// Page 6: ETSI RFC3161 (Timestamp)
|
|
1817
|
+
const rfc3161Sig = new PdfEtsiRfc3161SignatureObject({
|
|
1818
|
+
timeStampAuthority: {
|
|
1819
|
+
url: 'https://freetsa.org/tsr',
|
|
1820
|
+
},
|
|
1821
|
+
})
|
|
1822
|
+
allSignatures.push(rfc3161Sig)
|
|
1823
|
+
allPages.push(createSignaturePage('ETSI RFC3161 Timestamp', rfc3161Sig, 6))
|
|
1824
|
+
|
|
1825
|
+
// Create pages collection with all pages
|
|
1826
|
+
const pages = createPages(allPages)
|
|
1827
|
+
// Set parent reference for all pages
|
|
1828
|
+
allPages.forEach((page) => {
|
|
1829
|
+
page.content.set('Parent', pages.reference)
|
|
1830
|
+
})
|
|
1831
|
+
document.add(pages)
|
|
1832
|
+
|
|
1833
|
+
// Create catalog with AcroForm
|
|
1834
|
+
const catalog = createCatalog(pages.reference)
|
|
1835
|
+
|
|
1836
|
+
// Add AcroForm to catalog with all signature fields
|
|
1837
|
+
const acroForm = new PdfDictionary()
|
|
1838
|
+
acroForm.set('Fields', new PdfArray(signatureFields))
|
|
1839
|
+
acroForm.set('SigFlags', new PdfNumber(3))
|
|
1840
|
+
const acroFormObj = new PdfIndirectObject({ content: acroForm })
|
|
1841
|
+
document.add(acroFormObj)
|
|
1842
|
+
catalog.content.set('AcroForm', acroFormObj.reference)
|
|
1843
|
+
|
|
1844
|
+
document.add(catalog)
|
|
1845
|
+
|
|
1846
|
+
// Set the catalog as the root
|
|
1847
|
+
document.trailerDict.set('Root', catalog.reference)
|
|
1848
|
+
|
|
1849
|
+
// IMPORTANT: Add all signatures LAST - after all other objects
|
|
1850
|
+
// This ensures the ByteRange is calculated correctly for each signature
|
|
1851
|
+
allSignatures.forEach((sig) => {
|
|
1852
|
+
document.startNewRevision()
|
|
1853
|
+
document.add(sig)
|
|
1854
|
+
})
|
|
1855
|
+
|
|
1856
|
+
await document.commit()
|
|
1857
|
+
|
|
1858
|
+
const documentBytes = document.toBytes()
|
|
1859
|
+
const newDocument = await PdfDocument.fromBytes([documentBytes])
|
|
1860
|
+
|
|
1861
|
+
const validationResult = await newDocument.verifySignatures()
|
|
1862
|
+
console.log(
|
|
1863
|
+
'Signature validation result:',
|
|
1864
|
+
JSON.stringify(validationResult, null, 2),
|
|
1865
|
+
)
|
|
1866
|
+
```
|
package/README.md
CHANGED
|
@@ -8,10 +8,6 @@ A low-level, minimal-dependency, type-safe PDF library that works in the browser
|
|
|
8
8
|
|
|
9
9
|
PRs and issues are welcome!
|
|
10
10
|
|
|
11
|
-
## License
|
|
12
|
-
|
|
13
|
-
MIT License - see [LICENSE](LICENSE) for details.
|
|
14
|
-
|
|
15
11
|
## Features
|
|
16
12
|
|
|
17
13
|
- **Zero dependencies**: No external libraries are required, making it lightweight and easy to integrate.
|
|
@@ -99,6 +95,7 @@ Long-Term Validation (LTV) support ensures that digital signatures remain valid
|
|
|
99
95
|
**Other features:**
|
|
100
96
|
|
|
101
97
|
- [x] Timestamping
|
|
98
|
+
- [x] Verification of existing signatures
|
|
102
99
|
|
|
103
100
|
## Future Plans
|
|
104
101
|
|
|
@@ -283,3 +280,7 @@ import {
|
|
|
283
280
|
PdfEtsiRfc3161SignatureObject,
|
|
284
281
|
} from 'pdf-lite'
|
|
285
282
|
```
|
|
283
|
+
|
|
284
|
+
## License
|
|
285
|
+
|
|
286
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
@@ -13,13 +13,14 @@ export class PdfNumberToken extends PdfToken {
|
|
|
13
13
|
options instanceof Ref ||
|
|
14
14
|
options instanceof PdfNumberToken) {
|
|
15
15
|
this.#value = PdfNumberToken.getValue(options);
|
|
16
|
-
this.padTo = padTo ??
|
|
17
|
-
this.decimalPlaces =
|
|
16
|
+
this.padTo = padTo ?? PdfNumberToken.getPadding(options);
|
|
17
|
+
this.decimalPlaces =
|
|
18
|
+
decimalPlaces ?? PdfNumberToken.getDecimalPlaces(options);
|
|
18
19
|
this.isByteToken = false;
|
|
19
20
|
return;
|
|
20
21
|
}
|
|
21
22
|
this.#value = PdfNumberToken.getValue(options.value);
|
|
22
|
-
this.padTo = options.padTo ??
|
|
23
|
+
this.padTo = options.padTo ?? PdfNumberToken.getPadding(options.value);
|
|
23
24
|
this.decimalPlaces =
|
|
24
25
|
options.decimalPlaces ??
|
|
25
26
|
PdfNumberToken.getDecimalPlaces(options.value);
|
|
@@ -63,12 +64,16 @@ export class PdfNumberToken extends PdfToken {
|
|
|
63
64
|
if (typeof bytes === 'number') {
|
|
64
65
|
bytes = PdfNumberToken.toBytes(bytes);
|
|
65
66
|
}
|
|
66
|
-
|
|
67
|
+
// Count leading zeros
|
|
68
|
+
let leadingZeros = 0;
|
|
69
|
+
const originalLength = bytes.length;
|
|
67
70
|
while (bytes.length && bytes[0] === 0x30) {
|
|
68
71
|
bytes = bytes.slice(1);
|
|
69
|
-
|
|
72
|
+
leadingZeros++;
|
|
70
73
|
}
|
|
71
|
-
|
|
74
|
+
// If all characters were zeros (value is 0), padding should be total length
|
|
75
|
+
// Otherwise, padding should be total length (to maintain original formatting)
|
|
76
|
+
return originalLength;
|
|
72
77
|
}
|
|
73
78
|
static getDecimalPlaces(bytes) {
|
|
74
79
|
if (bytes instanceof PdfNumberToken) {
|
|
@@ -11,7 +11,7 @@ import { PdfEncryptionDictionaryObject } from '../security/types';
|
|
|
11
11
|
import { PdfTrailerEntries } from '../core/objects/pdf-trailer';
|
|
12
12
|
import { PdfDocumentSecurityStoreObject } from '../signing/document-security-store';
|
|
13
13
|
import { ByteArray } from '../types';
|
|
14
|
-
import { PdfSigner } from '../signing/signer';
|
|
14
|
+
import { PdfDocumentVerificationResult, PdfSigner } from '../signing/signer';
|
|
15
15
|
/**
|
|
16
16
|
* Represents a PDF document with support for reading, writing, and modifying PDF files.
|
|
17
17
|
* Handles document structure, revisions, encryption, and digital signatures.
|
|
@@ -214,9 +214,9 @@ export declare class PdfDocument extends PdfObject {
|
|
|
214
214
|
* Sets whether the document should use incremental updates.
|
|
215
215
|
* When true, locks all existing revisions to preserve original content.
|
|
216
216
|
*
|
|
217
|
-
* @param value - True to enable incremental mode, false to disable
|
|
217
|
+
* @param value - True to enable incremental mode, false to disable. Defaults to true.
|
|
218
218
|
*/
|
|
219
|
-
setIncremental(value
|
|
219
|
+
setIncremental(value?: boolean): void;
|
|
220
220
|
/**
|
|
221
221
|
* Checks if the document is in incremental mode.
|
|
222
222
|
*
|
|
@@ -274,4 +274,10 @@ export declare class PdfDocument extends PdfObject {
|
|
|
274
274
|
*/
|
|
275
275
|
static fromBytes(input: AsyncIterable<ByteArray> | Iterable<ByteArray>): Promise<PdfDocument>;
|
|
276
276
|
isModified(): boolean;
|
|
277
|
+
/**
|
|
278
|
+
* Verifies all digital signatures in the document.
|
|
279
|
+
*
|
|
280
|
+
* @returns A promise that resolves to the verification result
|
|
281
|
+
*/
|
|
282
|
+
verifySignatures(): Promise<PdfDocumentVerificationResult>;
|
|
277
283
|
}
|
package/dist/pdf/pdf-document.js
CHANGED
|
@@ -148,7 +148,11 @@ export class PdfDocument extends PdfObject {
|
|
|
148
148
|
const revision = this.revisions.find((rev) => rev.xref.offset === lastStartXRef.offset.ref) ??
|
|
149
149
|
this.revisions.find((rev) => rev.xref.offset.equals(lastStartXRef.offset.value));
|
|
150
150
|
if (!revision) {
|
|
151
|
-
throw new Error('Cannot find revision for last StartXRef'
|
|
151
|
+
throw new Error('Cannot find revision for last StartXRef with offset ' +
|
|
152
|
+
`${lastStartXRef.offset.value}. Options are: ` +
|
|
153
|
+
this.revisions
|
|
154
|
+
.map((rev) => rev.xref.offset.value)
|
|
155
|
+
.join(', '));
|
|
152
156
|
}
|
|
153
157
|
return revision;
|
|
154
158
|
}
|
|
@@ -497,9 +501,9 @@ export class PdfDocument extends PdfObject {
|
|
|
497
501
|
* Sets whether the document should use incremental updates.
|
|
498
502
|
* When true, locks all existing revisions to preserve original content.
|
|
499
503
|
*
|
|
500
|
-
* @param value - True to enable incremental mode, false to disable
|
|
504
|
+
* @param value - True to enable incremental mode, false to disable. Defaults to true.
|
|
501
505
|
*/
|
|
502
|
-
setIncremental(value) {
|
|
506
|
+
setIncremental(value = true) {
|
|
503
507
|
for (const revision of this.revisions) {
|
|
504
508
|
revision.locked = value;
|
|
505
509
|
}
|
|
@@ -685,4 +689,12 @@ export class PdfDocument extends PdfObject {
|
|
|
685
689
|
isModified() {
|
|
686
690
|
return (super.isModified() || this.revisions.some((rev) => rev.isModified()));
|
|
687
691
|
}
|
|
692
|
+
/**
|
|
693
|
+
* Verifies all digital signatures in the document.
|
|
694
|
+
*
|
|
695
|
+
* @returns A promise that resolves to the verification result
|
|
696
|
+
*/
|
|
697
|
+
async verifySignatures() {
|
|
698
|
+
return await this.signer.verify(this);
|
|
699
|
+
}
|
|
688
700
|
}
|
package/dist/signing/index.d.ts
CHANGED
package/dist/signing/index.js
CHANGED
|
@@ -54,4 +54,11 @@ export declare class PdfAdbePkcs7DetachedSignatureObject extends PdfSignatureObj
|
|
|
54
54
|
* @returns The CMS SignedData and revocation information.
|
|
55
55
|
*/
|
|
56
56
|
sign: PdfSignatureObject['sign'];
|
|
57
|
+
/**
|
|
58
|
+
* Verifies the signature against the provided document bytes.
|
|
59
|
+
*
|
|
60
|
+
* @param options - Verification options including the signed bytes.
|
|
61
|
+
* @returns The verification result.
|
|
62
|
+
*/
|
|
63
|
+
verify: PdfSignatureObject['verify'];
|
|
57
64
|
}
|
|
@@ -114,4 +114,40 @@ export class PdfAdbePkcs7DetachedSignatureObject extends PdfSignatureObject {
|
|
|
114
114
|
revocationInfo,
|
|
115
115
|
};
|
|
116
116
|
};
|
|
117
|
+
/**
|
|
118
|
+
* Verifies the signature against the provided document bytes.
|
|
119
|
+
*
|
|
120
|
+
* @param options - Verification options including the signed bytes.
|
|
121
|
+
* @returns The verification result.
|
|
122
|
+
*/
|
|
123
|
+
verify = async (options) => {
|
|
124
|
+
const { bytes, certificateValidation } = options;
|
|
125
|
+
try {
|
|
126
|
+
const signedData = SignedData.fromCms(this.signedBytes);
|
|
127
|
+
const certValidationOptions = certificateValidation === true
|
|
128
|
+
? {}
|
|
129
|
+
: certificateValidation || undefined;
|
|
130
|
+
const result = await signedData.verify({
|
|
131
|
+
data: bytes,
|
|
132
|
+
certificateValidation: certValidationOptions,
|
|
133
|
+
});
|
|
134
|
+
if (result.valid) {
|
|
135
|
+
return { valid: true };
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
return {
|
|
139
|
+
valid: false,
|
|
140
|
+
reasons: result.reasons,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
return {
|
|
146
|
+
valid: false,
|
|
147
|
+
reasons: [
|
|
148
|
+
`Failed to verify signature: ${error instanceof Error ? error.message : String(error)}`,
|
|
149
|
+
],
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
};
|
|
117
153
|
}
|
|
@@ -52,4 +52,11 @@ export declare class PdfAdbePkcs7Sha1SignatureObject extends PdfSignatureObject
|
|
|
52
52
|
* @returns The CMS SignedData and revocation information.
|
|
53
53
|
*/
|
|
54
54
|
sign: PdfSignatureObject['sign'];
|
|
55
|
+
/**
|
|
56
|
+
* Verifies the signature against the provided document bytes.
|
|
57
|
+
*
|
|
58
|
+
* @param options - Verification options including the signed bytes.
|
|
59
|
+
* @returns The verification result.
|
|
60
|
+
*/
|
|
61
|
+
verify: PdfSignatureObject['verify'];
|
|
55
62
|
}
|
|
@@ -118,4 +118,58 @@ export class PdfAdbePkcs7Sha1SignatureObject extends PdfSignatureObject {
|
|
|
118
118
|
revocationInfo,
|
|
119
119
|
};
|
|
120
120
|
};
|
|
121
|
+
/**
|
|
122
|
+
* Verifies the signature against the provided document bytes.
|
|
123
|
+
*
|
|
124
|
+
* @param options - Verification options including the signed bytes.
|
|
125
|
+
* @returns The verification result.
|
|
126
|
+
*/
|
|
127
|
+
verify = async (options) => {
|
|
128
|
+
const { bytes, certificateValidation } = options;
|
|
129
|
+
try {
|
|
130
|
+
const signedData = SignedData.fromCms(this.signedBytes);
|
|
131
|
+
// For adbe.pkcs7.sha1, the signed content is the SHA-1 hash of the document
|
|
132
|
+
// We need to compute the SHA-1 hash of the data and compare with the embedded content
|
|
133
|
+
const expectedHash = await DigestAlgorithmIdentifier.digestAlgorithm('SHA-1').digest(bytes);
|
|
134
|
+
const certValidationOptions = certificateValidation === true
|
|
135
|
+
? {}
|
|
136
|
+
: certificateValidation || undefined;
|
|
137
|
+
// Verify the signature with the hash as the data (non-detached mode)
|
|
138
|
+
const result = await signedData.verify({
|
|
139
|
+
certificateValidation: certValidationOptions,
|
|
140
|
+
});
|
|
141
|
+
if (!result.valid) {
|
|
142
|
+
return {
|
|
143
|
+
valid: false,
|
|
144
|
+
reasons: result.reasons,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
// Additionally verify that the embedded hash matches the document hash
|
|
148
|
+
const embeddedContent = signedData.encapContentInfo.eContent;
|
|
149
|
+
if (!embeddedContent) {
|
|
150
|
+
return {
|
|
151
|
+
valid: false,
|
|
152
|
+
reasons: ['No embedded content in SignedData'],
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
// Compare the hashes
|
|
156
|
+
if (!this.compareArrays(embeddedContent, expectedHash)) {
|
|
157
|
+
return {
|
|
158
|
+
valid: false,
|
|
159
|
+
reasons: [
|
|
160
|
+
'Document hash does not match embedded signature hash',
|
|
161
|
+
],
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return { valid: true };
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
return {
|
|
168
|
+
valid: false,
|
|
169
|
+
reasons: [
|
|
170
|
+
`Failed to verify signature: ${error instanceof Error ? error.message : String(error)}`,
|
|
171
|
+
],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
};
|
|
121
175
|
}
|
|
@@ -46,4 +46,11 @@ export declare class PdfAdbePkcsX509RsaSha1SignatureObject extends PdfSignatureO
|
|
|
46
46
|
* @returns The signature bytes and revocation information.
|
|
47
47
|
*/
|
|
48
48
|
sign: PdfSignatureObject['sign'];
|
|
49
|
+
/**
|
|
50
|
+
* Verifies the signature against the provided document bytes.
|
|
51
|
+
*
|
|
52
|
+
* @param options - Verification options including the signed bytes.
|
|
53
|
+
* @returns The verification result.
|
|
54
|
+
*/
|
|
55
|
+
verify: PdfSignatureObject['verify'];
|
|
49
56
|
}
|
|
@@ -2,7 +2,10 @@ import { PrivateKeyInfo } from 'pki-lite/keys/PrivateKeyInfo';
|
|
|
2
2
|
import { PdfSignatureObject } from './base';
|
|
3
3
|
import { OctetString } from 'pki-lite/asn1/OctetString';
|
|
4
4
|
import { AlgorithmIdentifier } from 'pki-lite/algorithms/AlgorithmIdentifier';
|
|
5
|
+
import { Certificate } from 'pki-lite/x509/Certificate';
|
|
5
6
|
import { fetchRevocationInfo } from '../utils';
|
|
7
|
+
import { PdfArray } from '../../core/objects/pdf-array';
|
|
8
|
+
import { PdfHexadecimal } from '../../core/objects/pdf-hexadecimal';
|
|
6
9
|
/**
|
|
7
10
|
* X.509 RSA-SHA1 signature object (adbe.x509.rsa_sha1).
|
|
8
11
|
* Creates a raw RSA-SHA1 signature with certificates in the Cert entry.
|
|
@@ -78,4 +81,78 @@ export class PdfAdbePkcsX509RsaSha1SignatureObject extends PdfSignatureObject {
|
|
|
78
81
|
revocationInfo,
|
|
79
82
|
};
|
|
80
83
|
};
|
|
84
|
+
/**
|
|
85
|
+
* Verifies the signature against the provided document bytes.
|
|
86
|
+
*
|
|
87
|
+
* @param options - Verification options including the signed bytes.
|
|
88
|
+
* @returns The verification result.
|
|
89
|
+
*/
|
|
90
|
+
verify = async (options) => {
|
|
91
|
+
const { bytes } = options;
|
|
92
|
+
try {
|
|
93
|
+
const certificates = [];
|
|
94
|
+
const Cert = this.content.get('Cert');
|
|
95
|
+
if (Cert instanceof PdfArray) {
|
|
96
|
+
for (const certObj of Cert.items) {
|
|
97
|
+
const certBytes = certObj instanceof PdfHexadecimal
|
|
98
|
+
? certObj.bytes
|
|
99
|
+
: certObj.raw;
|
|
100
|
+
const certificate = Certificate.fromDer(certBytes);
|
|
101
|
+
certificates.push(certificate);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else if (Cert instanceof PdfHexadecimal) {
|
|
105
|
+
const certificate = Certificate.fromDer(Cert.bytes);
|
|
106
|
+
certificates.push(certificate);
|
|
107
|
+
}
|
|
108
|
+
else if (Cert) {
|
|
109
|
+
const certificate = Certificate.fromDer(Cert.raw);
|
|
110
|
+
certificates.push(certificate);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
throw new Error('No Cert entry found in signature dictionary');
|
|
114
|
+
}
|
|
115
|
+
if (certificates.length === 0) {
|
|
116
|
+
return {
|
|
117
|
+
valid: false,
|
|
118
|
+
reasons: [
|
|
119
|
+
'No certificates available for adbe.x509.rsa_sha1 verification',
|
|
120
|
+
],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
// Parse the signature as an OctetString
|
|
124
|
+
const signatureOctetString = OctetString.fromDer(this.signedBytes);
|
|
125
|
+
const signatureValue = signatureOctetString.bytes;
|
|
126
|
+
const signatureAlgorithm = AlgorithmIdentifier.signatureAlgorithm(PdfAdbePkcsX509RsaSha1SignatureObject.ALGORITHM);
|
|
127
|
+
for (const cert of certificates) {
|
|
128
|
+
const isValid = await signatureAlgorithm.verify(bytes, signatureValue, cert.tbsCertificate.subjectPublicKeyInfo);
|
|
129
|
+
if (isValid) {
|
|
130
|
+
if (options.certificateValidation === true) {
|
|
131
|
+
await cert.validate({
|
|
132
|
+
//TODO: implement default validation options
|
|
133
|
+
checkSignature: true,
|
|
134
|
+
validateChain: true,
|
|
135
|
+
otherCertificates: certificates,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
else if (options.certificateValidation) {
|
|
139
|
+
await cert.validate(options.certificateValidation);
|
|
140
|
+
}
|
|
141
|
+
return { valid: true };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
valid: false,
|
|
146
|
+
reasons: ['Signature verification failed for all certificates'],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
return {
|
|
151
|
+
valid: false,
|
|
152
|
+
reasons: [
|
|
153
|
+
`Failed to verify signature: ${error instanceof Error ? error.message : String(error)}`,
|
|
154
|
+
],
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
};
|
|
81
158
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PdfDictionary } from '../../core/objects/pdf-dictionary';
|
|
2
|
-
import { PdfSignatureDictionaryEntries, PdfSignatureSubType, RevocationInfo } from '../types';
|
|
2
|
+
import { PdfSignatureDictionaryEntries, PdfSignatureSubType, PdfSignatureVerificationOptions, PdfSignatureVerificationResult, RevocationInfo } from '../types';
|
|
3
3
|
import { PdfHexadecimal } from '../../core/objects/pdf-hexadecimal';
|
|
4
4
|
import { PdfIndirectObject } from '../../core/objects/pdf-indirect-object';
|
|
5
5
|
import { ByteArray } from '../../types';
|
|
@@ -71,6 +71,13 @@ export declare abstract class PdfSignatureObject extends PdfIndirectObject<PdfSi
|
|
|
71
71
|
signedBytes: ByteArray;
|
|
72
72
|
revocationInfo?: RevocationInfo;
|
|
73
73
|
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Verifies the signature against the provided document bytes.
|
|
76
|
+
*
|
|
77
|
+
* @param options - Verification options including the signed bytes.
|
|
78
|
+
* @returns The verification result.
|
|
79
|
+
*/
|
|
80
|
+
abstract verify(options: PdfSignatureVerificationOptions): Promise<PdfSignatureVerificationResult>;
|
|
74
81
|
/**
|
|
75
82
|
* Gets the signature hexadecimal content.
|
|
76
83
|
*
|
|
@@ -104,4 +111,12 @@ export declare abstract class PdfSignatureObject extends PdfIndirectObject<PdfSi
|
|
|
104
111
|
* @returns High order value to place signature near end of document.
|
|
105
112
|
*/
|
|
106
113
|
order(): number;
|
|
114
|
+
/**
|
|
115
|
+
* Compares two byte arrays for equality.
|
|
116
|
+
*
|
|
117
|
+
* @param a - First byte array.
|
|
118
|
+
* @param b - Second byte array.
|
|
119
|
+
* @returns True if arrays are equal, false otherwise.
|
|
120
|
+
*/
|
|
121
|
+
protected compareArrays(a: ByteArray, b: ByteArray): boolean;
|
|
107
122
|
}
|
|
@@ -142,4 +142,22 @@ export class PdfSignatureObject extends PdfIndirectObject {
|
|
|
142
142
|
order() {
|
|
143
143
|
return PdfIndirectObject.MAX_ORDER_INDEX - 10;
|
|
144
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Compares two byte arrays for equality.
|
|
147
|
+
*
|
|
148
|
+
* @param a - First byte array.
|
|
149
|
+
* @param b - Second byte array.
|
|
150
|
+
* @returns True if arrays are equal, false otherwise.
|
|
151
|
+
*/
|
|
152
|
+
compareArrays(a, b) {
|
|
153
|
+
if (a.length !== b.length) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
for (let i = 0; i < a.length; i++) {
|
|
157
|
+
if (a[i] !== b[i]) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
145
163
|
}
|
|
@@ -61,4 +61,11 @@ export declare class PdfEtsiCadesDetachedSignatureObject extends PdfSignatureObj
|
|
|
61
61
|
* @returns The CMS SignedData and revocation information.
|
|
62
62
|
*/
|
|
63
63
|
sign: PdfSignatureObject['sign'];
|
|
64
|
+
/**
|
|
65
|
+
* Verifies the signature against the provided document bytes.
|
|
66
|
+
*
|
|
67
|
+
* @param options - Verification options including the signed bytes.
|
|
68
|
+
* @returns The verification result.
|
|
69
|
+
*/
|
|
70
|
+
verify: PdfSignatureObject['verify'];
|
|
64
71
|
}
|
|
@@ -156,4 +156,40 @@ export class PdfEtsiCadesDetachedSignatureObject extends PdfSignatureObject {
|
|
|
156
156
|
revocationInfo,
|
|
157
157
|
};
|
|
158
158
|
};
|
|
159
|
+
/**
|
|
160
|
+
* Verifies the signature against the provided document bytes.
|
|
161
|
+
*
|
|
162
|
+
* @param options - Verification options including the signed bytes.
|
|
163
|
+
* @returns The verification result.
|
|
164
|
+
*/
|
|
165
|
+
verify = async (options) => {
|
|
166
|
+
const { bytes, certificateValidation } = options;
|
|
167
|
+
try {
|
|
168
|
+
const signedData = SignedData.fromCms(this.signedBytes);
|
|
169
|
+
const certValidationOptions = certificateValidation === true
|
|
170
|
+
? {}
|
|
171
|
+
: certificateValidation || undefined;
|
|
172
|
+
const result = await signedData.verify({
|
|
173
|
+
data: bytes,
|
|
174
|
+
certificateValidation: certValidationOptions,
|
|
175
|
+
});
|
|
176
|
+
if (result.valid) {
|
|
177
|
+
return { valid: true };
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
return {
|
|
181
|
+
valid: false,
|
|
182
|
+
reasons: result.reasons,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
return {
|
|
188
|
+
valid: false,
|
|
189
|
+
reasons: [
|
|
190
|
+
`Failed to verify signature: ${error instanceof Error ? error.message : String(error)}`,
|
|
191
|
+
],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
};
|
|
159
195
|
}
|
|
@@ -34,4 +34,11 @@ export declare class PdfEtsiRfc3161SignatureObject extends PdfSignatureObject {
|
|
|
34
34
|
* @throws Error if no timestamp token is received.
|
|
35
35
|
*/
|
|
36
36
|
sign: PdfSignatureObject['sign'];
|
|
37
|
+
/**
|
|
38
|
+
* Verifies the timestamp signature against the provided document bytes.
|
|
39
|
+
*
|
|
40
|
+
* @param options - Verification options including the signed bytes.
|
|
41
|
+
* @returns The verification result.
|
|
42
|
+
*/
|
|
43
|
+
verify: PdfSignatureObject['verify'];
|
|
37
44
|
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { PdfSignatureObject } from './base';
|
|
2
2
|
import { DigestAlgorithmIdentifier } from 'pki-lite/algorithms/AlgorithmIdentifier';
|
|
3
3
|
import { TimeStampReq } from 'pki-lite/timestamp/TimeStampReq';
|
|
4
|
+
import { TSTInfo } from 'pki-lite/timestamp/TSTInfo';
|
|
4
5
|
import { MessageImprint } from 'pki-lite/timestamp/MessageImprint';
|
|
5
6
|
import { SignedData } from 'pki-lite/pkcs7/SignedData';
|
|
6
7
|
import { fetchRevocationInfo } from '../utils';
|
|
8
|
+
import { Certificate } from 'pki-lite/x509/Certificate';
|
|
9
|
+
import { OIDs } from 'pki-lite/core/OIDs';
|
|
7
10
|
/**
|
|
8
11
|
* RFC 3161 timestamp signature object (ETSI.RFC3161).
|
|
9
12
|
* Creates document timestamps using a Time Stamp Authority (TSA).
|
|
@@ -67,4 +70,103 @@ export class PdfEtsiRfc3161SignatureObject extends PdfSignatureObject {
|
|
|
67
70
|
revocationInfo,
|
|
68
71
|
};
|
|
69
72
|
};
|
|
73
|
+
/**
|
|
74
|
+
* Verifies the timestamp signature against the provided document bytes.
|
|
75
|
+
*
|
|
76
|
+
* @param options - Verification options including the signed bytes.
|
|
77
|
+
* @returns The verification result.
|
|
78
|
+
*/
|
|
79
|
+
verify = async (options) => {
|
|
80
|
+
const { bytes, certificateValidation } = options;
|
|
81
|
+
const digestAlgorithm = DigestAlgorithmIdentifier.digestAlgorithm('SHA-512');
|
|
82
|
+
const expectedMessageImprint = new MessageImprint({
|
|
83
|
+
hashAlgorithm: digestAlgorithm,
|
|
84
|
+
hashedMessage: await digestAlgorithm.digest(bytes),
|
|
85
|
+
});
|
|
86
|
+
try {
|
|
87
|
+
const signedData = SignedData.fromCms(this.signedBytes);
|
|
88
|
+
// Extract TSTInfo from the signed data's encapsulated content
|
|
89
|
+
// The eContentType should be id-ct-TSTInfo (1.2.840.113549.1.9.16.1.4)
|
|
90
|
+
const encapContentInfo = signedData.encapContentInfo;
|
|
91
|
+
if (encapContentInfo.eContentType.toString() !== OIDs.PKCS7.TST_INFO) {
|
|
92
|
+
return {
|
|
93
|
+
valid: false,
|
|
94
|
+
reasons: [
|
|
95
|
+
`Invalid content type: expected id-ct-TSTInfo (${OIDs.PKCS7.TST_INFO}), got ${encapContentInfo.eContentType.toString()}`,
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (!encapContentInfo.eContent) {
|
|
100
|
+
return {
|
|
101
|
+
valid: false,
|
|
102
|
+
reasons: ['No TSTInfo content found in timestamp token'],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
// Parse the TSTInfo from the encapsulated content
|
|
106
|
+
const tstInfo = TSTInfo.fromDer(encapContentInfo.eContent);
|
|
107
|
+
// Verify that the messageImprint in TSTInfo matches what we computed
|
|
108
|
+
const tstMessageImprint = tstInfo.messageImprint;
|
|
109
|
+
if (tstMessageImprint.hashAlgorithm.algorithm.toString() !==
|
|
110
|
+
expectedMessageImprint.hashAlgorithm.algorithm.toString()) {
|
|
111
|
+
return {
|
|
112
|
+
valid: false,
|
|
113
|
+
reasons: [
|
|
114
|
+
`Hash algorithm mismatch: TSTInfo uses ${tstMessageImprint.hashAlgorithm.algorithm.toString()}, expected ${expectedMessageImprint.hashAlgorithm.algorithm.toString()}`,
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const tstHash = tstMessageImprint.hashedMessage;
|
|
119
|
+
const expectedHash = expectedMessageImprint.hashedMessage;
|
|
120
|
+
if (tstHash.length !== expectedHash.length) {
|
|
121
|
+
return {
|
|
122
|
+
valid: false,
|
|
123
|
+
reasons: [
|
|
124
|
+
`Hash length mismatch: TSTInfo has ${tstHash.length} bytes, expected ${expectedHash.length} bytes`,
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
for (let i = 0; i < tstHash.length; i++) {
|
|
129
|
+
if (tstHash[i] !== expectedHash[i]) {
|
|
130
|
+
return {
|
|
131
|
+
valid: false,
|
|
132
|
+
reasons: [
|
|
133
|
+
'Message imprint mismatch: the timestamp does not match the document',
|
|
134
|
+
],
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Verify the signature on the SignedData
|
|
139
|
+
const certValidationOptions = certificateValidation === true
|
|
140
|
+
? {}
|
|
141
|
+
: certificateValidation || undefined;
|
|
142
|
+
if (options.certificateValidation) {
|
|
143
|
+
const certificates = signedData.certificates ?? [];
|
|
144
|
+
for (const cert of certificates) {
|
|
145
|
+
if (!(cert instanceof Certificate)) {
|
|
146
|
+
//TODO: support other cert types
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (!(await cert.validate(certValidationOptions))) {
|
|
150
|
+
return {
|
|
151
|
+
valid: false,
|
|
152
|
+
reasons: [
|
|
153
|
+
'Certificate validation failed for timestamp signer',
|
|
154
|
+
],
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
valid: true,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
return {
|
|
165
|
+
valid: false,
|
|
166
|
+
reasons: [
|
|
167
|
+
`Failed to verify signature: ${error instanceof Error ? error.message : String(error)}`,
|
|
168
|
+
],
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
};
|
|
70
172
|
}
|
package/dist/signing/signer.d.ts
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
import { PdfDocument } from '../pdf/pdf-document';
|
|
2
|
+
import { PdfSignatureObject } from './signatures';
|
|
3
|
+
import { PdfSignatureVerificationResult, CertificateValidationOptions, PdfSignatureSubType } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Result of verifying all signatures in a document.
|
|
6
|
+
*/
|
|
7
|
+
export type PdfDocumentVerificationResult = {
|
|
8
|
+
/** Whether all signatures in the document are valid. */
|
|
9
|
+
valid: boolean;
|
|
10
|
+
/** Individual signature verification results. */
|
|
11
|
+
signatures: {
|
|
12
|
+
/** The signature subfilter type. */
|
|
13
|
+
type: PdfSignatureSubType;
|
|
14
|
+
/** Index of the signature in the document. */
|
|
15
|
+
index: number;
|
|
16
|
+
/** The signature object. */
|
|
17
|
+
signature: PdfSignatureObject;
|
|
18
|
+
/** The verification result. */
|
|
19
|
+
result: PdfSignatureVerificationResult;
|
|
20
|
+
}[];
|
|
21
|
+
};
|
|
2
22
|
/**
|
|
3
23
|
* Handles digital signing operations for PDF documents.
|
|
4
24
|
* Processes signature objects and optionally stores revocation information in the DSS.
|
|
@@ -20,4 +40,39 @@ export declare class PdfSigner {
|
|
|
20
40
|
* @returns The signed document.
|
|
21
41
|
*/
|
|
22
42
|
sign(document: PdfDocument): Promise<PdfDocument>;
|
|
43
|
+
/**
|
|
44
|
+
* Instantiates the appropriate signature object based on SubFilter type.
|
|
45
|
+
*
|
|
46
|
+
* @param signatureDict - The signature dictionary.
|
|
47
|
+
* @returns A properly typed PdfSignatureObject subclass.
|
|
48
|
+
*/
|
|
49
|
+
private instantiateSignatureObject;
|
|
50
|
+
/**
|
|
51
|
+
* Verifies all signatures in the document.
|
|
52
|
+
* First serializes the document to bytes and reloads it to ensure signatures
|
|
53
|
+
* are properly deserialized into the correct classes before verification.
|
|
54
|
+
* Then searches for signature objects, computes their byte ranges, and verifies each one.
|
|
55
|
+
*
|
|
56
|
+
* @param document - The PDF document to verify.
|
|
57
|
+
* @param options - Optional verification options.
|
|
58
|
+
* @returns The verification result for all signatures.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const signer = new PdfSigner()
|
|
63
|
+
* const result = await signer.verify(document)
|
|
64
|
+
* if (result.valid) {
|
|
65
|
+
* console.log('All signatures are valid')
|
|
66
|
+
* } else {
|
|
67
|
+
* result.signatures.forEach(sig => {
|
|
68
|
+
* if (!sig.result.valid) {
|
|
69
|
+
* console.log(`Signature ${sig.index} invalid:`, sig.result.reasons)
|
|
70
|
+
* }
|
|
71
|
+
* })
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
verify(document: PdfDocument, options?: {
|
|
76
|
+
certificateValidation?: CertificateValidationOptions | boolean;
|
|
77
|
+
}): Promise<PdfDocumentVerificationResult>;
|
|
23
78
|
}
|
package/dist/signing/signer.js
CHANGED
|
@@ -3,7 +3,11 @@ import { PdfHexadecimalToken } from '../core/tokens/hexadecimal-token';
|
|
|
3
3
|
import { PdfNameToken } from '../core/tokens/name-token';
|
|
4
4
|
import { concatUint8Arrays } from '../utils/concatUint8Arrays';
|
|
5
5
|
import { PdfDocumentSecurityStoreObject } from './document-security-store';
|
|
6
|
-
import { PdfSignatureObject } from './signatures';
|
|
6
|
+
import { PdfSignatureObject, PdfAdbePkcs7DetachedSignatureObject, PdfAdbePkcs7Sha1SignatureObject, PdfAdbePkcsX509RsaSha1SignatureObject, PdfEtsiCadesDetachedSignatureObject, PdfEtsiRfc3161SignatureObject, PdfSignatureDictionary, } from './signatures';
|
|
7
|
+
import { PdfNumber } from '../core/objects/pdf-number';
|
|
8
|
+
import { PdfIndirectObject } from '../core/objects/pdf-indirect-object';
|
|
9
|
+
import { PdfDictionary } from '../core/objects/pdf-dictionary';
|
|
10
|
+
import { PdfArray } from '../core/objects/pdf-array';
|
|
7
11
|
/**
|
|
8
12
|
* Handles digital signing operations for PDF documents.
|
|
9
13
|
* Processes signature objects and optionally stores revocation information in the DSS.
|
|
@@ -89,4 +93,195 @@ export class PdfSigner {
|
|
|
89
93
|
}
|
|
90
94
|
return document;
|
|
91
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Instantiates the appropriate signature object based on SubFilter type.
|
|
98
|
+
*
|
|
99
|
+
* @param signatureDict - The signature dictionary.
|
|
100
|
+
* @returns A properly typed PdfSignatureObject subclass.
|
|
101
|
+
*/
|
|
102
|
+
instantiateSignatureObject(signatureDict) {
|
|
103
|
+
const content = signatureDict.content;
|
|
104
|
+
const subFilter = content.get('SubFilter').value;
|
|
105
|
+
// Create a PdfSignatureDictionary wrapper
|
|
106
|
+
const sigDict = new PdfSignatureDictionary({
|
|
107
|
+
Type: content.get('Type'),
|
|
108
|
+
Filter: content.get('Filter'),
|
|
109
|
+
SubFilter: content.get('SubFilter'),
|
|
110
|
+
Reason: content.get('Reason'),
|
|
111
|
+
M: content.get('M'),
|
|
112
|
+
Name: content.get('Name'),
|
|
113
|
+
Reference: content.get('Reference'),
|
|
114
|
+
ContactInfo: content.get('ContactInfo'),
|
|
115
|
+
Location: content.get('Location'),
|
|
116
|
+
Cert: content.get('Cert'),
|
|
117
|
+
ByteRange: content.get('ByteRange'),
|
|
118
|
+
Contents: content.get('Contents'),
|
|
119
|
+
});
|
|
120
|
+
// Instantiate the appropriate signature type based on SubFilter
|
|
121
|
+
let signatureObj;
|
|
122
|
+
switch (subFilter) {
|
|
123
|
+
case 'adbe.pkcs7.detached':
|
|
124
|
+
signatureObj = new PdfAdbePkcs7DetachedSignatureObject({
|
|
125
|
+
privateKey: new Uint8Array(),
|
|
126
|
+
certificate: new Uint8Array(),
|
|
127
|
+
});
|
|
128
|
+
break;
|
|
129
|
+
case 'adbe.pkcs7.sha1':
|
|
130
|
+
signatureObj = new PdfAdbePkcs7Sha1SignatureObject({
|
|
131
|
+
privateKey: new Uint8Array(),
|
|
132
|
+
certificate: new Uint8Array(),
|
|
133
|
+
});
|
|
134
|
+
break;
|
|
135
|
+
case 'adbe.x509.rsa_sha1':
|
|
136
|
+
signatureObj = new PdfAdbePkcsX509RsaSha1SignatureObject({
|
|
137
|
+
privateKey: new Uint8Array(),
|
|
138
|
+
certificate: new Uint8Array(),
|
|
139
|
+
});
|
|
140
|
+
break;
|
|
141
|
+
case 'ETSI.CAdES.detached':
|
|
142
|
+
signatureObj = new PdfEtsiCadesDetachedSignatureObject({
|
|
143
|
+
privateKey: new Uint8Array(),
|
|
144
|
+
certificate: new Uint8Array(),
|
|
145
|
+
});
|
|
146
|
+
break;
|
|
147
|
+
case 'ETSI.RFC3161':
|
|
148
|
+
signatureObj = new PdfEtsiRfc3161SignatureObject({
|
|
149
|
+
timeStampAuthority: {
|
|
150
|
+
url: '',
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
break;
|
|
154
|
+
default:
|
|
155
|
+
throw new Error(`Unsupported signature SubFilter type: ${subFilter}`);
|
|
156
|
+
}
|
|
157
|
+
// Replace the content with the actual signature dictionary
|
|
158
|
+
signatureObj.content = sigDict;
|
|
159
|
+
signatureObj.objectNumber = signatureDict.objectNumber;
|
|
160
|
+
signatureObj.generationNumber = signatureDict.generationNumber;
|
|
161
|
+
return signatureObj;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Verifies all signatures in the document.
|
|
165
|
+
* First serializes the document to bytes and reloads it to ensure signatures
|
|
166
|
+
* are properly deserialized into the correct classes before verification.
|
|
167
|
+
* Then searches for signature objects, computes their byte ranges, and verifies each one.
|
|
168
|
+
*
|
|
169
|
+
* @param document - The PDF document to verify.
|
|
170
|
+
* @param options - Optional verification options.
|
|
171
|
+
* @returns The verification result for all signatures.
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```typescript
|
|
175
|
+
* const signer = new PdfSigner()
|
|
176
|
+
* const result = await signer.verify(document)
|
|
177
|
+
* if (result.valid) {
|
|
178
|
+
* console.log('All signatures are valid')
|
|
179
|
+
* } else {
|
|
180
|
+
* result.signatures.forEach(sig => {
|
|
181
|
+
* if (!sig.result.valid) {
|
|
182
|
+
* console.log(`Signature ${sig.index} invalid:`, sig.result.reasons)
|
|
183
|
+
* }
|
|
184
|
+
* })
|
|
185
|
+
* }
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
async verify(document, options) {
|
|
189
|
+
const documentBytes = document.toBytes();
|
|
190
|
+
const results = [];
|
|
191
|
+
let allValid = true;
|
|
192
|
+
const documentObjects = document.objects;
|
|
193
|
+
for (let i = 0; i < documentObjects.length; i++) {
|
|
194
|
+
const obj = documentObjects[i];
|
|
195
|
+
if (!(obj instanceof PdfIndirectObject)) {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (!(obj.content instanceof PdfDictionary)) {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
// Check if this is a signature dictionary by looking for Type = /Sig or SubFilter
|
|
202
|
+
const typeEntry = obj.content.get('Type');
|
|
203
|
+
const subFilterEntry = obj.content.get('SubFilter');
|
|
204
|
+
// A signature can be identified by Type=/Sig or by having a SubFilter entry
|
|
205
|
+
const isSignature = (typeEntry && typeEntry.toString() === '/Sig') ||
|
|
206
|
+
(subFilterEntry &&
|
|
207
|
+
subFilterEntry.toString().startsWith('/adbe.')) ||
|
|
208
|
+
subFilterEntry?.toString().startsWith('/ETSI.');
|
|
209
|
+
if (!isSignature) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
const signatureDict = obj;
|
|
213
|
+
const subFilter = signatureDict.content.get('SubFilter').value;
|
|
214
|
+
// Instantiate the correct signature type
|
|
215
|
+
const signature = this.instantiateSignatureObject(signatureDict);
|
|
216
|
+
// Get the ByteRange from the signature dictionary
|
|
217
|
+
const byteRangeEntry = signatureDict.content.get('ByteRange');
|
|
218
|
+
if (!byteRangeEntry) {
|
|
219
|
+
results.push({
|
|
220
|
+
type: subFilter,
|
|
221
|
+
index: i,
|
|
222
|
+
signature,
|
|
223
|
+
result: {
|
|
224
|
+
valid: false,
|
|
225
|
+
reasons: ['Signature is missing ByteRange entry'],
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
allValid = false;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
// Extract the byte range values
|
|
232
|
+
const byteRangeArray = byteRangeEntry.as(PdfArray);
|
|
233
|
+
if (!byteRangeArray) {
|
|
234
|
+
results.push({
|
|
235
|
+
type: subFilter,
|
|
236
|
+
index: i,
|
|
237
|
+
signature,
|
|
238
|
+
result: {
|
|
239
|
+
valid: false,
|
|
240
|
+
reasons: ['ByteRange is not an array'],
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
allValid = false;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const byteRange = byteRangeArray.items.map((item) => {
|
|
247
|
+
if (item instanceof PdfNumber) {
|
|
248
|
+
return item.value;
|
|
249
|
+
}
|
|
250
|
+
return 0;
|
|
251
|
+
});
|
|
252
|
+
if (byteRange.length !== 4) {
|
|
253
|
+
results.push({
|
|
254
|
+
type: subFilter,
|
|
255
|
+
index: i,
|
|
256
|
+
signature,
|
|
257
|
+
result: {
|
|
258
|
+
valid: false,
|
|
259
|
+
reasons: ['Invalid ByteRange format'],
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
allValid = false;
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
// Compute the bytes that were signed (excluding the signature contents)
|
|
266
|
+
const signedBytes = concatUint8Arrays(documentBytes.slice(byteRange[0], byteRange[0] + byteRange[1]), documentBytes.slice(byteRange[2], byteRange[2] + byteRange[3]));
|
|
267
|
+
// Verify the signature
|
|
268
|
+
const result = await signature.verify({
|
|
269
|
+
bytes: signedBytes,
|
|
270
|
+
certificateValidation: options?.certificateValidation,
|
|
271
|
+
});
|
|
272
|
+
results.push({
|
|
273
|
+
type: subFilter,
|
|
274
|
+
index: i,
|
|
275
|
+
signature,
|
|
276
|
+
result,
|
|
277
|
+
});
|
|
278
|
+
if (!result.valid) {
|
|
279
|
+
allValid = false;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
valid: allValid,
|
|
284
|
+
signatures: results,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
92
287
|
}
|
package/dist/signing/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HashAlgorithm } from 'pki-lite/core/crypto/index.js';
|
|
2
|
+
import type { CertificateValidationOptions, CertificateValidationResult, TrustAnchor } from 'pki-lite/core/CertificateValidator.js';
|
|
2
3
|
import { PdfDictionary } from '../core/objects/pdf-dictionary';
|
|
3
4
|
import { PdfName } from '../core/objects/pdf-name';
|
|
4
5
|
import { PdfHexadecimal } from '../core/objects/pdf-hexadecimal';
|
|
@@ -6,6 +7,7 @@ import { PdfArray } from '../core/objects/pdf-array';
|
|
|
6
7
|
import { PdfNumber } from '../core/objects/pdf-number';
|
|
7
8
|
import { PdfString } from '../core/objects/pdf-string';
|
|
8
9
|
import { ByteArray } from '../types';
|
|
10
|
+
export type { CertificateValidationOptions, CertificateValidationResult, TrustAnchor, };
|
|
9
11
|
/**
|
|
10
12
|
* PDF signature subfilter types defining the signature format.
|
|
11
13
|
* - 'adbe.pkcs7.detached': PKCS#7 detached signature
|
|
@@ -76,3 +78,27 @@ export type SignaturePolicyDocument = {
|
|
|
76
78
|
/** Hash algorithm used for the policy document. */
|
|
77
79
|
hashAlgorithm: HashAlgorithm;
|
|
78
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* Result of a PDF signature verification operation.
|
|
83
|
+
*/
|
|
84
|
+
export type PdfSignatureVerificationResult = {
|
|
85
|
+
/** Whether the signature is valid. */
|
|
86
|
+
valid: boolean;
|
|
87
|
+
/** Reasons for verification failure, if any. */
|
|
88
|
+
reasons?: string[];
|
|
89
|
+
/** Certificate validation result, if certificate validation was performed. */
|
|
90
|
+
certificateValidationResult?: CertificateValidationResult;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Options for PDF signature verification.
|
|
94
|
+
*/
|
|
95
|
+
export type PdfSignatureVerificationOptions = {
|
|
96
|
+
/** The original document bytes that were signed. */
|
|
97
|
+
bytes: ByteArray;
|
|
98
|
+
/**
|
|
99
|
+
* Certificate validation options.
|
|
100
|
+
* Pass `true` to use default certificate validation, or provide custom options.
|
|
101
|
+
* Pass `undefined` or `false` to skip certificate validation.
|
|
102
|
+
*/
|
|
103
|
+
certificateValidation?: CertificateValidationOptions | boolean;
|
|
104
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pdf-lite",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
],
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"pako": "2.1.0",
|
|
57
|
-
"pki-lite": "^1.0.
|
|
58
|
-
"pki-lite-crypto-extended": "^1.0.
|
|
57
|
+
"pki-lite": "^1.0.12",
|
|
58
|
+
"pki-lite-crypto-extended": "^1.0.12"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"test:acceptance": "vitest run test/acceptance",
|