From 0d3849a6d8c00573b695eaa0290db29fe70c979b Mon Sep 17 00:00:00 2001 From: Vincent LAURENT Date: Wed, 31 Jul 2024 16:03:41 +0200 Subject: [PATCH 1/9] =?UTF-8?q?M=C3=A9thode=20create=20share=20dans=20le?= =?UTF-8?q?=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.php | 39 ++++++++++++++++++-------------------- lib/PDFSignature.class.php | 16 +++++++++++++++- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/app.php b/app.php index 3d654f2..00742a8 100644 --- a/app.php +++ b/app.php @@ -208,21 +208,24 @@ $f3->route('POST /share', function($f3) { $hash = Web::instance()->slug($_POST['hash']); $sharingFolder = $f3->get('PDF_STORAGE_PATH').$hash; - $f3->set('UPLOADS', $sharingFolder."/"); + $symmetricKey = (isset($_COOKIE[$hash])) ? GPGCryptography::protectSymmetricKey($_COOKIE[$hash]) : null; + if (!is_dir($f3->get('PDF_STORAGE_PATH'))) { $f3->error(500, 'Sharing folder doesn\'t exist'); } if (!is_writable($f3->get('PDF_STORAGE_PATH'))) { $f3->error(500, 'Sharing folder is not writable'); } - mkdir($sharingFolder); - $expireFile = $sharingFolder.".expire"; - file_put_contents($expireFile, $f3->get('POST.duration')); - touch($expireFile, date_format(date_modify(date_create(), file_get_contents($expireFile)), 'U')); + + $pdfSignature = new PDFSignature($sharingFolder, $symmetricKey); + $pdfSignature->createShare($f3->get('POST.duration')); + + $f3->set('UPLOADS', $sharingFolder."/"); + $filename = "original.pdf"; $tmpfile = tempnam($sharingFolder, date('YmdHis')); unlink($tmpfile); - $svgFiles = ""; + $svgFiles = []; $files = Web::instance()->receive(function($file,$formFieldName){ if($formFieldName == "pdf" && strpos(Web::instance()->mime($file['tmp_name'], true), 'application/pdf') !== 0) { $f3->error(403); @@ -238,7 +241,7 @@ $f3->route('POST /share', return $filename; } if($formFieldName == "svg") { - $svgFiles .= " ".$tmpfile."_".$fileBaseName; + $svgFiles[] = $tmpfile."_".$fileBaseName; return basename($tmpfile."_".$fileBaseName); } }); @@ -246,26 +249,20 @@ $f3->route('POST /share', if(!count($files)) { $f3->error(403); } - if($svgFiles) { - shell_exec(sprintf("rsvg-convert -f pdf -o %s %s", $tmpfile.'.svg.pdf', $svgFiles)); - } - if(!$f3->get('DEBUG')) { - array_map('GPGCryptography::hardUnlink', glob($tmpfile."*.svg")); + + $pdfSignature->saveShare(); + + if(count($svgFiles)) { + $pdfSignature->addSignature($svgFiles, $tmpfile."svg.pdf"); } - $symmetricKey = ""; - if (isset($_COOKIE[$hash])) { - $symmetricKey = "#" . $_COOKIE[$hash]; - $encryptor = new GPGCryptography($_COOKIE[$hash], $f3->get('PDF_STORAGE_PATH').$hash); - if (!$encryptor->encrypt()) { - GPGCryptography::hardUnlink($sharingFolder); - $f3->error(500); - } + if(!$f3->get('DEBUG')) { + $pdfSignature->clean(); } \Flash::instance()->setKey('openModal', 'shareinformations'); - $f3->reroute($f3->get('REVERSE_PROXY_URL').'/signature/'.$hash.$symmetricKey); + $f3->reroute($f3->get('REVERSE_PROXY_URL').'/signature/'.$hash.(($symmetricKey) ? '#'.$symmetricKey : null)); } ); diff --git a/lib/PDFSignature.class.php b/lib/PDFSignature.class.php index 92f7465..0b6cbb9 100644 --- a/lib/PDFSignature.class.php +++ b/lib/PDFSignature.class.php @@ -15,10 +15,24 @@ class PDFSignature $this->gpg = new GPGCryptography($symmetricKey, $pathHash); } + + public function createShare($duration) { + mkdir($this->pathHash); + $expireFile = $this->pathHash.".expire"; + file_put_contents($expireFile, $duration); + touch($expireFile, date_format(date_modify(date_create(), file_get_contents($expireFile)), 'U')); + } + + public function saveShare() { + if($this->symmetricKey) { + $this->gpg->encrypt(); + } + } + public function getPDF() { $sharingFolder = $this->gpg->decrypt(); if ($sharingFolder == false) { - throw new Exception( "PDF file could not be decrypted. Cookie encryption key might be missing."); + throw new Exception("PDF file could not be decrypted. Cookie encryption key might be missing."); } if ($this->pathHash != $sharingFolder && $this->gpg->isEncrypted()) { $this->toClean[] = $sharingFolder; From 72e3ee694dc8cd43a5fb404f8cb8486189189777 Mon Sep 17 00:00:00 2001 From: Vincent LAURENT Date: Wed, 31 Jul 2024 16:27:59 +0200 Subject: [PATCH 2/9] Methode static to add asignature to a pdf --- app.php | 11 ++++++----- lib/PDFSignature.class.php | 12 ++++++++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app.php b/app.php index 00742a8..d001d60 100644 --- a/app.php +++ b/app.php @@ -160,7 +160,7 @@ $f3->route('POST /sign', $filename = null; $tmpfile = tempnam($f3->get('UPLOADS'), 'pdfsignature_sign'); unlink($tmpfile); - $svgFiles = ""; + $svgFiles = []; $files = Web::instance()->receive(function($file,$formFieldName){ if($formFieldName == "pdf" && strpos(Web::instance()->mime($file['tmp_name'], true), 'application/pdf') !== 0) { @@ -179,7 +179,7 @@ $f3->route('POST /sign', } if($formFieldName == "svg") { - $svgFiles .= " ".$tmpfile."_".$fileBaseName; + $svgFiles[] = $tmpfile."_".$fileBaseName; return basename($tmpfile."_".$fileBaseName); } @@ -189,12 +189,13 @@ $f3->route('POST /sign', $f3->error(403); } - if(!$svgFiles) { + if(!count($svgFiles)) { $f3->error(403); } - shell_exec(sprintf("rsvg-convert -f pdf -o %s %s", $tmpfile.'.svg.pdf', $svgFiles)); - shell_exec(sprintf("pdftk %s multistamp %s output %s", $tmpfile.".pdf", $tmpfile.'.svg.pdf', $tmpfile.'_signe.pdf')); + PDFSignature::createPDFFromSvg($svgFiles, $tmpfile.'.svg.pdf'); + PDFSignature::addSvgToPDF($tmpfile.'.pdf', $tmpfile.'.svg.pdf', $tmpfile.'_signe.pdf'); + Web::instance()->send($tmpfile.'_signe.pdf', null, 0, TRUE, $filename); if($f3->get('DEBUG')) { diff --git a/lib/PDFSignature.class.php b/lib/PDFSignature.class.php index 0b6cbb9..fd29525 100644 --- a/lib/PDFSignature.class.php +++ b/lib/PDFSignature.class.php @@ -58,7 +58,7 @@ class PDFSignature copy($originalFile, $finalFile); $bufferFile = $finalFile.".tmp"; foreach($layers as $layerFile) { - shell_exec(sprintf("pdftk %s multistamp %s output %s", $finalFile, $layerFile, $bufferFile)); + self::addSvgToPDF($finalFile, $layerFile, $bufferFile); rename($bufferFile, $finalFile); } @@ -73,7 +73,7 @@ class PDFSignature $expireFile = $this->pathHash.".expire"; touch($expireFile, date_format(date_modify(date_create(), file_get_contents($expireFile)), 'U')); - shell_exec(sprintf("rsvg-convert -f pdf -o %s %s", $outputPdfFile, implode(" ", $svgFiles))); + self::createPDFFromSvg($svgFiles, $outputPdfFile); if($this->gpg->isEncrypted()) { $this->gpg->encrypt(); @@ -81,6 +81,14 @@ class PDFSignature $this->toClean = array_merge($this->toClean, $svgFiles); } + public static function createPDFFromSvg(array $svgFiles, $outputPdfFile) { + shell_exec(sprintf("rsvg-convert -f pdf -o %s %s", $outputPdfFile, implode(" ", $svgFiles))); + } + + public static function addSvgToPDF($pdfOrigin, $pdfSvg, $pdfOutput) { + shell_exec(sprintf("pdftk %s multistamp %s output %s", $pdfOrigin, $pdfSvg, $pdfOutput)); + } + public function clean() { foreach($this->toClean as $path) { GPGCryptography::hardUnlink($path); From 7f2a8bfa2986e222dd2e63074c3faa9776994173 Mon Sep 17 00:00:00 2001 From: Tangui Morlier Date: Wed, 31 Jul 2024 17:01:27 +0200 Subject: [PATCH 3/9] Introducing digital signature with pdfsig --- app.php | 6 ++ config/config.ini.example | 5 ++ lib/NSSCryptography.class.php | 113 ++++++++++++++++++++++++++++++++++ lib/PDFSignature.class.php | 3 + tools/create_nss_certs.sh | 46 ++++++++++++++ 5 files changed, 173 insertions(+) create mode 100644 lib/NSSCryptography.class.php create mode 100644 tools/create_nss_certs.sh diff --git a/app.php b/app.php index d001d60..2b2b3d8 100644 --- a/app.php +++ b/app.php @@ -2,6 +2,7 @@ setlocale(LC_ALL, ""); require(__DIR__.'/lib/GPGCryptography.class.php'); +require(__DIR__.'/lib/NSSCryptography.class.php'); require(__DIR__.'/lib/PDFSignature.class.php'); $f3 = require(__DIR__.'/vendor/fatfree/base.php'); @@ -61,6 +62,11 @@ if (!$f3->exists('PDF_STORAGE_ENCRYPTION')) { $f3->set('PDF_STORAGE_ENCRYPTION', GPGCryptography::isGpgInstalled()); } +if ($f3->exists('NSS3_DIRECTORY') && $f3->exists('NSS3_PASSWORD') && $f3->exists('NSS3_NICK')) { + NSSCryptography::getInstance($f3->get('NSS3_DIRECTORY'), $f3->get('NSS3_PASSWORD'), $f3->get('NSS3_NICK')); +} + + $domain = basename(glob($f3->get('ROOT')."/locale/application_*.pot")[0], '.pot'); bindtextdomain($domain, $f3->get('ROOT')."/locale"); diff --git a/config/config.ini.example b/config/config.ini.example index 8dc39be..3739292 100644 --- a/config/config.ini.example +++ b/config/config.ini.example @@ -18,3 +18,8 @@ PDF_STORAGE_PATH=/path/to/folder ; Encryption activation (default activation if GPG is installed) ;PDF_STORAGE_ENCRYPTION=true + +;NSS3 configuration (used to sign pdf with pdfsig) +;NSS3_DIRECTORY=/path/to/nss3 +;NSS3_PASSWORD="my secret password" +;NSS3_NICK="certificate nick" diff --git a/lib/NSSCryptography.class.php b/lib/NSSCryptography.class.php new file mode 100644 index 0000000..490b01a --- /dev/null +++ b/lib/NSSCryptography.class.php @@ -0,0 +1,113 @@ +nss_directory = $dir; + $this->nss_password = $pass; + $this->nss_nick = $nick; + } + + private static $instance = null; + + public static function getInstance($dir = null, $pass = null, $nick = null) { + if (!self::$instance) { + self::$instance = new NSSCryptography($dir, $pass, $nick); + } + return self::$instance; + } + + public function addSignature($pdf_path, $reason) { + putenv('NSSPASS='.$this->nss_password); + exec('pdfsig '.$pdf_path.' '.$pdf_path.'.signed.pdf -add-signature -nssdir "'.$this->nss_directory.'" -nss-pwd "$NSSPASS" -nick "'.$this->nss_nick.'" -reason "'.$reason.'" 2>&1', $output, $returnCode); + if ($returnCode) { + throw new Exception('pdfsign error: '.implode(' ', $output)); + } + rename($pdf_path.'.signed.pdf', $pdf_path); + } + + public function verify($pdf_path) { + $signatures = []; + + putenv('NSSPASS='.$this->nss_password); + exec('pdfsig -nssdir "'.$this->nss_directory.'" -nss-pwd "$NSSPASS" '.$pdf_path.' 2>&1', $output, $returnCode); + + if ($returnCode && !preg_match('/does not contain any signatures/', $output[0])) { + throw new Exception('pdfsign error: '.implode(' ', $output)); + } + + $index = null; + foreach($output as $l) { + if (preg_match('/^(Signature[^:]*):/', $l, $m)) { + $index = $m[1]; + $signatures[$index] = []; + continue; + } + if (preg_match('/^ - ([^:]*):(.*)/', $l, $m)) { + $signatures[$index][$m[1]] = $m[2]; + }elseif (preg_match('/^ - (.*) (document signed)/', $l, $m)) { + $signatures[$index]["Document signed"] = $m[1]; + } + } + return $signatures; + } + + public function isEnabled() { + if (!$this->nss_directory || !$this->nss_nick) { + return false; + } + return true; + } + + public function isPDFSigConfigured() { + if (!$this->isEnabled()) { + return false; + } + if (!$this->isPDFSigInstalled()) { + return false; + } + if (!$this->isCertUtilInstalled()) { + return false; + } + + $file = tempnam('/tmp', 'certutil'); + file_put_contents($file, $this->nss_password); + exec('certutil -f '.$file.' -d '.$this->nss_directory.' -L -n "'.$this->nss_nick.'" 2>&1', $output, $returnCodeL); + exec('certutil -f '.$file.' -d '.$this->nss_directory.' -K | grep ":'.$this->nss_nick.'" 2>&1', $output, $returnCodeK); + unlink($file); + + return ($returnCodeL == 0 && $returnCodeK == 0); + } + + public static function isCertUtilInstalled() { + $output = null; + $returnCode = null; + exec('certutil -v 2>&1', $output, $returnCode); + if ($returnCode != 1) { + return false; + } + return "OK"; + } + + + public static function isPDFSigInstalled() { + $output = null; + $returnCode = null; + exec('pdfsig -v 2>&1', $output, $returnCode); + + if ($returnCode != 0) { + return false; + } + + $version = explode(' ', $output[0])[2]; + if (! $version >= "21") { + return false; + } + return $version; + + } +} diff --git a/lib/PDFSignature.class.php b/lib/PDFSignature.class.php index fd29525..d8ef352 100644 --- a/lib/PDFSignature.class.php +++ b/lib/PDFSignature.class.php @@ -87,6 +87,9 @@ class PDFSignature public static function addSvgToPDF($pdfOrigin, $pdfSvg, $pdfOutput) { shell_exec(sprintf("pdftk %s multistamp %s output %s", $pdfOrigin, $pdfSvg, $pdfOutput)); + if (NSSCryptography::getInstance()->isEnabled()) { + NSSCryptography::getInstance()->addSignature($pdfOutput, 'Signed with SignaturePDF'); + } } public function clean() { diff --git a/tools/create_nss_certs.sh b/tools/create_nss_certs.sh new file mode 100644 index 0000000..3f5aadb --- /dev/null +++ b/tools/create_nss_certs.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +nss_dir=$1 +nss_pass=$2 +nss_nick=$3 +signaturepdf_url=$4 + +if ! test "$signaturepdf_url"; then + echo "Usage:" + echo "\t$0 "; + exit 1: +fi + +if ! test -d "$nss_dir"; then + echo "ERROR: nss_dir \"$nss_dir\" should exist"; + exit 2; +fi +if echo "$nss_nick" | grep '\.' > /dev/null ; then + echo "ERROR: $nss_nick should not contain . "; + exit 3; +fi + +signaturepdf_domain=$(echo $signaturepdf_url | sed 's/https*:\/\///' | sed 's/\/.*//') +signaturepdf_path=$(echo $signaturepdf_url | sed 's/https*:\/\///' | sed 's/.*'$signaturepdf_domain'\/*//') +signaturepdf_dc=$(echo $signaturepdf_domain | tr '.' '\n' | sed 's/^/DC=/' | tr '\n' ',' | sed 's/,$//') +if test "$signaturepdf_path"; then + signaturepdf_dc="DC=/"$signaturepdf_path','$signaturepdf_dc; +fi + +echo "$nss_pass" > /tmp/nss.$$.tmp +certutil -N -f /tmp/nss.$$.tmp -d "$nss_dir" + +echo $RANDOM" CACert "$(date)" $$ "$RANDOM | shasum > /tmp/nss_noise.$$.tmp +certutil -S -s "CN=PDF Sign CA Cert,$signaturepdf_dc" -n "PDFSignCA" -z /tmp/nss_noise.$$.tmp -x -t "CT,CT,CT" -v 120 -m 1234 -d "$nss_dir" -f /tmp/nss.$$.tmp + +echo $RANDOM" $signaturepdf_dc "$(date)" $$ "$RANDOM | shasum >> /tmp/nss_noise.$$.tmp +certutil -S -s "CN=$nss_nick,$signaturepdf_dc" -n "$nss_nick" -c "PDFSignCA" -z /tmp/nss_noise.$$.tmp -t ",," -m 730 -d "$nss_dir" -f /tmp/nss.$$.tmp + +echo "Certs created :" +echo "===============" +certutil -f /tmp/nss.$$.tmp -d $nss_dir -L +echo "Private keys created :" +echo "======================" +certutil -f /tmp/nss.$$.tmp -d $nss_dir -K + +rm /tmp/nss.$$.tmp /tmp/nss_noise.$$.tmp From 210d2512ffd78e4d00e8ef4cd719b10d88ad1bb0 Mon Sep 17 00:00:00 2001 From: Vincent LAURENT Date: Wed, 31 Jul 2024 17:02:50 +0200 Subject: [PATCH 4/9] Le fichier n'avait pas le ".svg" --- app.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app.php b/app.php index d001d60..5f415c0 100644 --- a/app.php +++ b/app.php @@ -254,7 +254,7 @@ $f3->route('POST /share', $pdfSignature->saveShare(); if(count($svgFiles)) { - $pdfSignature->addSignature($svgFiles, $tmpfile."svg.pdf"); + $pdfSignature->addSignature($svgFiles, $tmpfile.".svg.pdf"); } if(!$f3->get('DEBUG')) { @@ -311,7 +311,7 @@ $f3->route('POST /signature/@hash/save', } $pdfSignature = new PDFSignature($f3->get('PDF_STORAGE_PATH').$hash, $symmetricKey); - $pdfSignature->addSignature($svgFiles, $tmpfile."svg.pdf"); + $pdfSignature->addSignature($svgFiles, $tmpfile.".svg.pdf"); if(!$f3->get('DEBUG')) { $pdfSignature->clean(); @@ -330,7 +330,7 @@ $f3->route('GET /signature/@hash/nblayers', $files = scandir($f3->get('PDF_STORAGE_PATH').$hash); $nbLayers = 0; foreach($files as $file) { - if(strpos($file, 'svg.pdf') !== false) { + if(strpos($file, '.svg.pdf') !== false) { $nbLayers++; } } From b173392ab6e8be397e1d53fa52f3ca53c8b3a390 Mon Sep 17 00:00:00 2001 From: Vincent LAURENT Date: Wed, 31 Jul 2024 17:07:46 +0200 Subject: [PATCH 5/9] =?UTF-8?q?La=20signature=20digital=20n'est=20realis?= =?UTF-8?q?=C3=A9=20pour=20le=20moment=20que=20en=20signature=20non=20part?= =?UTF-8?q?ag=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/PDFSignature.class.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/PDFSignature.class.php b/lib/PDFSignature.class.php index d8ef352..7244893 100644 --- a/lib/PDFSignature.class.php +++ b/lib/PDFSignature.class.php @@ -58,7 +58,7 @@ class PDFSignature copy($originalFile, $finalFile); $bufferFile = $finalFile.".tmp"; foreach($layers as $layerFile) { - self::addSvgToPDF($finalFile, $layerFile, $bufferFile); + self::addSvgToPDF($finalFile, $layerFile, $bufferFile, false); rename($bufferFile, $finalFile); } @@ -85,9 +85,9 @@ class PDFSignature shell_exec(sprintf("rsvg-convert -f pdf -o %s %s", $outputPdfFile, implode(" ", $svgFiles))); } - public static function addSvgToPDF($pdfOrigin, $pdfSvg, $pdfOutput) { + public static function addSvgToPDF($pdfOrigin, $pdfSvg, $pdfOutput, $digitalSignature = true) { shell_exec(sprintf("pdftk %s multistamp %s output %s", $pdfOrigin, $pdfSvg, $pdfOutput)); - if (NSSCryptography::getInstance()->isEnabled()) { + if (NSSCryptography::getInstance()->isEnabled() && $digitalSignature) { NSSCryptography::getInstance()->addSignature($pdfOutput, 'Signed with SignaturePDF'); } } From 884b28bce9452f9e2afb8d99583ec4dadf21b1cd Mon Sep 17 00:00:00 2001 From: Tangui Morlier Date: Wed, 31 Jul 2024 17:11:28 +0200 Subject: [PATCH 6/9] create_nss_certs.sh usage --- tools/create_nss_certs.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/create_nss_certs.sh b/tools/create_nss_certs.sh index 3f5aadb..d29a3d1 100644 --- a/tools/create_nss_certs.sh +++ b/tools/create_nss_certs.sh @@ -6,17 +6,17 @@ nss_nick=$3 signaturepdf_url=$4 if ! test "$signaturepdf_url"; then - echo "Usage:" - echo "\t$0 "; - exit 1: + echo "Usage:" 1>&2 ; + echo " $0 " 1>&2 ; + exit 1; fi if ! test -d "$nss_dir"; then - echo "ERROR: nss_dir \"$nss_dir\" should exist"; + echo "ERROR: nss_dir \"$nss_dir\" should exist" 1>&2 ; exit 2; fi if echo "$nss_nick" | grep '\.' > /dev/null ; then - echo "ERROR: $nss_nick should not contain . "; + echo "ERROR: $nss_nick should not contain . " 1>&2 ; exit 3; fi From c60786fed4fb75f0671a1f598bb6a80d90a0e3e3 Mon Sep 17 00:00:00 2001 From: Tangui Morlier Date: Wed, 31 Jul 2024 17:11:49 +0200 Subject: [PATCH 7/9] Digital signature documentation --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index a130c22..f8b3be8 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,19 @@ For example, for Apache: ``` chown www-data /path/to/folder/to/store/pdf ``` +## Enabling digital signature + +The digital signature depends on `pdfsig` from the poppler project (poppler-utils debian package) and `certutil` from libnss3 project (libnss3-tools debian package). + +To enable digital signature, create certificates in a NSS database. The shell script `create_nss_certs.sh` in `tools` helps to do it : + + bash tools/create_nss_certs.sh NSS_DIRECORY/ MY_NSS_PASSWORD MY_CERT_NICK MY_SIGNATUREPDF_URL + +Once created, set the following directives in the `config/config.ini` file. + + NSS3_DIRECTORY=NSS_DIRECORY/ + NSS3_PASSWORD="MY_NSS_PASSWORD" + NSS3_NICK="MY_CERT_NICK" ### Disabling the Organize Mode From c35a46204cf66d5a5959505192218ea7a585aea5 Mon Sep 17 00:00:00 2001 From: Vincent LAURENT Date: Wed, 31 Jul 2024 17:25:14 +0200 Subject: [PATCH 8/9] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index f8b3be8..936b42f 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,12 @@ chown www-data /path/to/folder/to/store/pdf The digital signature depends on `pdfsig` from the poppler project (poppler-utils debian package) and `certutil` from libnss3 project (libnss3-tools debian package). +On debian : + +``` +sudo apt-get install poppler-utils libnss3-tool +``` + To enable digital signature, create certificates in a NSS database. The shell script `create_nss_certs.sh` in `tools` helps to do it : bash tools/create_nss_certs.sh NSS_DIRECORY/ MY_NSS_PASSWORD MY_CERT_NICK MY_SIGNATUREPDF_URL From 01879b8a4b1fcb528354cb2b26e57e1705bbf051 Mon Sep 17 00:00:00 2001 From: Vincent LAURENT Date: Wed, 31 Jul 2024 17:25:43 +0200 Subject: [PATCH 9/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 936b42f..23420b6 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ For example, for Apache: ``` chown www-data /path/to/folder/to/store/pdf ``` -## Enabling digital signature +### Enabling digital signature The digital signature depends on `pdfsig` from the poppler project (poppler-utils debian package) and `certutil` from libnss3 project (libnss3-tools debian package).