nkpdfsign 0.1.1
nkpdfsign: ^0.1.1 copied to clipboard
Pure Dart digital signing of existing PDF documents (PAdES-B-B, adbe.pkcs7.detached) with incremental updates. Server-side, no native dependencies.
nkpdfsign #
Pure Dart digital signing of existing PDF documents — PAdES-B-B level,
/adbe.pkcs7.detached (CMS/PKCS#7) with the full certificate chain embedded.
Runs server-side on the Dart VM (Windows/Linux/macOS) with no Flutter, no
Java, no native dependencies.
Companion package to pkcs12_parser,
which supplies the signing identity from a .p12/.pfx file.
Features #
- Signs arbitrary existing PDFs — not just documents this library created.
- Reads classic xref tables and cross-reference streams (PDF 1.5+),
/Prevchains, hybrid-reference files (/XRefStm), and object streams (/ObjStm). - Appends a proper incremental update in the same xref style as the original file, so prior revisions — including existing signatures — stay byte-for-byte intact. Signing twice yields two valid signatures.
- CMS
SignedDataper RFC 5652: SHA-256, RSA PKCS#1 v1.5, DER-sorted signed attributes (contentType,messageDigest), certificate chain included, nosigningTimeattribute (PAdES uses the dictionary's/Minstead). - Invisible signature field (zero rect) wired into
/AcroFormand the first page's/Annots. - Typed exceptions — the library refuses to emit a plausibly-broken file (encrypted input, DocMDP P=1 certification, exotic filters, oversized CMS).
Usage #
import 'dart:io';
import 'package:nkpdfsign/nkpdfsign.dart';
void main() {
final p12 = Pkcs12.load(File('identity.p12').readAsBytesSync(), 'password');
final signer = NkPdfSigner(SigningCredentials.fromPkcs12(p12));
final signed = signer.sign(
File('document.pdf').readAsBytesSync(),
reason: 'Approval',
location: 'Zagreb',
signerName: 'Jane Doe',
);
File('document-signed.pdf').writeAsBytesSync(signed);
}
Credentials can also be built directly from a pointycastle RSAPrivateKey
plus DER certificates via the SigningCredentials constructor.
CLI #
dart run nkpdfsign input.pdf identity.p12 password [output.pdf] \
[--reason=...] [--location=...] [--contact=...] [--name=...]
Scope and limitations (v1) #
| Area | Status |
|---|---|
| Signature level | PAdES-B-B (no timestamp, no LTV) |
| SubFilter | /adbe.pkcs7.detached |
| Keys | RSA (2048+). EC planned. |
| Appearance | Invisible only (no visible signature block) |
| Encrypted PDFs | Rejected (EncryptedPdfException) |
| DocMDP P=1 certified docs | Rejected (DocMdpViolationException) |
| Stream filters on xref/ObjStm | FlateDecode (+ TIFF/PNG predictors) only |
| Linearized PDFs | Signed fine; output is no longer linearized (harmless) |
If the CMS exceeds the reserved /Contents space, a
SignatureTooLargeException tells you the exact signatureSizeEstimate to
pass.
Verifying the output #
- Adobe Acrobat Reader: open the signed file → signature panel → "Signature is valid, document has not been modified". (Trust for a test CA must be added manually.)
- openssl: extract
/Contentsand the two ByteRange spans, thenopenssl cms -verify -inform DER -in sig.der -content spans.bin -binary -noverify. - pyHanko:
pyhanko sign validate signed.pdf.
The test suite (dart test) does all of the structural checks and — when an
openssl binary is available — cryptographic cross-verification
automatically.