Как этот код извлекает подпись? - PullRequest
2 голосов
/ 09 марта 2012

Я должен отладить старый PHP-скрипт от разработчика, который покинул компанию.Я понимаю большую часть кода, кроме следующей функции.Мой вопрос: что ...

if ($ seq == 0x03 || $ seq == 0x30)

... означает в контексте извлеченияподпись из сертификата X.509?

public function extractSignature($certPemString) {

    $bin = $this->ConvertPemToBinary($certPemString);

    if(empty($certPemString) || empty($bin))
    {
        return false;
    }    

    $bin = substr($bin,4);

    while(strlen($bin) > 1) 
    {            
        $seq = ord($bin[0]); 
        if($seq == 0x03 || $seq == 0x30) 
        {            
            $len = ord($bin[1]);
            $bytes = 0;

            if ($len & 0x80)
            {
                $bytes = ($len & 0x0f);
                $len = 0;
                for ($i = 0; $i < $bytes; $i++)
                {
                    $len = ($len << 8) | ord($bin[$i + 2]);
                }
            }

            if($seq == 0x03)
            {
                return substr($bin,3 + $bytes, $len);
            }
            else 
            {
                $bin = substr($bin,2 + $bytes + $len);                  
            }                                                    
        }
        else 
        {                            
            return false;                
        }
    }
    return false;
}

Ответы [ 5 ]

5 голосов
/ 09 марта 2012

Сертификат X.509 содержит данные в нескольких разделах (называемых триплетами Tag-Length-Value).Каждый раздел начинается с байта тега, который указывает формат данных раздела.Вы можете увидеть список этих типов данных здесь .

0x03 - это байт тега для типа данных BIT STRING и 0x30 - это байт тега для типа данных SEQUENCE .

Таким образом, этот код предназначен для обработки типов данных BIT STRING и SEQUENCE.Если вы посмотрите на эту часть:

if($seq == 0x03)
{
    return substr($bin,3 + $bytes, $len);
}
else // $seq == 0x30
{
    $bin = substr($bin,2 + $bytes + $len);                  
}

, вы увидите, что функция предназначена для пропуска последовательностей (0x30) до тех пор, пока не найдет битовую строку (0x03), после чего она возвращает значениебитовая строка.

Возможно, вам интересно, почему магическое число составляет 3 для Битовая строка и 2 для Последовательность .Это связано с тем, что в Bit String первый байт значения является специальным дополнительным полем, которое указывает, сколько битов не используется в последнем байте данных.(Например, если вы отправляете 13 бит данных, это займет 2 байта = 16 бит, а поле « неиспользуемые биты » будет равно 3.)

Следующая проблема: поле длины.Когда длина значения меньше 128 байтов, длина просто указывается с использованием одного байта (самый старший бит будет равен 0).Если длина составляет 128 или больше, то первый байт длины имеет установленный бит 7, а оставшиеся 7 битов указывают, сколько следующих байтов содержат длину (в порядке с прямым порядком байтов).Больше описания здесь .Синтаксический анализ поля длины происходит в этом разделе кода:

$len = ord($bin[1]);
$bytes = 0;

if ($len & 0x80)
{
    // length is greater than 127!
    $bytes = ($len & 0x0f);
    $len = 0;
    for ($i = 0; $i < $bytes; $i++)
    {
         $len = ($len << 8) | ord($bin[$i + 2]);
    }
}

После этого $bytes содержит количество дополнительных байтов, используемых полем длины, а $len содержит длинуПоле значения (в байтах).

Вы обнаружили ошибку в коде?Помните,

Если длина равна 128 или больше, то для первого байта длины установлен бит 7, а оставшиеся 7 битов указывают, сколько следующих байтов содержат длину.

, но код говорит $bytes = ($len & 0x0f), что занимает только младшие 4 бита байта!Должно быть:

$bytes = ($len & 0x7f);

Конечно, эта ошибка является проблемой только для длинных сообщений чрезвычайно : она будет работать нормально, пока значение длины будет соответствовать 0x0f = 15 байт.Это означает, что данные должны быть не более 256 ^ 15 байт.Это примерно триллион йотбайт, что должно быть достаточно для всех.

2 голосов
/ 09 марта 2012

Я бы рискнул предположить, что это независимая от порядка байтов проверка для идентификатора версии '3' в сертификате x.509.См. RFC 1422 , стр. 7.Остальное потянет подпись побайтово.

2 голосов
/ 09 марта 2012

Как говорит Пейтман выше, у вас просто есть логическое if, мы просто проверяем, является ли $seq 0x30 или 0x03.

У меня такое чувство, что вы уже это знаете, так что здесь. $seq - это первый байт сертификата, который, вероятно, является либо версией сертификата, либо магическим числом, обозначающим, что файл является сертификатом (также известным как «Я предполагаю это, потому что 10:45 не время для начать читать RFC ").

В этом случае мы сравниваем с 0x30 и 0x03. Эти числа выражены в шестнадцатеричном формате (как и каждое число, начинающееся с 0x), которое является основанием-16. Это просто очень удобное сокращение для двоичного файла, поскольку каждая шестнадцатеричная цифра соответствует ровно четырем двоичным битам. Быстрый стол это:

0 = 0000
1 = 0001
2 = 0010
3 = 0011
...
...
E = 1110
F = 1111

Одинаково хорошо, мы могли бы сказать if($seq == 3 || $seq == 48), но в этом случае гекс намного легче читать и понимать.

1 голос
/ 09 марта 2012

ord () получает значение передаваемого вами символа ASCII.В этом случае проверяется, является ли символ ASCII 0 или концом текста (в соответствии с этой таблицей ASCII ).

0 голосов
/ 09 марта 2012

0x03 и 0x30 - шестнадцатеричные значения. Посмотрите, и вы получите то, что $ seq соответствует

...