Перейти к содержимому
ecdsa.com

PKCS#12 (.pfx/.p12) или PEM: упаковка ключа и сертификата

PEM хранит ключи и сертификаты отдельными текстовыми блоками. PKCS#12 упаковывает их в один защищённый паролем двоичный файл, который Windows, macOS, Java и браузеры импортируют напрямую.
Что сравниваетсяPKCS #12 (PFX)PEM

Выбор между PKCS#12 и PEM встаёт каждый раз, когда ключ и сертификат переносят между Unix-серверами и платформами с собственными хранилищами ключей. PEM-файлы — это отдельные читаемые блоки: программы на основе OpenSSL загружают их напрямую; файл PKCS#12 (.p12 или .pfx) — единый зашифрованный контейнер, который операционные системы и Java импортируют за один шаг. Ключи и сертификаты те же, упаковка разная, и от алгоритмов упаковки зависит, пройдёт ли импорт.

Коротко о главном

PEMPKCS#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.6PBES2 с 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 и снова распаковывает его.

OpenSSL 3.6: из PEM в 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, который локально показывает пакеты и алгоритмы, не раскрывая ключ.

Заметки о миграции

  1. Включайте полную цепочку, когда собираете PKCS#12 для серверов или клиентов, начиная с конечного сертификата; отсутствующие промежуточные сертификаты — самая частая причина сбоев после импорта.
  2. Сверьте ключ и сертификат перед упаковкой: инструмент сопоставления ключа и сертификата сравнивает их открытые ключи.
  3. Выбирайте алгоритмы под самую старую систему, которая будет импортировать файл, а затем переходите на новые: когда старых платформ не останется, экспортируйте файл заново с современными настройками по умолчанию.
  4. Используйте надёжные случайные пароли для файлов PKCS#12, которые куда-либо передаются, поскольку файл можно атаковать офлайн; 2 048 итераций PBKDF2, которые OpenSSL использует по умолчанию, по меркам хранения паролей немного.
  5. Не оставляйте извлечённые ключи где попало: после конвертации .pfx в PEM для сервера ограничьте права доступа к файлу и удалите временные копии.

Источники

Все сравнения