Openssl pkcs7 read: примеры (PHP)

Использование openssl_pkcs7_read для работы с криптографическими сообщениями
Раздел: Шифрование (OpenSSL)
openssl_pkcs7_read(string $data, array &$certificates): bool
Назначение и параметры openssl_pkcs7_read

Функция openssl_pkcs7_read разбирает строку, содержащую данные в формате PKCS#7, и извлекает сертификаты и другие данные в массив. Формат PKCS#7 (также известный как Cryptographic Message Syntax - CMS) применяется для цифровых подписей, шифрования и упаковки сертификатов.

Функция часто используется при обработке S/MIME сообщений, проверке цифровых подписей или извлечении вложенных сертификатов из криптоконтейнеров.

Синтаксис и аргументы
bool openssl_pkcs7_read(string $data, array &$certificates)
  • $data (string) - строка с данными в формате PKCS#7. Может содержать как подписанные, так и зашифрованные данные.
  • &$certificates (array) - ссылка на массив, который будет заполнен извлеченными сертификатами и другими данными после успешного выполнения функции.

Функция возвращает true при успешном разборе данных и false в случае ошибки.

Базовые примеры использования
Пример 1: Извлечение сертификатов из подписи

Разбор простой PKCS#7 структуры:

$pkcs7_data = '-----BEGIN PKCS7-----
MIAGCSqGSIb3DQEHAq...
-----END PKCS7-----'; // Усечено для примера
$certs = [];

if (openssl_pkcs7_read($pkcs7_data, $certs)) {
    echo 'Извлечено сертификатов: ' . count($certs);
    foreach ($certs as $idx => $cert) {
        echo "\nСертификат #$idx:\n";
        echo $cert;
    }
} else {
    echo 'Ошибка чтения PKCS#7 данных';
}
Извлечено сертификатов: 2
Сертификат #0:
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJA...
-----END CERTIFICATE-----
Сертификат #1:
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgI...
-----END CERTIFICATE-----
Пример 2: Обработка S/MIME сообщения
$smime_message = file_get_contents('message.smime');
$certificates = [];

if (openssl_pkcs7_read($smime_message, $certificates)) {
    $cert_info = openssl_x509_parse($certificates[0]);
    echo 'Владелец сертификата: ' . $cert_info['subject']['CN'];
} else {
    echo 'Не удалось прочитать S/MIME сообщение';
}
Владелец сертификата: Example Corporation
Альтернативные функции в PHP
  • openssl_pkcs7_verify() - проверяет подпись PKCS#7. Предпочтительнее при необходимости валидации подписи, а не только извлечения данных.
  • openssl_pkcs7_decrypt() - расшифровывает PKCS#7 сообщение. Используется когда данные зашифрованы.
  • openssl_x509_read() - парсит отдельный сертификат X.509. Подходит если уже есть извлеченный сертификат в строковом формате.
  • openssl_cms_read() (PHP 8.0+) - работает с форматом CMS (более современный аналог PKCS#7). Рекомендуется для новых проектов.
Типичные ошибки и их решение
Ошибка 1: Неверный формат данных
$data = 'Просто текст, а не PKCS#7';
$certs = [];
$result = openssl_pkcs7_read($data, $certs);
var_dump($result); // bool(false)
var_dump($certs); // array(0) {}
Ошибка 2: Отсутствие расширения OpenSSL
// При отсутствии расширения возникает фатальная ошибка:
// Fatal error: Uncaught Error: Call to undefined function openssl_pkcs7_read()

Решение: убедиться что расширение OpenSSL активировано в php.ini

Ошибка 3: Передача массива по значению вместо ссылки
$certs = [];
// Неправильно - не будет работать в PHP 8+
$result = openssl_pkcs7_read($data, $certs); // Предупреждение
Изменения в версиях PHP
  • PHP 8.0.0: Функция теперь возвращает false вместо null при ошибке. Тип параметра $certificates строго проверяется - должен быть массивом.
  • PHP 7.2.0: Добавлена поддержка дополнительных форматов кодирования.
  • Ранние версии: Функция могла некорректно обрабатывать некоторые варианты BER кодировки.
Расширенные примеры использования
Извлечение и проверка цепочки сертификатов
Пример php
function extractCertificateChain($pkcs7Data) {
    $certs = [];
    if (!openssl_pkcs7_read($pkcs7Data, $certs)) {
        return null;
    }
    
    $chain = [];
    foreach ($certs as $certPem) {
        $certData = openssl_x509_parse($certPem);
        if ($certData) {
            $chain[] = [
                'pem' => $certPem,
                'subject' => $certData['subject'],
                'issuer' => $certData['issuer'],
                'validity' => [
                    'from' => date('Y-m-d', $certData['validFrom_time_t']),
                    'to' => date('Y-m-d', $certData['validTo_time_t'])
                ]
            ];
        }
    }
    return $chain;
}

// Использование
$chain = extractCertificateChain($pkcs7_data);
if ($chain) {
    echo 'Цепочка из ' . count($chain) . ' сертификатов:\n';
    foreach ($chain as $i => $cert) {
        echo "{$i}. CN: {$cert['subject']['CN']} (до {$cert['validity']['to']})\n";
    }
}
Обработка вложенных PKCS#7 структур
Пример php
function deepParsePKCS7($data, &$allCerts = []) {
    $currentCerts = [];
    if (openssl_pkcs7_read($data, $currentCerts)) {
        foreach ($currentCerts as $cert) {
            // Проверяем не является ли сертификат на самом деле PKCS#7 контейнером
            if (strpos($cert, 'BEGIN PKCS7') !== false) {
                deepParsePKCS7($cert, $allCerts);
            } else {
                $allCerts[] = $cert;
            }
        }
        return true;
    }
    return false;
}

$allCertificates = [];
deepParsePKCS7($nested_pkcs7_data, $allCertificates);
echo 'Найдено всех сертификатов: ' . count($allCertificates);
Работа с бинарными данными DER
Пример php
// Конвертация DER в PEM для openssl_pkcs7_read
$derData = file_get_contents('signature.der');
$pemData = "-----BEGIN PKCS7-----\n" . 
           chunk_split(base64_encode($derData), 64, "\n") . 
           "-----END PKCS7-----";

$certs = [];
if (openssl_pkcs7_read($pemData, $certs)) {
    echo 'Успешно конвертировано и прочитано';
}
Извлечение атрибутов подписи
Пример php
$pkcs7Data = file_get_contents('signed_document.p7m');
$certs = [];
if (openssl_pkcs7_read($pkcs7Data, $certs)) {
    // Первый элемент часто содержит подписанные данные
    $signedData = $certs[0];
    
    // Поиск атрибутов подписи
    if (preg_match('/signingTime:(.*?)(?:\n|$)/', $signedData, $matches)) {
        echo 'Время подписания: ' . trim($matches[1]);
    }
    
    // Извлечение OID атрибутов
    $oids = [];
    if (preg_all('/OID:(.*?)\n/', $signedData, $oidMatches)) {
        $oids = $oidMatches[1];
    }
    print_r($oids);
}
Аналоги в других языках программирования
Python (cryptography)
from cryptography.hazmat.primitives.serialization import pkcs7
from cryptography import x509
import base64

# Данные PKCS#7 в формате PEM
pkcs7_data = '-----BEGIN PKCS7-----...'

# Загрузка PKCS#7 данных
pkcs7_obj = pkcs7.load_pem_pkcs7_certificates(pkcs7_data.encode())
print(f"Извлечено {len(pkcs7_obj)} сертификатов")
for cert in pkcs7_obj:
    print(cert.subject)
JavaScript (Node.js с модулем 'pkcs7')
const pkcs7 = require('pkcs7');
const fs = require('fs');

const pkcs7Data = fs.readFileSync('signature.p7m', 'binary');
const certs = pkcs7.extractCertificates(pkcs7Data);
console.log(`Извлечено сертификатов: ${certs.length}`);
Отличия от PHP

В Python библиотека cryptography предоставляет более объектно-ориентированный интерфейс. В Node.js требуется установка дополнительных модулей. PHP функция встроена в расширение OpenSSL и работает с PEM форматом по умолчанию.

PHP openssl_pkcs7_read function comments

En
Openssl pkcs7 read Export the PKCS7 file to an array of PEM certificates