跳转到内容
getnextpdf.com

PDF 加密究竟如何运作——以及它的限制

Spec: ISO 32000-2, §7.6

PDF 加密会把一份文件内部的文字和图像打乱,使得只有持有正确密码的人才能读到它们。这是货真价实的密码学,而且它把自己唯一的那件事做得很好。它也被广泛误解,因为同一项功能还附带了第二套机制——权限标志——它看起来像一把锁,实则不是。

本页把这两者分开。它说明 AES-256 实际保护的是什么、那两个密码真正的用途,以及那条诚实的边界落在何处。

当一份文件涉及敏感信息时,人们会求助于加密:一份合同、一张工资单、一封医疗信函。这个期望是合理的——让这份内容保密,并阻止别人打印或复制它。PDF 令人信服地实现了这个愿望的前半部分,而后半部分只靠自觉来维持。

把这两者混为一谈,代价就落在这里。一个团队把某个文件标记为“禁止复制”,信任平台会强制执行,然后就发了出去。几个月后,有人用另一个读取器在三十秒内把文字复制走了,而那个从未成立过的假设,悄无声息地变成了一次泄露。加密从未被攻破。它只是被要求去做一件它本就不做的事。

  • 加密保护机密性。 有了 AES-256,一个 PDF 的字符串和流内容就是真正的密文。没有密钥,它就无法读取。
  • 用户密码用于打开文件。 提供它,你就能看到内容。没有它,就没有任何可读的东西。
  • 所有者密码管辖权限。 它是那把“完整权限”密钥。它所守护的权限标志——打印、复制、修改——记录在加密文件的内部。
  • 权限标志依赖读取器的配合。 它们是对一个行为良好的读取器发出的请求,而不是强制执行。一个无视它们的读取器照样可以打印、复制或编辑。
  • 结构始终可见。 加密隐藏的是内容,而不是文件的骨架——页数和对象布局并不是秘密。

PDF 加密由 Spec: ISO 32000-2, §7.6 定义。这个模型很精确:一个安全处理程序从一个密码派生出文件加密密钥,文件的字符串和流用那把密钥加密,而一个加密字典——由文件 trailer 引用——记录使用了哪种密码算法、密钥有多长,以及作者声明了哪些权限。

现代、推荐的密码算法是采用 256 位密钥的 AES。当你以这种方式加密一个 PDF 时,可读的有效载荷——你会提取出的文字、图像字节、字体数据——会被转换成密文。文件的外层形态有意保持清晰可读:读取器必须先找到加密字典、了解密钥长度,然后才能向你索要密码。这正是为什么加密最好被理解为内容机密性,而不是整文件的不透明。

那两个密码处于不同的层级,而这个区别正是最值得弄清楚的部分。

  1. A password is suppliedEither the user password (open) or the owner password (full rights).
  2. The security handler derives the keyISO 32000-2 §7.6.4 turns the password into the file encryption key.
  3. Content is decryptedAES-256 turns the encrypted strings and streams back into readable bytes.
  4. Permissions are consultedThe owner key grants all rights; the user key is bound by the declared permission flags — if the reader chooses to honour them.
一个密码如何变成可读的内容:用户密码(或所有者密码)被送入标准安全处理程序的密钥派生例程,以恢复出文件加密密钥,后者再解密文件的字符串和流。所有者密码还额外解锁全套权限;用户密码则受作者所声明的任何权限标志约束。

用户密码是那把打开密码。它回答一个问题:你究竟可不可以看到内容?没有用户密码,就没有可读的文件——这一部分是受密码学强制保障的,因为没有那把派生出的密钥,就没有任何东西可供解密。

所有者密码是那把权限密码。一个文件可以被任何人打开(没有用户密码),却仍然声明一个所有者密码来把守那些“完整权限”操作——高分辨率打印、复制文字、修改文件。关键在于,所有者密码所保护的那些权限位,受到标准安全处理程序的完整性保护:对它们的篡改会被一个合规的读取器检测到并拒绝,因为该处理程序把所声明的权限绑定到了加密密钥上。这些位并不是字面意义上不可读或不可编辑的字节——攻击者可以更改它们——但被改动过的副本将无法通过验证,因此一个合规的读取器会把它当作已损坏来对待。它们是防篡改可察觉的。它们只是不会自我强制

加密设置很容易表达。真正要紧的是诚实地解读其结果:这配置的是意图,而那份意图中关于权限的部分,只是建议性的。

<?php
declare(strict_types=1);
use NextPDF\Core\Document;
use NextPDF\Security\Encryption\EncryptionMode;
use NextPDF\Security\Encryption\Permission;
$document = Document::createStandalone();
$document->setTitle('Confidential Offer');
$document->addPage();
// Confidentiality is real: AES-256 means the content is unreadable
// without the user password. This half is cryptographically enforced.
$document->encrypt(
userPassword: 'open-sesame',
ownerPassword: 'full-rights-key',
mode: EncryptionMode::Aes256,
// Permissions are a declared request to a cooperating reader,
// recorded inside the encrypted document — not a hard lock.
permissions: Permission::Print->value | Permission::CopyContent->value,
);
$document->save('offer.pdf');

用户密码和所有者密码是职责不同的两把不同密钥。permissions 参数陈述的是一个行为良好的读取器应该允许什么。它是诚实的元数据,受安全处理程序的完整性保护,使得篡改可被察觉——而这就是一个生成库所能承诺的上限。

这个陷阱在于把“权限”读成“强制执行”。这很符合直觉:标志写着不允许打印,那打印理所当然就被阻止了。但 PDF 里没有任何东西能伸出手去禁用读取器的打印按钮。那个权限整数被记录下来,是供一个配合的读取器去遵从的。一个选择不配合的读取器——好几个就是不配合——可以自由地打印、复制和编辑。

NextPDF 对此态度明确。核心加密路径把权限标志当作所声明的意图,从不假装能强制执行它们。如果你需要某些操作被真正阻止,那种控制必须存在于文件之外——存在于你把文件交给了谁,或者存在于围绕它的一套访问控制系统之中。

请精确地认识每一半各自保证什么。加密(用户密码层)是一项真正的机密性控制:如果密码强,它就强,而且它的可靠程度,只取决于你选择的那个秘密。权限(所有者密码层)是防篡改可察觉的元数据,而不是访问控制。

What you might expectWhat encryption actually delivers
没有密码,任何人都无法读取内容成立。有了 AES-256 和一个强用户密码,字符串和流在没有密钥时无法读取。这是真正受强制保障的。
没有人能把“禁止复制”改写成“允许复制”防篡改可察觉,而非不可能。权限标志受标准安全处理程序的完整性保护:外部人员可以编辑这些字节,但被改动过的副本将无法通过验证,因此一个合规的读取器会检测到并拒绝这一改动。
读取器会被阻止复制或打印任何 PDF 生成器都无法保证这一点。一个不配合的读取器可以无视这些标志。这是这个格式的特性,而不是 NextPDF 的限制。

还有两条边界值得直说。第一,加密保护的是内容,而不是文件的结构:对象布局和页数仍然可见,这是正常且必要的,好让读取器能定位到加密字典。第二,一个加密的 PDF 无法同时声称符合归档级 PDF/A 合规——那个配置文件禁止 Encrypt 条目,因此你只能二选一。加密与权限故障排查页详细讲解了具体的例外情形以及 PDF/A 的这项拒绝。

  • AES-256——采用 256 位密钥的高级加密标准,是用于 PDF 内容加密的推荐现代密码算法。
  • 用户密码(打开密码)——打开并读取一个加密文件所需的秘密。没有它,内容就无法解密。
  • 所有者密码(权限密码)——管辖权限标志的那把“完整权限”秘密。一个文件可以拥有它,而无需用户密码即可打开。
  • 权限标志——存储在加密字典中、所声明的打印/复制/修改意图。受完整性保护使得篡改可被察觉,但仅由一个配合的读取器来遵从。
  • 安全处理程序——从密码派生出文件加密密钥的组件,由 ISO 32000-2 中的标准安全处理程序定义。
  • 依赖读取器配合——一种控制,它取决于消费方软件选择遵从它,而不是由文件本身强制执行。