Make it possible to change the signature reservation
This commit is contained in:
parent
2d08100b58
commit
796a9640d3
|
@ -9,8 +9,6 @@ the Cairo library. As such, it currently comes with some restrictions:
|
||||||
overwritten
|
overwritten
|
||||||
* the document may not employ cross-reference streams, or must constitute
|
* the document may not employ cross-reference streams, or must constitute
|
||||||
a hybrid-reference file at least
|
a hybrid-reference file at least
|
||||||
* the signature may take at most 4 kilobytes as a compile-time limit,
|
|
||||||
which should be enough space even for one intermediate certificate
|
|
||||||
|
|
||||||
The signature is attached to the first page and has no appearance.
|
The signature is attached to the first page and has no appearance.
|
||||||
|
|
||||||
|
@ -37,6 +35,10 @@ Usage
|
||||||
|
|
||||||
$ ./pdf-simple-sign document.pdf document.signed.pdf KeyAndCerts.p12 password
|
$ ./pdf-simple-sign document.pdf document.signed.pdf KeyAndCerts.p12 password
|
||||||
|
|
||||||
|
If the signature doesn't fit within the default reservation of 4 kibibytes,
|
||||||
|
you might need to adjust it using the `-r` option, or throw out any unnecessary
|
||||||
|
intermediate certificates.
|
||||||
|
|
||||||
Contributing and Support
|
Contributing and Support
|
||||||
------------------------
|
------------------------
|
||||||
Use https://git.janouch.name/p/pdf-simple-sign to report bugs, request features,
|
Use https://git.janouch.name/p/pdf-simple-sign to report bugs, request features,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018, Přemysl Eric Janouch <p@janouch.name>
|
// Copyright (c) 2018 - 2020, Přemysl Eric Janouch <p@janouch.name>
|
||||||
//
|
//
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
// purpose with or without fee is hereby granted.
|
// purpose with or without fee is hereby granted.
|
||||||
|
@ -20,8 +20,9 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"janouch.name/pdf-simple-sign/pdf"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"janouch.name/pdf-simple-sign/pdf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// #include <unistd.h>
|
// #include <unistd.h>
|
||||||
|
@ -39,10 +40,13 @@ func die(status int, format string, args ...interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func usage() {
|
func usage() {
|
||||||
die(1, "Usage: %s [-h] INPUT-FILENAME OUTPUT-FILENAME "+
|
die(1, "Usage: %s [-h] [-r RESERVATION] INPUT-FILENAME OUTPUT-FILENAME "+
|
||||||
"PKCS12-PATH PKCS12-PASS", os.Args[0])
|
"PKCS12-PATH PKCS12-PASS", os.Args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var reservation = flag.Int(
|
||||||
|
"r", 4096, "signature reservation as a number of bytes")
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Usage = usage
|
flag.Usage = usage
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
@ -51,7 +55,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
inputPath, outputPath := flag.Arg(0), flag.Arg(1)
|
inputPath, outputPath := flag.Arg(0), flag.Arg(1)
|
||||||
pdfDocument, err := ioutil.ReadFile(inputPath)
|
doc, err := ioutil.ReadFile(inputPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
die(1, "%s", err)
|
die(1, "%s", err)
|
||||||
}
|
}
|
||||||
|
@ -63,10 +67,10 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
die(3, "%s", err)
|
die(3, "%s", err)
|
||||||
}
|
}
|
||||||
if pdfDocument, err = pdf.Sign(pdfDocument, key, certs); err != nil {
|
if doc, err = pdf.Sign(doc, key, certs, *reservation); err != nil {
|
||||||
die(4, "error: %s", err)
|
die(4, "error: %s", err)
|
||||||
}
|
}
|
||||||
if err = ioutil.WriteFile(outputPath, pdfDocument, 0666); err != nil {
|
if err = ioutil.WriteFile(outputPath, doc, 0666); err != nil {
|
||||||
die(5, "%s", err)
|
die(5, "%s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
// -------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
using uint = unsigned int;
|
using uint = unsigned int;
|
||||||
|
using ushort = unsigned short;
|
||||||
|
|
||||||
static std::string concatenate(const std::vector<std::string>& v, const std::string& delim) {
|
static std::string concatenate(const std::vector<std::string>& v, const std::string& delim) {
|
||||||
std::string res;
|
std::string res;
|
||||||
|
@ -831,7 +832,7 @@ error:
|
||||||
/// https://www.adobe.com/devnet-docs/acrobatetk/tools/DigSig/Acrobat_DigitalSignatures_in_PDF.pdf
|
/// https://www.adobe.com/devnet-docs/acrobatetk/tools/DigSig/Acrobat_DigitalSignatures_in_PDF.pdf
|
||||||
/// https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf
|
/// https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf
|
||||||
/// https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PPKAppearances.pdf
|
/// https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PPKAppearances.pdf
|
||||||
static std::string pdf_sign(std::string& document) {
|
static std::string pdf_sign(std::string& document, ushort reservation) {
|
||||||
pdf_updater pdf(document);
|
pdf_updater pdf(document);
|
||||||
auto err = pdf.initialize();
|
auto err = pdf.initialize();
|
||||||
if (!err.empty())
|
if (!err.empty())
|
||||||
|
@ -855,7 +856,7 @@ static std::string pdf_sign(std::string& document) {
|
||||||
pdf.document.append((byterange_len = 32 /* fine for a gigabyte */), ' ');
|
pdf.document.append((byterange_len = 32 /* fine for a gigabyte */), ' ');
|
||||||
pdf.document.append("\n /Contents <");
|
pdf.document.append("\n /Contents <");
|
||||||
sign_off = pdf.document.size();
|
sign_off = pdf.document.size();
|
||||||
pdf.document.append((sign_len = 8192 /* certificate, digest, encrypted digest, ... */), '0');
|
pdf.document.append((sign_len = reservation * 2), '0');
|
||||||
pdf.document.append("> >>");
|
pdf.document.append("> >>");
|
||||||
|
|
||||||
// We actually need to exclude the hexstring quotes from signing
|
// We actually need to exclude the hexstring quotes from signing
|
||||||
|
@ -945,15 +946,18 @@ static void die(int status, const char* format, ...) {
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
auto invocation_name = argv[0];
|
auto invocation_name = argv[0];
|
||||||
auto usage = [=]{
|
auto usage = [=]{
|
||||||
die(1, "Usage: %s [-h] INPUT-FILENAME OUTPUT-FILENAME PKCS12-PATH PKCS12-PASS",
|
die(1, "Usage: %s [-h] [-r RESERVATION] INPUT-FILENAME OUTPUT-FILENAME PKCS12-PATH PKCS12-PASS",
|
||||||
invocation_name);
|
invocation_name);
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct option opts[] = {
|
static struct option opts[] = {
|
||||||
{"help", no_argument, 0, 'h'},
|
{"help", no_argument, 0, 'h'},
|
||||||
|
{"reservation", required_argument, 0, 'r'},
|
||||||
{nullptr, 0, 0, 0},
|
{nullptr, 0, 0, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Reserved space in bytes for the certificate, digest, encrypted digest, ...
|
||||||
|
long reservation = 4096;
|
||||||
while (1) {
|
while (1) {
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
auto c = getopt_long(argc, const_cast<char* const*>(argv),
|
auto c = getopt_long(argc, const_cast<char* const*>(argv),
|
||||||
|
@ -961,9 +965,16 @@ int main(int argc, char* argv[]) {
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
char* end = nullptr;
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h': usage(); break;
|
case 'r':
|
||||||
default: usage();
|
errno = 0, reservation = strtol(optarg, &end, 10);
|
||||||
|
if (errno || *end || reservation <= 0 || reservation > USHRT_MAX)
|
||||||
|
die(1, "%s: must be a positive number", optarg);
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -990,7 +1001,7 @@ int main(int argc, char* argv[]) {
|
||||||
die(1, "%s: %s", input_path, strerror(errno));
|
die(1, "%s: %s", input_path, strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto err = pdf_sign(pdf_document);
|
auto err = pdf_sign(pdf_document, ushort(reservation));
|
||||||
if (!err.empty()) {
|
if (!err.empty()) {
|
||||||
die(2, "Error: %s", err.c_str());
|
die(2, "Error: %s", err.c_str());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1115,12 +1115,14 @@ func FillInSignature(document []byte, signOff, signLen int,
|
||||||
// There must be at least one certificate, matching the private key.
|
// There must be at least one certificate, matching the private key.
|
||||||
// The certificates must form a chain.
|
// The certificates must form a chain.
|
||||||
//
|
//
|
||||||
|
// A good default for the reservation is around 4096 (the value is in bytes).
|
||||||
|
//
|
||||||
// The presumption here is that the document is valid and that it doesn't
|
// The presumption here is that the document is valid and that it doesn't
|
||||||
// employ cross-reference streams from PDF 1.5, or at least constitutes
|
// employ cross-reference streams from PDF 1.5, or at least constitutes
|
||||||
// a hybrid-reference file. The results with PDF 2.0 (2017) are currently
|
// a hybrid-reference file. The results with PDF 2.0 (2017) are currently
|
||||||
// unknown as the standard costs money.
|
// unknown as the standard costs money.
|
||||||
func Sign(document []byte,
|
func Sign(document []byte, key crypto.PrivateKey, certs []*x509.Certificate,
|
||||||
key crypto.PrivateKey, certs []*x509.Certificate) ([]byte, error) {
|
reservation int) ([]byte, error) {
|
||||||
pdf, err := NewUpdater(document)
|
pdf, err := NewUpdater(document)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1152,7 +1154,7 @@ func Sign(document []byte,
|
||||||
buf.WriteString("\n /Contents <")
|
buf.WriteString("\n /Contents <")
|
||||||
|
|
||||||
signOff = buf.Len()
|
signOff = buf.Len()
|
||||||
signLen = 8192 // cert, digest, encrypted digest, ...
|
signLen = reservation * 2 // cert, digest, encrypted digest, ...
|
||||||
buf.Write(bytes.Repeat([]byte{'0'}, signLen))
|
buf.Write(bytes.Repeat([]byte{'0'}, signLen))
|
||||||
buf.WriteString("> >>")
|
buf.WriteString("> >>")
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue