使用openpgpjs加密PDF后Chrome无法加载,如何调出密码弹窗?
加密PDF后Chrome无法识别并弹出密码输入框的问题
我正在开发一款应用,支持选择本地PDF文件并使用openpgpjs加密。目前能成功完成加密,但在Chrome中打开加密后的文件时,会弹出“无法加载PDF文档”的错误提示。不过手动解密该文件后,原始PDF可以正常显示。
选择文件并赋值给uint8Array的代码:
//file picker listener uint8Array document.getElementById("file_picker").onchange = function (event) { var file = event.target.files[0]; console.log(file); var reader = new FileReader(); reader.readAsArrayBuffer(file); reader.addEventListener('load', (loadEvent) => { console.log("array data") var buffer = loadEvent.target.result; uint8Array = new Uint8Array(buffer); console.log(uint8Array); }) }
加密并下载加密文件的代码:
//encrypt click listener document .getElementById("encrypt") .addEventListener("click", function (e) { if (uint8Array != undefined) { (async () => { const message = await openpgp.createMessage({ binary: uint8Array }); const encrypted = await openpgp.encrypt({ message, // input as Message object passwords: ['password'], // multiple passwords possible format: 'binary' // don't ASCII armor (for Uint8Array output) }); console.log(encrypted); // Uint8Array downloadBlob(encrypted, 'data.pdf', 'application/pdf'); })(); } }); function downloadBlob(data, fileName, mimeType) { var blob, url; blob = new Blob([data], { type: mimeType }); url = window.URL.createObjectURL(blob); downloadURL(url, fileName); setTimeout(function () { return window.URL.revokeObjectURL(url); }, 1000); } function downloadURL(data, fileName) { var a; a = document.createElement('a'); a.href = data; a.download = fileName; document.body.appendChild(a); a.style = 'display: none'; a.click(); a.remove(); }
现在PDF能正常下载,但打开时会出现上述错误。是否需要设置某些参数来触发Chrome弹出密码输入弹窗?
更新
我尝试改用CryptoJS的AES加密,依然遇到相同问题。
期望效果
打开加密PDF时,能弹出如下密码输入弹窗:
问题原因与解决方案
你现在的核心问题是:用openpgpjs或CryptoJS做的是「整个PDF文件的加密」,而Chrome的PDF阅读器只识别PDF标准内置的加密格式,这是两种完全不同的加密逻辑。
解决思路
要让Chrome弹出原生的PDF密码输入框,必须遵循PDF规范的内置加密机制,而非直接加密整个文件二进制流。具体来说:
- 不能用通用加密库直接加密PDF的Uint8Array,需要操作PDF内部的加密字典(Encrypt字典),添加符合标准的密码保护。
- 推荐使用专门处理PDF的库实现,比如
pdf-lib,它支持给PDF添加标准的用户/所有者密码,加密后的文件能被Chrome原生阅读器识别并触发密码弹窗。
用pdf-lib实现的示例代码
// 先通过CDN或npm引入pdf-lib import { PDFDocument } from 'pdf-lib'; // 修改文件选择后的处理逻辑 document.getElementById("file_picker").onchange = async function (event) { const file = event.target.files[0]; const pdfBytes = await file.arrayBuffer(); // 加载原始PDF const pdfDoc = await PDFDocument.load(pdfBytes); // 设置用户密码(打开时需输入)和所有者密码(用于权限控制) const userPassword = 'your-user-password'; const ownerPassword = 'your-owner-password'; pdfDoc.setUserPassword(userPassword); pdfDoc.setOwnerPassword(ownerPassword); // 导出加密后的PDF字节流 const encryptedPdfBytes = await pdfDoc.save(); // 下载文件 downloadBlob(encryptedPdfBytes, 'protected.pdf', 'application/pdf'); }; // 保留原有的下载函数 function downloadBlob(data, fileName, mimeType) { var blob, url; blob = new Blob([data], { type: mimeType }); url = window.URL.createObjectURL(blob); downloadURL(url, fileName); setTimeout(function () { return window.URL.revokeObjectURL(url); }, 1000); } function downloadURL(data, fileName) { var a; a = document.createElement('a'); a.href = data; a.download = fileName; document.body.appendChild(a); a.style = 'display: none'; a.click(); a.remove(); }
为什么原方式不行?
openpgpjs和CryptoJS是对整个文件进行加密,加密后的文件本质上已经不是合法的PDF格式,只是一个加密后的二进制数据块。Chrome的PDF阅读器无法识别这种非标准格式,因此会提示“无法加载PDF文档”;而手动解密后恢复了原始PDF结构,所以能正常打开。只有按照PDF规范添加的内置加密,才能被阅读器识别并触发原生密码输入弹窗。
内容的提问来源于stack exchange,提问作者KonTash
相关产品推荐
相关产品推荐

