Skip to content

Commit

Permalink
Introducing digital signature with pdfsig
Browse files Browse the repository at this point in the history
  • Loading branch information
teymour committed Jul 31, 2024
1 parent 72e3ee6 commit 7f2a8bf
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 0 deletions.
6 changes: 6 additions & 0 deletions app.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -61,6 +62,11 @@
$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");
Expand Down
5 changes: 5 additions & 0 deletions config/config.ini.example
Original file line number Diff line number Diff line change
Expand Up @@ -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"
113 changes: 113 additions & 0 deletions lib/NSSCryptography.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

class NSSCryptography
{
private $nss_directory = null;
private $nss_password = null;
private $nss_nick = null;

private function __construct($dir, $pass, $nick) {
$this->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;

}
}
3 changes: 3 additions & 0 deletions lib/PDFSignature.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ public static function createPDFFromSvg(array $svgFiles, $outputPdfFile) {

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() {
Expand Down
46 changes: 46 additions & 0 deletions tools/create_nss_certs.sh
Original file line number Diff line number Diff line change
@@ -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 <nss_dir> <nss_pass> <nss_nick> <signaturepdf_url>";
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

0 comments on commit 7f2a8bf

Please sign in to comment.