vbuch / node-signpdf

Simple signing of PDFs in node.
MIT License
688 stars 175 forks source link

Help Needed Visible Signature Using USB Token #153

Closed afsalkriti closed 2 years ago

afsalkriti commented 2 years ago

I am using USB Token for signing the PDF.

I am able to create the invisible signature on the PDF using this Library.

I have gone through most of the posts here to make a visible signature using USB Token.

const graphene = require("graphene-pk11")
const Module = graphene.Module
const forge = require("node-forge")
const pki = forge.pki
const fs = require('fs');
const HELPERS = require("node-signpdf/dist/helpers")
const DEFAULT_BYTE_RANGE_PLACEHOLDER = "**********"
let mod,session
mod = Module.load("C:/Windows/System32/eps2003csp11v2.dll")
mod.initialize()
const slots = mod.getSlots()
const slot = slots.items(0)
const initialized = (slot.flags & graphene.SlotFlag.TOKEN_PRESENT)
if (!initialized) throw new Error("Slot 0 is not initialized")
session = slot.open()
session.login("Abcd1234");

const getPkById=(id) => {
    const keys = session.find({ class: graphene.ObjectClass.PRIVATE_KEY })
    for (let i = 0; i < keys.length; i++) {
        const obj = keys.items(i).toType()
        if (id === obj.id.toString("hex")) return obj
    }
    return null
}

const getCertAndPk=() => {
    const certs = session.find({ class: graphene.ObjectClass.CERTIFICATE })
    for (let i = 0; i < certs.length; i++) {
        const obj = certs.items(i).toType()
        if (obj instanceof graphene.X509Certificate) {
            const val = obj.value, cert = pki.certificateFromAsn1(forge.asn1.fromDer(val.toString("binary")))
            const id = obj.id.toString("hex"), privateKey = getPkById(id)
            return { privateKey: privateKey, certificate: cert } 
        }
    }
    return { privateKey: null, certificate: null }
}

const signpdf = (pdfBuffer) => {
        const { privateKey, certificate } = getCertAndPk()
        if (!certificate) throw new Error("certificate is not found")
        if (!privateKey) throw new Error("privateKey is not found")
        pdfBuffer = HELPERS.plainAddPlaceholder({ pdfBuffer, reason: 'reason' })
        let pdf = HELPERS.removeTrailingNewLine(pdfBuffer)
        const byteRangePlaceholder = [0, `/${DEFAULT_BYTE_RANGE_PLACEHOLDER}`, `/${DEFAULT_BYTE_RANGE_PLACEHOLDER}`, `/${DEFAULT_BYTE_RANGE_PLACEHOLDER}`]
        const byteRangeString = `/ByteRange [${byteRangePlaceholder.join(' ')}]`
        const byteRangePos = pdf.indexOf(byteRangeString)
        if (byteRangePos === -1) throw new Error(`Could not find ByteRange placeholder: ${byteRangeString}`)
        const byteRangeEnd = byteRangePos + byteRangeString.length
        const contentsTagPos = pdf.indexOf('/Contents ', byteRangeEnd)
        const placeholderPos = pdf.indexOf('<', contentsTagPos)
        const placeholderEnd = pdf.indexOf('>', placeholderPos)
        const placeholderLengthWithBrackets = (placeholderEnd + 1) - placeholderPos
        const placeholderLength = placeholderLengthWithBrackets - 2
        const byteRange = [0, 0, 0, 0]
        byteRange[1] = placeholderPos
        byteRange[2] = byteRange[1] + placeholderLengthWithBrackets
        byteRange[3] = pdf.length - byteRange[2]
        let actualByteRange = `/ByteRange [${byteRange.join(' ')}]`
        actualByteRange += ' '.repeat(byteRangeString.length - actualByteRange.length)
        pdf = Buffer.concat([pdf.slice(0, byteRangePos), Buffer.from(actualByteRange), pdf.slice(byteRangeEnd)])
        pdf = Buffer.concat([pdf.slice(0, byteRange[1]), pdf.slice(byteRange[2], byteRange[2] + byteRange[3])])
        const sign = session.createSign("SHA256_RSA_PKCS", privateKey)
        const sigv = sign.once(pdf).toString("binary")
        const p7 = forge.pkcs7.createSignedData()
        p7.content = forge.util.createBuffer(pdf.toString("binary"))
        p7.addCertificate(certificate)
        const signer = {}
        signer.sign = (md, algo) => { return sigv }
        p7.addSigner({ key: signer, certificate: certificate, digestAlgorithm: pki.oids.sha256 })
        p7.sign({ detached: true })
        const raw = forge.asn1.toDer(p7.toAsn1()).getBytes()
        if ((raw.length * 2) > placeholderLength) throw new Error(`Signature exceeds placeholder length: ${raw.length * 2} > ${placeholderLength}`)
        let signature = Buffer.from(raw, "binary").toString("hex")
        signature += Buffer.from(String.fromCharCode(0).repeat((placeholderLength / 2) - raw.length)).toString("hex")
        pdf = Buffer.concat([pdf.slice(0, byteRange[1]), Buffer.from(`<${signature}>`), pdf.slice(byteRange[1])])
        fs.writeFileSync('PDF3.pdf', pdf)
        // return pdf
}

// signpdf(fs.readFileSync('./Invoice.pdf'));

signpdf(fs.readFileSync('./Custom.pdf'));

I am using the above code for signing the PDF.

Any Help is appreciated.

vbuch commented 2 years ago

See #129 and #150