Проверьте диапазон байтов PDSignature в файле PDF, используя PDFBox - PullRequest
0 голосов
/ 25 сентября 2019

Я загрузил PDDocument.

Я получил объект PDSignature с именем sig .

Диапазон байтов подписи предоставляется sig.getByteRange() .В моем случае это:

0-18373 43144-46015

Я хочу убедиться, что диапазон байтов подписи действителен.Поскольку подпись должна проверять весь файл, ожидайте себя.Кроме того, диапазон байтов обеспечивается сигнатурой, поэтому я не могу на нее полагаться.

Я могу проверить, чтобы первое значение было 0 , а последнее значение должно быть размером файла -1.

Но мне также нужно проверить второе и третье значение (18373 и 43144).Поэтому мне нужно знать положение подписи PDS в документе и ее длину.

Как мне их получить?

1 Ответ

3 голосов
/ 25 сентября 2019

Посмотрите на пример PDFBox ShowSignature.Он делает это косвенно: проверяет, совпадают ли байты в промежутке диапазонов байтов со значением подписи, определенным при разборе документа.

В методе showSignature:

int[] byteRange = sig.getByteRange();
if (byteRange.length != 4)
{
    System.err.println("Signature byteRange must have 4 items");
}
else
{
    long fileLen = infile.length();
    long rangeMax = byteRange[2] + (long) byteRange[3];
    // multiply content length with 2 (because it is in hex in the PDF) and add 2 for < and >
    int contentLen = contents.getString().length() * 2 + 2;
    if (fileLen != rangeMax || byteRange[0] != 0 || byteRange[1] + contentLen != byteRange[2])
    {
        // a false result doesn't necessarily mean that the PDF is a fake
        // see this answer why:
        // https://stackoverflow.com/a/48185913/535646
        System.out.println("Signature does not cover whole document");
    }
    else
    {
        System.out.println("Signature covers whole document");
    }
    checkContentValueWithFile(infile, byteRange, contents);
}

Вспомогательный метод checkContentValueWithFile:

private void checkContentValueWithFile(File file, int[] byteRange, COSString contents) throws IOException
{
    // https://stackoverflow.com/questions/55049270
    // comment by mkl: check whether gap contains a hex value equal
    // byte-by-byte to the Content value, to prevent attacker from using a literal string
    // to allow extra space
    try (RandomAccessBufferedFileInputStream raf = new RandomAccessBufferedFileInputStream(file))
    {
        raf.seek(byteRange[1]);
        int c = raf.read();
        if (c != '<')
        {
            System.err.println("'<' expected at offset " + byteRange[1] + ", but got " + (char) c);
        }
        byte[] contentFromFile = raf.readFully(byteRange[2] - byteRange[1] - 2);
        byte[] contentAsHex = Hex.getString(contents.getBytes()).getBytes(Charsets.US_ASCII);
        if (contentFromFile.length != contentAsHex.length)
        {
            System.err.println("Raw content length from file is " +
                    contentFromFile.length +
                    ", but internal content string in hex has length " +
                    contentAsHex.length);
        }
        // Compare the two, we can't do byte comparison because of upper/lower case
        // also check that it is really hex
        for (int i = 0; i < contentFromFile.length; ++i)
        {
            try
            {
                if (Integer.parseInt(String.valueOf((char) contentFromFile[i]), 16) !=
                    Integer.parseInt(String.valueOf((char) contentAsHex[i]), 16))
                {
                    System.err.println("Possible manipulation at file offset " +
                            (byteRange[1] + i + 1) + " in signature content");
                    break;
                }
            }
            catch (NumberFormatException ex)
            {
                System.err.println("Incorrect hex value");
                System.err.println("Possible manipulation at file offset " +
                        (byteRange[1] + i + 1) + " in signature content");
                break;
            }
        }
        c = raf.read();
        if (c != '>')
        {
            System.err.println("'>' expected at offset " + byteRange[2] + ", but got " + (char) c);
        }
    }
}

(Строго говоря, двоичная строка в нормальных скобках тоже будет в порядке , если она заполняет весь пробел , это не должно бытьшестнадцатеричная строка.)

...