1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

pdfのstreamオブジェクトはパスワードかかってなくても暗号化されているケースがある

Last updated at Posted at 2019-11-09

少し要因が見えてきたので、タイトル変更しました。
→解決しました。
結論は追記およびコメント参照ください。

経緯

とあるpdfファイルからテキストを抽出できないか 1 調べていたところ
<<.../FlateDecode...>stream...endstreamのデータを復号できずに困り、pdfフォーマットの解説書を読んだが、そのファイル自身が、自ら定義しているフォーマットに違反しているようにしか見えなかった。

pdfフォーマットの解説書

https://www.adobe.com/devnet/pdf/pdf_reference.html 内のリンク
Document Management – Portable Document Format – Part 1: PDF 1.7, First Edition - PDF32000_2008.pdf

image.png

RFC1950 (とRFC1951)に準拠しているように読み取れる。

該当のPDFのデータ

image.png

"stream" 2 以降の9A FC~が、データであり、zlibフォーマットに従っているはずだが、

RFCs 1950によると

  • 先頭から2byteがCMF(1byte),FLG(1byte)の順で配置され、CMFの下位4bit(Compresstion method)は、値8しか定義されていない。(ただ、使うなとは書いていない。)

  • 先頭から2byteをビッグエンディアンの数値としてみたときに、31で割り切れること

The FCHECK value must be such that CMF and FLG, when viewed as
a 16-bit unsigned integer stored in MSB order (CMF*256 + FLG),
is a multiple of 31.

とあるが、下記のよるに、0x9AFC(39676)は31では割り切れない。(余り:27)

であり、準拠しているように見えない。
どうやって復号すりゃいいんだ・・。へるぷみー
とりあえずJavaで試してみたが復号できない。
ちなみにC#で先頭2byte捨ててDeflateStreamで読み込む方法もダメでした。

要因→追記参照

試してみたDeflate復号用のソースコード(Java)

78 9Cとかで始まっているデータで動確済み。

ソースコード
DeflateTest.java

import java.io.*;
import java.util.zip.*; // to use Inflater

public class DeflateTest
{
    public static byte[] GetBytes(String path, long srcOffset, int srcLength) throws IOException
    {
        byte[] b = new byte[srcLength];
        int n = 0;

        FileInputStream fs = null;

        try {
            fs = new FileInputStream(path);
            
            fs.skip(srcOffset);
            n = fs.read(b, 0, srcLength);
            if (n != srcLength) {
                b = null;
            }
        }
        catch (FileNotFoundException e) {
            System.out.println(e);
            return null;
        }
        catch (IOException e) {
            System.out.println(e);
            return null;
        }
        finally {
            if (fs != null) {
                fs.close();
            }
        }
        return b;
    }


    public static void DecodeFile(String path, long srcOffset, int srcLength) throws IOException
    {
        byte[] buf = GetBytes(path, srcOffset, srcLength);
        if ( buf == null ) {
            return;
        }

        Inflater inf = new Inflater();

        inf.setInput(buf, 0, srcLength);
        byte[] tmp = new byte[1024];

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("test_out.dat");

            while ( !inf.finished() ) {
                int len = inf.inflate(tmp);
                if ( len > 0)  {
                    fos.write(tmp, 0, len);
                }
            }
        }
        catch (DataFormatException e) {
            System.out.println(e);
        }
        finally {
            if ( fos != null ) {
                fos.close();
            }
        }
    }

    public static void main(String[] args) throws IOException
    {
        if (args.length != 3) {
            System.out.println("Parameter error.");
            return;
        }
        
        long offset = 0;
        int length = 0;
        try {
            offset = Long.decode(args[1]);
            length = Integer.decode(args[2]);
        }
        catch (Exception e) {
            System.out.println("Parameter parse error.");
            return;
        }
        DecodeFile(args[0], offset, length);
    }
}

コンパイル方法:javac DeflateTest.java
実行方法:java DeflateTest ファイルパス 開始バイト位置 バイト長

実行結果

>java DeflateTest PDF32000_2008.pdf 0x434A 542
java.util.zip.DataFormatException: incorrect header check

追記

streamが暗号化されている

保護されたpdfだと事前に別のデコード処理が要るとかか→あたりっぽい。
(pdf最後尾(trailer)に/Encryptがあるかどうかで判断できそう。)

7.6.2章の最後のほうに下記の記載がある。まさにこれ。
image.png

AESの場合は、さらに、streamの先頭16byte分にAES CBC modeのinitial vectorが配置されているらしい。
image.png

下記が非常に参考になりました。
pdfの暗号化 - https://qiita.com/mtgto/items/e54fb19d0547590ca791

差分エンコーディング?

さらに差分エンコーディングしている可能性がある。不明。
/Decodeparms, /Predictor(PLRMを参照)が関係しそう?

http://7shi.hateblo.jp/entry/20110207/1297053867

今回は関係なかった。

  1. Word2016でpdfを開くと、表とかも含めてWord形式にコンバートできる。が、ファイルや環境によってはデータが破損したりして使えないケースがある。

  2. 直後の改行コード\r(0D)または\n(0A)または\r\n(0D 0A)は無視される

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?