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

Шифрование данных с помощью openssl_pkcs7_encrypt
Раздел: Шифрование (OpenSSL)
openssl_pkcs7_encrypt(string $input_filename, string $output_filename, OpenSSLCertificate|array|string $certificate, array|null $headers, int $flags = 0, int $cipher_algo = OPENSSL_CIPHER_AES_128_CBC): bool
PHP функция openssl_pkcs7_encrypt

Функция openssl_pkcs7_encrypt предназначена для шифрования сообщения с использованием сертификатов по стандарту S/MIME (PKCS #7). Основное применение - безопасная передача данных, например, шифрование электронной почты или файлов. Функция шифрует содержимое, которое может быть расшифровано только владельцем закрытого ключа, соответствующего указанному сертификату.

Аргументы функции
  1. input_filename (string) - путь к файлу, который содержит данные для шифрования.
  2. output_filename (string) - путь к файлу, в который будет записан результат шифрования.
  3. recipcerts (string|array) - сертификат или массив сертификатов получателей. Может быть путем к файлу, строкой с PEM-сертификатом или массивом таких значений.
  4. headers (array) - массив заголовков, которые добавляются в начало данных перед шифрованием. Часто используется для заголовков электронной почты.
  5. flags (int) - битовая маска флагов, изменяющих поведение шифрования.
    • OPENSSL_PKCS7_TEXT - добавляет заголовок Content-type: text/plain и преобразует конец строки.
    • OPENSSL_PKCS7_BINARY - предотвращает преобразование конца строки.
    • OPENSSL_PKCS7_NOOLDMIMETYPE - использует тип application/pkcs7-mime вместо application/x-pkcs7-mime.
  6. cipher_algo (int) - шифр, используемый для шифрования. По умолчанию используется OPENSSL_CIPHER_AES_128_CBC. Возможные значения: OPENSSL_CIPHER_RC2_40, OPENSSL_CIPHER_RC2_128, OPENSSL_CIPHER_RC2_64, OPENSSL_CIPHER_DES, OPENSSL_CIPHER_3DES, OPENSSL_CIPHER_AES_128_CBC, OPENSSL_CIPHER_AES_192_CBC, OPENSSL_CIPHER_AES_256_CBC.
Простые примеры использования

Пример 1: Базовое шифрование для одного получателя.

$data = "Конфиденциальное сообщение.";
file_put_contents('msg.txt', $data);

$cert = file_get_contents('recipient_cert.pem');
$headers = [];

$result = openssl_pkcs7_encrypt('msg.txt', 'encrypted.msg', $cert, $headers);

if ($result) {
    echo "Шифрование успешно.";
} else {
    echo "Ошибка шифрования.";
}
Шифрование успешно.

Пример 2: Шифрование с текстовым флагом и заголовками.

$data = "Конфиденциальное сообщение.\nНовая строка.";
file_put_contents('msg.txt', $data);

$cert = file_get_contents('recipient_cert.pem');
$headers = [
    'From' => 'sender@example.com',
    'To' => 'recipient@example.com',
    'Subject' => 'Важное письмо'
];

$result = openssl_pkcs7_encrypt('msg.txt', 'encrypted.msg', $cert, $headers, OPENSSL_PKCS7_TEXT);

var_dump($result);
bool(true)

Пример 3: Шифрование для нескольких получателей с указанием шифра.

$certs = [
    file_get_contents('recipient1_cert.pem'),
    file_get_contents('recipient2_cert.pem')
];

$headers = ['Subject' => 'Множественное шифрование'];
$result = openssl_pkcs7_encrypt(
    'plain.txt',
    'multi_enc.msg',
    $certs,
    $headers,
    0, // Без флагов
    OPENSSL_CIPHER_AES_256_CBC
);

echo $result ? 'Успех' : 'Неудача';
Успех
Похожие функции в PHP

Функция создает подпись PKCS #7 для указанного файла. Используется для цифровой подписи, а не для шифрования. Предпочтительна, когда требуется гарантировать целостность и аутентичность данных.

Обратная функция для расшифровки данных, зашифрованных с помощью openssl_pkcs7_encrypt. Требует закрытый ключ и сертификат получателя.

openssl_public_encrypt / openssl_private_decrypt

Функции для асимметричного шифрования небольших объемов данных (например, ключей сессии). Не используют формат PKCS #7. Подходят для шифрования данных в памяти, а не файлов.

openssl_seal / openssl_open

Функции для симметричного шифрования с асимметричной защитой ключа. Генерируют случайный ключ, который шифруется открытым ключом каждого получателя. Более эффективны для шифрования больших данных, чем прямое асимметричное шифрование.

Выбор функции зависит от задачи. openssl_pkcs7_encrypt применяется для шифрования файлов или сообщений в стандартном формате S/MIME, часто для электронной почты. openssl_seal подходит для общего шифрования данных, когда требуется несколько получателей.

Типичные ошибки

Ошибка 1: Неверный путь к файлу или отсутствие прав на чтение/запись.

$result = openssl_pkcs7_encrypt('missing.txt', 'output.msg', $cert, []);
if (!$result) {
    echo openssl_error_string();
}
error:02001002:system library:fopen:No such file or directory

Ошибка 2: Некорректный или поврежденный сертификат.

$badCert = "-----BEGIN CERTIFICATE-----\nINVALID_DATA\n-----END CERTIFICATE-----";
$result = openssl_pkcs7_encrypt('msg.txt', 'output.msg', $badCert, []);
echo $result ? 'Ok' : openssl_error_string();
error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag

Ошибка 3: Попытка шифрования пустого файла.

file_put_contents('empty.txt', '');
$result = openssl_pkcs7_encrypt('empty.txt', 'output.msg', $cert, []);
var_dump($result);
bool(false)

Ошибка 4: Несовместимость флагов, например, одновременное использование OPENSSL_PKCS7_TEXT и OPENSSL_PKCS7_BINARY не имеет смысла, но не вызовет явной ошибки, а флаги будут интерпретированы согласно внутренней логике.

Для избежания ошибок стоит проверять существование файлов, валидность сертификатов с помощью openssl_x509_read и обрабатывать возвращаемое значение функции.

Изменения в версиях PHP

В PHP 8.0.0 параметр cipher_algo стал обязательным. В предыдущих версиях он был опциональным, и по умолчанию использовался OPENSSL_CIPHER_RC2_40, что считалось слабым шифром.

До PHP 8.0.0 можно было вызывать функцию без этого аргумента:

// Работало до PHP 8.0
openssl_pkcs7_encrypt('input.txt', 'output.msg', $cert, $headers);

Начиная с PHP 8.0.0, необходимо явно указывать шифр:

// PHP 8.0 и новее
openssl_pkcs7_encrypt('input.txt', 'output.msg', $cert, $headers, 0, OPENSSL_CIPHER_AES_128_CBC);

Это изменение повысило безопасность по умолчанию, заставляя разработчиков явно выбирать алгоритм шифрования. Рекомендуется использовать стойкие алгоритмы, такие как AES (OPENSSL_CIPHER_AES_128_CBC, OPENSSL_CIPHER_AES_256_CBC).

Расширенные примеры
Шифрование с выбором алгоритма и сохранением в строку
Пример php
// Шифрование с явным указанием шифра и захватом вывода в переменную
$data = "Секретные данные для отчета №456.";
$inputFile = 'temp_input_' . uniqid() . '.txt';
$outputFile = 'temp_output_' . uniqid() . '.msg';

file_put_contents($inputFile, $data);

$cert = file_get_contents('recipient_cert.pem');
$headers = [
    'Content-Type' => 'text/plain; charset=utf-8',
    'X-Priority' => '1'
];

$success = openssl_pkcs7_encrypt(
    $inputFile,
    $outputFile,
    $cert,
    $headers,
    OPENSSL_PKCS7_TEXT,
    OPENSSL_CIPHER_AES_256_CBC
);

if ($success) {
    $encryptedContent = file_get_contents($outputFile);
    echo "Зашифрованное содержимое (первые 500 символов):\n";
    echo htmlspecialchars(substr($encryptedContent, 0, 500)) . '...';
} else {
    echo "Ошибка: " . openssl_error_string();
}

// Удаление временных файлов
unlink($inputFile);
unlink($outputFile);
Зашифрованное содержимое (первые 500 символов):
MIME-Version: 1.0
Content-Disposition: attachment; filename="smime.p7m"
Content-Type: application/x-pkcs7-mime; smime-type=enveloped-data; name="smime.p7m"
Content-Transfer-Encoding: base64

MIAGCSqGSIb3DQEHA6CAMIACAQAxggHXMIIB0wIBADCBpjCBmjELMAkGA1UEBhMCUlUxETAPBgNV
BAgMCNCa0LvRjtC60LAxFDASBgNVBAcMC9CQ0L3QuNC80LAxGzAZBgNVBAoMEtCf0YDQvtGC0LXQ...
Цепочка операций: шифрование и последующая подпись
Пример php
// Сначала шифруем, затем подписываем уже зашифрованный файл (реже наоборот).
$plainFile = 'document.txt';
$encryptedFile = 'encrypted.msg';
$signedFile = 'encrypted_signed.msg';

file_put_contents($plainFile, "Важный документ.");

// 1. Шифрование
$certEnc = file_get_contents('recipient_cert.pem');
$encResult = openssl_pkcs7_encrypt($plainFile, $encryptedFile, $certEnc, [], 0, OPENSSL_CIPHER_AES_128_CBC);

// 2. Подпись зашифрованного файла (отправителем)
$signerCert = file_get_contents('sender_cert.pem');
$signerKey = file_get_contents('sender_key.pem');
$signResult = openssl_pkcs7_sign($encryptedFile, $signedFile, $signerCert, [$signerKey], [], PKCS7_DETACHED);

echo "Шифрование: " . ($encResult ? 'OK' : 'FAIL') . "\n";
echo "Подпись: " . ($signResult ? 'OK' : 'FAIL') . "\n";
Шифрование: OK
Подпись: OK
Обработка бинарных данных (изображения)
Пример php
// Шифрование бинарного файла с флагом OPENSSL_PKCS7_BINARY
$binaryData = file_get_contents('image.jpg'); // Или любой бинарный контент
$inputBin = 'bin_input.dat';
$outputBin = 'bin_encrypted.msg';

file_put_contents($inputBin, $binaryData);

$cert = file_get_contents('recipient_cert.pem');
$headers = [
    'Content-Description' => 'Encrypted Image',
    'Content-Disposition' => 'attachment; filename="image.jpg"'
];

// Важно использовать OPENSSL_PKCS7_BINARY
$result = openssl_pkcs7_encrypt(
    $inputBin,
    $outputBin,
    $cert,
    $headers,
    OPENSSL_PKCS7_BINARY,
    OPENSSL_CIPHER_AES_128_CBC
);

if ($result) {
    $size = filesize($outputBin);
    echo "Файл зашифрован. Размер выходного файла: $size байт";
}
Файл зашифрован. Размер выходного файла: 3427 байт
Альтернативы в других языках
Python (cryptography)
from cryptography.hazmat.primitives.serialization import load_pem_public_key
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
# В Python нет прямой аналогии PKCS#7, обычно используют CMS (Cryptographic Message Syntax)
# или комбинируют шифрование. Пример гибридного шифрования:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
import os

# Генерация сессионного ключа
session_key = os.urandom(32)
# Загрузка сертификата получателя
with open("recipient_cert.pem", "rb") as cert_file:
    cert = load_pem_public_key(cert_file.read())
# Шифрование сессионного ключа
encrypted_key = cert.encrypt(
    session_key,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)
# Далее сессионным ключом шифруют данные симметричным алгоритмом (AES).
JavaScript (Node.js с библиотекой node-forge или crypto)
// Использование библиотеки 'node-forge' для работы с PKCS#7
const forge = require('node-forge');
// Шифрование аналогично, но API отличается.
// Пример создания CMS-сообщения (аналог PKCS#7):
const p7 = forge.pkcs7.createEnvelopedData();
p7.addRecipient(certificate);
p7.content = forge.util.createBuffer('Конфиденциальное сообщение.');
p7.encrypt();
const encrypted = forge.pkcs7.messageToPem(p7);

Openssl pkcs7 encrypt в MySQL

В MySQL нет встроенной функции для асимметричного шифрования в формате PKCS #7. Для шифрования данных обычно используются симметричные функции, такие как AES_ENCRYPT, или функции для работы с SSL-сертификатами на уровне соединения.

Основные отличия от PHP функции: в Python и JavaScript часто используется более низкоуровневый подход или библиотеки, реализующие стандарт CMS (преемник PKCS #7). Процесс часто разделяется на шифрование сессионного ключа и данных.

PHP openssl_pkcs7_encrypt function comments

En
Openssl pkcs7 encrypt Encrypt an S/MIME message