Выбор между PKCS#12 и PEM встаёт каждый раз, когда ключ и сертификат переносят между Unix-серверами и платформами с собственными хранилищами ключей. PEM-файлы — это отдельные читаемые блоки: программы на основе OpenSSL загружают их напрямую; файл PKCS#12 (.p12 или .pfx) — единый зашифрованный контейнер, который операционные системы и Java импортируют за один шаг. Ключи и сертификаты те же, упаковка разная, и от алгоритмов упаковки зависит, пройдёт ли импорт.
Коротко о главном
| PEM | PKCS#12 (PFX) | |
|---|---|---|
| Спецификация | RFC 7468 (текстовая кодировка DER) | RFC 7292 (PKCS #12 v1.1), RFC 9579 (PBMAC1) |
| Формат | Текст, один или несколько блоков BEGIN … END | Двоичный контейнер DER из пакетов SafeBag |
| Что хранит | Всё, что вы положите в файл: ключ, сертификаты, CRL | Закрытые ключи, сертификаты, CRL, секреты, с понятными именами и идентификаторами ключей |
| Защита | Необязательная, для каждого ключа отдельно (зашифрованный PKCS#8) | Шифрование пакетов на основе пароля и MAC для контроля целостности |
| Защита по умолчанию в OpenSSL 3.6 | PBES2 с AES-256-CBC для зашифрованных ключей | PBES2, PBKDF2-HMAC-SHA256, AES-256-CBC, 2 048 итераций; MAC HMAC-SHA256 |
OpenSSL -legacy | Неприменимо | RC2-40 для сертификатов, 3DES для ключей, MAC HMAC-SHA1 |
| Типичные расширения | .pem, .crt, .key | .p12, .pfx |
| Кто читает | OpenSSL, веб-серверы, прокси, Go, Python, Node.js | Хранилище сертификатов Windows, Связка ключей macOS, Java KeyStore, браузеры, signtool |
Что внутри .pfx
Файл PKCS#12 — это структура PFX: аутентифицированный контейнер (authenticated safe) из блоков содержимого, в каждом из которых лежат пакеты. Закрытые ключи хранятся в записях pkcs8ShroudedKeyBag (зашифрованный PKCS#8), сертификаты — в записях certBag (часто тоже в зашифрованном блоке), а у пакетов есть атрибуты, например friendlyName и localKeyId, которые связывают ключ с его сертификатом. MAC, выведенный из пароля, защищает целостность всего файла; RFC 9579 (2024) добавляет PBMAC1, где ключ MAC выводится с помощью PBKDF2, а не устаревшей KDF из PKCS#12.
У PEM контейнера нет: серверу указывают файл ключа и файл сертификата (или цепочки) либо один файл с несколькими блоками. Защита — та, что есть у файла ключа, а на серверах её обычно нет, потому что процесс должен читать ключ, не дожидаясь, пока человек введёт пароль.
Конвертация
Скрипт ниже, запущенный с OpenSSL 3.6.5, упаковывает одноразовые ключ и сертификат в файл PKCS#12 и снова распаковывает его.
#!/bin/sh
dir=$(mktemp -d)
cd "$dir" || exit 1
openssl req -new -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -keyout key.pem \
-subj "/CN=example.com" -days 30 -out cert.pem 2>/dev/null
openssl pkcs12 -export -inkey key.pem -in cert.pem -name demo -passout pass:demo -out bundle.p12
openssl pkcs12 -in bundle.p12 -passin pass:demo -info -noout 2>&1 | grep -E "MAC:|Shrouded|PKCS7 Encrypted"
openssl pkcs12 -in bundle.p12 -passin pass:demo -nodes -out extracted.pem 2>/dev/null
grep -E "^-----BEGIN" extracted.pem
cd / && rm -rf "$dir"
MAC: sha256, Iteration 2048
PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
-----BEGIN CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
Чтобы добавить промежуточные сертификаты, передайте их через -certfile chain.pem; чтобы извлечь только сертификаты, используйте -nokeys, только ключ — -nocerts. Флаг -nodes (в новых релизах он называется -noenc) записывает ключ без шифрования, поэтому обращайтесь с полученным файлом осторожно.
Совместимость: современные и устаревшие алгоритмы
В OpenSSL 3.0 сменились алгоритмы экспорта по умолчанию: раньше это были RC2-40 и 3DES с MAC на основе HMAC-SHA1, теперь — AES-256 с MAC на основе HMAC-SHA256. Поддержка у многих программ, которые импортируют такие файлы, появилась позже:
- macOS: фреймворк Security от Apple импортирует современные файлы PKCS#12 начиная с macOS 15; в macOS 14 и более ранних версиях импорт завершается ошибкой «MAC verification failed during PKCS12 import (wrong password?)» даже при правильном пароле.
- Java: в JDK 9 PKCS#12 стал типом хранилища ключей по умолчанию; в JDK 16 (и в обновлениях 8 и 11 за 2021 год) собственные настройки Java по умолчанию сменились на PBES2 с AES-256 и MAC на основе HMAC-SHA256, а текущие версии читают файлы OpenSSL 3.
- Windows: текущие релизы импортируют PFX-файлы с AES-256; очень старым версиям Windows Server может понадобиться устаревший формат.
- OpenSSL 3 и старые файлы: файлы, в которых сертификаты зашифрованы RC2-40 (по умолчанию в OpenSSL 1.1; его же использует экспорт с
-legacy, если поддержка RC2 включена в сборку), загружаются только с флагом-legacy: он подключает провайдер legacy. Без этого флага OpenSSL 3.6.5 выводитerror:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupportedдляRC2-40-CBC, а Node.js 26 отклоняет файл с ошибкойUnsupported PKCS12 PFX data. Файлы, в которых используются только 3DES и MAC на основе HMAC-SHA1, загружаются без провайдера legacy.
Если современный файл не импортируется на заведомо старой системе, экспортируйте его командой openssl pkcs12 -export -legacy … или задайте алгоритмы явно (-keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg SHA1) и считайте, что при передаче такой файл защищён слабо.
Сообщения об ошибках здесь сбивают с толку: «MAC verification failed» или «wrong password» обычно означают не неверный пароль, а то, что программа, которая читает файл, не поддерживает алгоритм MAC или шифрования. Прежде чем подбирать пароли, посмотрите, какие алгоритмы использованы, командой openssl pkcs12 -info -noout (для старых файлов добавьте -legacy).
PKCS#12 как хранилище ключей
Java работает с файлом PKCS#12 как с хранилищем ключей (keystore): keytool -list -keystore bundle.p12 -storetype PKCS12 показывает его записи, а friendlyName становится псевдонимом (alias). Хранилище ключей может содержать несколько записей с ключами и доверенные сертификаты, чего PEM-файл не может выразить без соглашений об именовании. Хранилища доверенных сертификатов (truststore) — особый случай: хранилищу PKCS#12, в котором есть только сертификаты, нужны специфичные для Java атрибуты, помечающие эти сертификаты как доверенные, поэтому надёжнее всего создавать такое хранилище командой keytool -importcert. Большинство других платформ игнорируют эти атрибуты Java и при импорте сопоставляют ключи и сертификаты по localKeyId.
Что когда выбирать
- TLS на серверах Linux, обратных прокси, Kubernetes Ingress: ключ в PEM и сертификат с полной цепочкой.
- Импорт сертификатов в Windows IIS, Azure App Service и Key Vault: PKCS#12.
- TLS в Java и Spring Boot: хранилище ключей PKCS#12 (или PEM, который свежие версии Spring Boot принимают напрямую).
- Клиентские сертификаты для браузеров и мобильных устройств: PKCS#12 с надёжным паролем, причём файл передаётся отдельно от пароля.
- Подпись кода с помощью signtool или jarsigner: PKCS#12, если ключ вообще хранится в файле; с 2023 года публично доверенные ключи для подписи кода должны храниться на аппаратных устройствах.
- Анализ и отладка: PEM или инспектор PFX, который локально показывает пакеты и алгоритмы, не раскрывая ключ.
Заметки о миграции
- Включайте полную цепочку, когда собираете PKCS#12 для серверов или клиентов, начиная с конечного сертификата; отсутствующие промежуточные сертификаты — самая частая причина сбоев после импорта.
- Сверьте ключ и сертификат перед упаковкой: инструмент сопоставления ключа и сертификата сравнивает их открытые ключи.
- Выбирайте алгоритмы под самую старую систему, которая будет импортировать файл, а затем переходите на новые: когда старых платформ не останется, экспортируйте файл заново с современными настройками по умолчанию.
- Используйте надёжные случайные пароли для файлов PKCS#12, которые куда-либо передаются, поскольку файл можно атаковать офлайн; 2 048 итераций PBKDF2, которые OpenSSL использует по умолчанию, по меркам хранения паролей немного.
- Не оставляйте извлечённые ключи где попало: после конвертации
.pfxв PEM для сервера ограничьте права доступа к файлу и удалите временные копии.
Источники
- RFC 7292, PKCS #12 v1.1
- RFC 9579, PBMAC1 в PKCS #12
- RFC 7468, текстовые кодировки PEM
- RFC 5958, пакеты асимметричных ключей PKCS #8
- Руководство OpenSSL по openssl-pkcs12
- JDK-8228481, более стойкие алгоритмы PKCS #12 по умолчанию в Java
- Apple Developer Forums: поддержка современных файлов PKCS #12 в macOS 15