PDF加密技术咨询:字符串编码适配及加密范围疑问
PDF文本字段加密问题与解答
自定义加密方法代码
PDFWriter.prototype.encrypt = function (ref, obj, pdfSecurity) { let encryptedData; const encryptFn = pdfSecurity.getEncryptFn( ref.objectNumber, ref.generationNumber, ); if (encryptFn) { if (obj instanceof PDFStream) { let toBeEncrypt = obj.getContents(); encryptedData = new Uint8Array(encryptFn(toBeEncrypt)); obj.contents = encryptedData; } else if (obj.lookup(PDFLib.PDFName.of("Type")) == PDFLib.PDFName.of("Annot")) { let values = ["V", "T", "DA"]; for (var i = 0; i < values.length; i++) { let data = obj.lookup(PDFLib.PDFName.of(values[i])); if (data && data.value) { encryptedData = encryptFn(new Uint8Array(System.Text.Encoding.ASCII.GetBytes(data.value))); data.value = System.Text.Encoding.ASCII.GetString(encryptedData); } } } } }
测试用例:加密前后PDF对象内容
加密前
4 0 obj << /AP << /N 26 0 R >> /DA (0 0 0 rg /F3 11 Tf) /DR << /Font 24 0 R >> /DV <FEFF> /F 4 /FT /Tx /MaxLen 40 /P 22 0 R /Rect [ 165.699997 453.700012 315.700012 467.899994 ] /Subtype /Widget /T (Given Name Text Box) /TU <FEFF004600690072007300740020006E0061006D0065> /Type /Annot /V (Hello) >> endobj 5 0 obj << /AP << /N 27 0 R >> /DA (0 0 0 rg /F3 11 Tf) /DR << /Font 24 0 R >> /DV <FEFF> /F 4 /FT /Tx /MaxLen 40 /P 22 0 R /Rect [ 165.699997 421.200012 315.700012 435.399994 ] /Subtype /Widget /T (Family Name Text Box) /TU <FEFF004C0061007300740020006E0061006D0065> /Type /Annot /V (þÿ'D91\(J\)) >> endobj 6 0 obj << /AP << /N 28 0 R >> /DA (0 0 0 rg /F3 11 Tf) /DR << /Font 24 0 R >> /DV <FEFF> /F 4 /FT /Tx /MaxLen 40 /P 22 0 R /Rect [ 165.699997 388.299988 315.700012 402.5 ] /Subtype /Widget /T (Address 1 Text Box) /Type /Annot /V (þÿY'U‰WߊeM) >> endobj 7 0 obj << /AP << /N 29 0 R >> /DA (0 0 0 rg /F3 11 Tf) /DR << /Font 24 0 R >> /DV <FEFF> /F 4 /FT /Tx /MaxLen 20 /P 22 0 R /Rect [ 378.4 388.4 446.9 402.6 ] /Subtype /Widget /T (House nr Text Box) /TU <FEFF0048006F00750073006500200061006E006400200066006C006F006F0072> /Type /Annot /V <FEFF> >> endobj
加密后
4 0 obj << /AP << /N 26 0 R >> /DA (Bý„¡Öû‹¾B Þ¤„îfbÎ&óp£ÿIp‘ù›Š\þ:ø„{†?-}B<ª ) /DR << /Font 24 0 R >> /DV <FEFF> /F 4 /FT /Tx /MaxLen 40 /P 22 0 R /Rect [ 165.699997 453.700012 315.700012 467.899994 ] /Subtype /Widget /T (Bý„¡Öû‹¾B Þ¤„ñ“æ®ÖLÄc&žQþÍuòÖÒt.ó-‘Å´ƒ}p) /TU <FEFF004600690072007300740020006E0061006D0065> /Type /Annot /V (Bý„¡Öû‹¾B Þ¤„Šª™†÷ß¿`?Õò˜o) >> endobj 5 0 obj << /AP << /N 27 0 R >> /DA (4‘_‚J~ßZñuJ]\îÍø)ô[0¸ßœT;ÌÔjÀÇ\éY»X6ß-M.üí) /DR << /Font 24 0 R >> /DV <FEFF> /F 4 /FT /Tx /MaxLen 40 /P 22 0 R /Rect [ 165.699997 421.200012 315.700012 435.399994 ] /Subtype /Widget /T (4‘_‚J~ßZñuJ]\î"o÷§Àij\Ôˆu!•‡e˜‰Îó)Öçy¥¤ïJFdŠ) /TU <FEFF004C0061007300740020006E0061006D0065> /Type /Annot /V (4‘_‚J~ßZñuJ]\îR|™ARqƒ¡hã³»Uy<÷<@@>"I»a£Ê§0Û) >> endobj 6 0 obj << /AP << /N 28 0 R >> /DA (Q¾Ú©hª¶ÈŒæäêèæ@îx ªùé–Cóï5ÍÏ$°léu*Çì‹!t&) /DR << /Font 24 0 R >> /DV <FEFF> /F 4 /FT /Tx /MaxLen 40 /P 22 0 R /Rect [ 165.699997 388.299988 315.700012 402.5 ] /Subtype /Widget /T (Q¾Ú©hª¶ÈŒæäêèæg;«è·k6W¹Q!ä€åư]ŒÎ˜*õö5èÅ›t) /Type /Annot /V (Q¾Ú©hª¶ÈŒæäêèæÈ۫ʱ' ½R7ì!ñ1) >> endobj 7 0 obj << /AP << /N 29 0 R >> /DA (ó„Fû’6ò\ñ=t><ÉA¾©ªí˜$•Ã5G ÉFÁþy¸x n‹£§)âÈ×) /DR << /Font 24 0 R >> /DV <FEFF> /F 4 /FT /Tx /MaxLen 20 /P 22 0 R /Rect [ 378.4 388.4 446.9 402.6 ] /Subtype /Widget /T (ó„Fû’6ò\ñ=t><3xN-ñ>dVù¬H©u`‚7‘ÍžDÆ—í J2g) /TU <FEFF0048006F00750073006500200061006E006400200066006C006F006F0072> /Type /Annot /V <ó„Fû’6ò\ñ=t><žqæ;F˶¶.öLK> >> endobj
技术疑问解答
问题1:为何仅ASCII编码下加密可生效?PDF中是否支持UTF-8编码的加密方式?
你当前代码用System.Text.Encoding.ASCII.GetBytes和GetString处理字符串,ASCII编码会把所有非ASCII字符替换成默认替代符(比如?),直接丢失原始字节信息,导致加密后无法正确还原,所以只有纯ASCII内容能正常工作。
PDF加密本身不关心字符串编码——加密是作用在字节流上的操作,不管是UTF-8、PDF常用的UTF-16BE(比如带<FEFF> BOM的十六进制字符串)还是ASCII,只要你按以下方式处理就能正常生效:
- 区分PDF字符串类型:如果是括号包裹的普通ASCII字符串
(...),将其转为UTF-8或ASCII字节数组;如果是尖括号包裹的十六进制Unicode字符串<...>,直接解析十六进制为字节数组。 - 用这些字节数组执行加密,加密后的字节按对应格式写回PDF:ASCII字符串用括号
(...)包裹,Unicode字符串转成十六进制后用尖括号<...>包裹。
你现在的问题是把所有字符串都按ASCII转字节,破坏了Unicode字符串的原始数据,而且加密后的非ASCII字节用ASCII转回字符串,放到PDF的括号格式里会被错误解析,导致空白或乱码。
问题2:除文本字段中的/V、/T、/DA外,PDF中还有哪些类型的字符串需要进行加密?
根据PDF规范,除了加密字典本身、文件头、交叉引用表、trailer固定字段外,所有字符串对象和流对象都需要加密。针对表单字段和注释,还需要处理:
/TU:字段的工具提示字符串(你测试用例里的/TU是十六进制Unicode,当前未加密)/DV:字段的默认值字符串/AA:注释动作字典中的字符串(比如/JS脚本内容)/RC:注释的富文本内容/Contents:注释的说明文本/Q:字段拼写检查相关字符串(如果存在)
另外,整个PDF文档里的其他字符串,比如文档信息字典的/Title、/Author,书签的标题字符串,也都属于需要加密的范围。
内容的提问来源于stack exchange,提问作者Mustapha BOUFARA
相关产品推荐
相关产品推荐

