generated from jric11/baseProject
253 lines
8.9 KiB
PHP
253 lines
8.9 KiB
PHP
<?php
|
||
|
||
/**
|
||
* E056_encryption_cert_recipients.php
|
||
*
|
||
* @since 2026-05-01
|
||
* @category Library
|
||
* @package Pdf
|
||
* @author Nicola Asuni <info@tecnick.com>
|
||
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
|
||
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
|
||
* @link https://github.com/tecnickcom/tc-lib-pdf
|
||
*
|
||
* This file is part of tc-lib-pdf software library.
|
||
*/
|
||
|
||
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
|
||
// trusted paths that covers this package tree, so run the examples in place. To read assets
|
||
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
|
||
// parameter (see E047_remote_resources_security.php).
|
||
|
||
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
|
||
|
||
// autoloader when using Composer
|
||
require __DIR__ . '/../vendor/autoload.php';
|
||
|
||
// define fonts directory
|
||
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
|
||
|
||
/**
|
||
* Demonstrate certificate-based (public-key) PDF encryption with a permission matrix.
|
||
*
|
||
* Certificate-based encryption lets you define a list of recipients; each recipient's
|
||
* public-key certificate is used to wrap the document key. Only holders of the
|
||
* corresponding private key can decrypt the document. No shared password is required.
|
||
*
|
||
* The example creates two logical recipients from the same demo certificate to show
|
||
* how different permission sets can be defined per-recipient:
|
||
* Recipient A – read-only (print only)
|
||
* Recipient B – reviewer (print + annotate)
|
||
*
|
||
* The demo certificate shipped with the examples is a self-signed X.509 cert.
|
||
* To create your own certificate:
|
||
* openssl req -x509 -nodes -days 365000 -newkey rsa:2048 \
|
||
* -keyout cert.pem -out cert.pem
|
||
*/
|
||
|
||
$certPath = \realpath(__DIR__ . '/data/cert/tcpdf.crt');
|
||
if ($certPath === false) {
|
||
throw new \RuntimeException('Missing demo certificate: examples/data/cert/tcpdf.crt');
|
||
}
|
||
|
||
$certUri = 'file://' . $certPath;
|
||
|
||
$mode = PHP_SAPI === 'cli' ? 'encrypted' : 'preview';
|
||
if (PHP_SAPI === 'cli') {
|
||
$mode = (string) ($argv[1] ?? 'encrypted');
|
||
} elseif (isset($_GET['mode']) && \is_string($_GET['mode'])) {
|
||
$mode = $_GET['mode'];
|
||
}
|
||
|
||
$mode = \strtolower(\trim($mode));
|
||
if (!\in_array($mode, ['encrypted', 'preview'], true)) {
|
||
$mode = PHP_SAPI === 'cli' ? 'encrypted' : 'preview';
|
||
}
|
||
|
||
$useEncryption = $mode === 'encrypted';
|
||
|
||
// Build recipient list:
|
||
// Each entry has 'c' (certificate path/URI) and 'p' (allowed permissions array).
|
||
$pubkeys = [
|
||
[
|
||
'c' => $certUri,
|
||
'p' => ['print'], // Recipient A: print-only
|
||
],
|
||
[
|
||
'c' => $certUri,
|
||
'p' => ['print', 'annot-forms'], // Recipient B: print + annotate
|
||
],
|
||
];
|
||
|
||
$fileId = \md5('E056_encryption_cert_recipients');
|
||
|
||
$encrypt = null;
|
||
if ($useEncryption) {
|
||
// AES-128 (mode 2) with certificate recipients – no shared password needed.
|
||
$encrypt = new \Com\Tecnick\Pdf\Encrypt\Encrypt(
|
||
true, // enabled
|
||
$fileId,
|
||
2, // mode: AES-128
|
||
[], // global permissions (empty – controlled per-recipient)
|
||
'', // no user password
|
||
'', // no owner password
|
||
$pubkeys, // certificate recipients
|
||
true, // encryptMetadata
|
||
true, // encryptEmbeddedFiles
|
||
);
|
||
}
|
||
|
||
// main TCPDF object
|
||
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
|
||
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
|
||
isunicode: true,
|
||
subsetfont: false,
|
||
compress: true,
|
||
mode: \Com\Tecnick\Pdf\PdfConformance::None,
|
||
objEncrypt: $encrypt,
|
||
);
|
||
|
||
$pdf->setCreator('tc-lib-pdf');
|
||
$pdf->setAuthor('Nicola Asuni');
|
||
$pdf->setSubject('tc-lib-pdf example: 056');
|
||
$pdf->setTitle('Certificate-Based Recipient Encryption');
|
||
$pdf->setKeywords('TCPDF tc-lib-pdf example encryption certificate recipients public-key');
|
||
$pdf->setPDFFilename('E056_encryption_cert_recipients.pdf');
|
||
|
||
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
|
||
|
||
$pdf->enableDefaultPageContent();
|
||
|
||
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
|
||
$bfontB = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 12);
|
||
|
||
// -----------------------------------------------------------------------
|
||
// Page 1 – Overview
|
||
// -----------------------------------------------------------------------
|
||
$page1 = $pdf->addPage();
|
||
$pdf->page->addContent($bfontB['out']);
|
||
$pdf->page->addContent($pdf->getTextCell(
|
||
txt: 'Certificate-Based Recipient Encryption',
|
||
posx: 15,
|
||
posy: 15,
|
||
width: 180,
|
||
height: 0,
|
||
offset: 0,
|
||
linespace: 1,
|
||
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
|
||
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
|
||
));
|
||
$pdf->page->addContent($bfont['out']);
|
||
$pdf->font->insert($pdf->pon, 'helvetica', '', 10);
|
||
|
||
$html = <<<HTML
|
||
<p><b>How it works</b></p>
|
||
<p>Certificate-based encryption (also called <em>public-key encryption</em>) wraps the
|
||
document encryption key using each recipient's X.509 public-key certificate.
|
||
No shared password is distributed; only the holder of the matching private key can
|
||
open the document.</p>
|
||
|
||
<p><b>Recipient permission matrix</b></p>
|
||
<table border="1" cellpadding="4" cellspacing="0">
|
||
<tr>
|
||
<th>Recipient</th>
|
||
<th>Certificate</th>
|
||
<th>Allowed permissions</th>
|
||
</tr>
|
||
<tr>
|
||
<td>Recipient A (read-only)</td>
|
||
<td>tcpdf.crt (demo)</td>
|
||
<td>print</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Recipient B (reviewer)</td>
|
||
<td>tcpdf.crt (demo)</td>
|
||
<td>print, annot-forms</td>
|
||
</tr>
|
||
</table>
|
||
|
||
<p><b>Encryption parameters</b></p>
|
||
<ul>
|
||
<li>Output mode: MODE_PLACEHOLDER</li>
|
||
<li>Algorithm: AES-128 (mode 2) when mode is <em>encrypted</em></li>
|
||
<li>Metadata encrypted: yes (encrypted mode)</li>
|
||
<li>Embedded files encrypted: yes (encrypted mode)</li>
|
||
<li>User/owner password: none (certificate recipients only, encrypted mode)</li>
|
||
</ul>
|
||
|
||
<p><b>Creating your own certificate</b></p>
|
||
<p>Use the following OpenSSL command to generate a self-signed certificate suitable for
|
||
recipient encryption:</p>
|
||
<p><em>openssl req -x509 -nodes -days 365000 -newkey rsa:2048 -keyout cert.pem -out cert.pem</em></p>
|
||
HTML;
|
||
|
||
$modeLabel = $useEncryption ? 'encrypted (certificate recipients)' : 'preview (unencrypted for browser compatibility)';
|
||
|
||
$html = \str_replace('MODE_PLACEHOLDER', $modeLabel, $html);
|
||
|
||
$pdf->addHTMLCell(html: $html, posx: 15, posy: 30, width: 180);
|
||
|
||
// -----------------------------------------------------------------------
|
||
// Page 2 – Permissions reference
|
||
// -----------------------------------------------------------------------
|
||
$page2 = $pdf->addPage();
|
||
$pdf->page->addContent($bfontB['out']);
|
||
$pdf->page->addContent($pdf->getTextCell(
|
||
txt: 'PDF Permission Flags Reference',
|
||
posx: 15,
|
||
posy: 15,
|
||
width: 180,
|
||
height: 0,
|
||
offset: 0,
|
||
linespace: 1,
|
||
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
|
||
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
|
||
));
|
||
$pdf->page->addContent($bfont['out']);
|
||
$pdf->font->insert($pdf->pon, 'helvetica', '', 10);
|
||
|
||
$html2 = <<<HTML
|
||
<p><b>Available permission identifiers</b></p>
|
||
<table border="1" cellpadding="4" cellspacing="0">
|
||
<tr><th>Identifier</th><th>Description</th></tr>
|
||
<tr><td>print</td><td>Print the document (low-quality).</td></tr>
|
||
<tr><td>modify</td><td>Modify document content (not covered by more specific flags).</td></tr>
|
||
<tr><td>copy</td><td>Copy or extract text and graphics.</td></tr>
|
||
<tr><td>annot-forms</td><td>Add/modify annotations and fill interactive forms.</td></tr>
|
||
<tr><td>fill-forms</td><td>Fill in existing interactive form fields.</td></tr>
|
||
<tr><td>extract</td><td>Extract content for accessibility.</td></tr>
|
||
<tr><td>assemble</td><td>Insert/rotate/delete pages, create bookmarks and thumbnails.</td></tr>
|
||
<tr><td>print-high</td><td>Print at high quality (faithful digital reproduction).</td></tr>
|
||
</table>
|
||
|
||
<p><b>Notes</b></p>
|
||
<ul>
|
||
<li>With certificate recipients, permission flags are embedded inside the CMS enveloped-data
|
||
structure for each recipient separately.</li>
|
||
<li>Viewer enforcement of permissions depends on the PDF reader implementation.</li>
|
||
<li>Combining certificate encryption with a digital signature (E057) provides both
|
||
access control and document integrity assurance.</li>
|
||
</ul>
|
||
HTML;
|
||
|
||
$pdf->addHTMLCell(html: $html2, posx: 15, posy: 30, width: 180);
|
||
|
||
// -----------------------------------------------------------------------
|
||
// Output
|
||
// -----------------------------------------------------------------------
|
||
$rawpdf = $pdf->getOutPDFString();
|
||
|
||
if (PHP_SAPI !== 'cli') {
|
||
if ($useEncryption) {
|
||
// Most browser-native PDF viewers do not support certificate-based decryption.
|
||
// Force a download so the file can be opened with a compatible PDF reader.
|
||
$pdf->downloadPDF(rawpdf: $rawpdf);
|
||
exit();
|
||
}
|
||
|
||
$pdf->renderPDF(rawpdf: $rawpdf);
|
||
exit();
|
||
}
|
||
|
||
echo $rawpdf;
|