PKCS12, PEM или DER: что на самом деле нужно VPN-клиенту
Упаковка решает, импортирует ли клиент идентичность, а не то, можно ли ей доверять. Сравните PKCS12, PEM и DER, затем проверьте.
В обсуждениях сертификатов ходят два утверждения. Первое — что .p12 это надёжный вариант, а .pem — слабый; второе — что текстовый файл должен быть безопасным для отправки, потому что его можно прочитать. Ни одно не выдерживает минуты проверки.
PKCS12, PEM и DER описывают, как объекты сертификатов упаковываются и кодируются. Они не решают, кто вторая сторона, можно ли доверять издателю и нужен ли файлу пароль.
Что принимает клиент — вопрос совместимости. Что содержит файл — вопрос идентичности. Разделите эти два, и большинство ошибок импорта перестанут быть загадочными.
Три контейнера, одна объектная модель
Начните с того, что у вас действительно есть. Сертификат — это подписанное утверждение об открытом ключе. Закрытый ключ — отдельный материал, доказывающий, что вы владеете этим ключом. Цепочка — набор издателей, связывающих ваш сертификат с корнем, которому доверяет клиент. Все три существуют независимо от упаковки.
Файл PKCS12, обычно .p12 или .pfx, — это контейнер, определённый в RFC 7292, который может нести закрытый ключ, один или несколько сертификатов и цепочку в одном блоке, защищённом паролем, с псевдонимом, именующим идентичность. Файл PEM — это текстовая рамка по RFC 7468: тело base64 между строками BEGIN и END, и один файл может содержать несколько блоков подряд. DER — двоичное кодирование тех же структур ASN.1, а тело PEM — буквально DER, обёрнутый в base64.
Последнее предложение стоит запомнить. Преобразование PEM в DER переписывает рамку, а не содержимое, а обратное преобразование возвращает исходные байты.
| Container | Can hold | Does not decide |
|---|---|---|
| PKCS12 (.p12, .pfx) | Key, leaf certificate, chain, alias, password | Which root the client trusts |
| PEM (.pem, .crt, .cer) | Any number of base64 blocks | Whether the private key is present |
| DER (.der, .cer) | One binary structure | The object type, whatever the extension says |
Кодирование — не шифрование
base64 — это транспортный алфавит. Он ничего не скрывает, и любой может декодировать блок PEM в оболочке. Файл DER двоичный и столь же публичный. Ни одно из свойств не является защитой, и ни одно не подразумевает наличие пароля.
Настоящая защита находится в материале ключа. Пакет PKCS12 может шифровать своё содержимое ключом, производным от пароля. Закрытый ключ PEM можно зашифровать старым блоком заголовка или хранить в PKCS#8 EncryptedPrivateKeyInfo. Сертификат сам по себе не хранит ничего секретного, поэтому экспорт, который дал только сертификат, оставляет клиента без возможности аутентификации, в каком бы формате вы его ни упаковали.
Расширение — подсказка, а не договор. Файлы, оканчивающиеся на .cer и .crt, встречаются в обеих кодировках, а устройства достаточно часто экспортируют сертификаты DER под именами .pem, так что угадывание по суффиксу стоит потраченного дня. Читайте первые байты: файл PEM начинается со строки BEGIN, а DER начинается с 0x30, потому что внешняя структура — SEQUENCE.
Почему импорт на самом деле не удаётся
Сообщения об ошибках клиента сжимают несколько разных проблем в одну фразу о недействительном сертификате, поэтому разделяйте их по причине. Самая частая — несовпадение кодировки: клиент, разбирающий PEM, возвращает ошибку разбора на файле DER, и решение — преобразование, а не новый сертификат. Вторая — отсутствующий ключ, когда сертификат в порядке, а идентичность доказать нельзя. Третья — цепочка, когда пакет без промежуточного сертификата выглядит правильным в диалоге импорта и терпит неудачу во время рукопожатия.
Параметры шифрования вызывают четвёртую, и она выглядит точно как неверный пароль. Старые устройства экспортировали файлы PKCS12 с RC2-40 и 3DES, которые OpenSSL 3 отвергает, если не загружен legacy-провайдер. Повторный экспорт с устройства с AES-256-CBC и PBKDF2 — долговечное решение. Оставлять старые шифры постоянно включёнными на клиентской машине ради одного файла — плохая сделка.
Пятая — неоднозначность. Контейнер с двумя ключами и двумя сертификатами или файл PEM с тремя блоками CERTIFICATE заставляют клиента выбирать, и его выбор часто просто первый элемент. Когда туннель поднимается с неверной идентичностью, обычно это и есть причина.
Проверьте перед преобразованием
Читайте структуру, не печатая секретов. openssl pkcs12 -in bundle.p12 -info -noout сообщает алгоритмы шифрования и псевдонимы, подавляя вывод ключа и сертификата, а руководство openssl-pkcs12 описывает флаги. Убрать -noout и добавить -nodes — это для создания файлов, которые вы собираетесь хранить, а не для быстрого взгляда, потому что так закрытый ключ записывается открыто.
Для сертификата openssl x509 -in cert.pem -noout -subject -issuer -dates отвечает на то, на что не ответит диалог импорта, а добавление -ext subjectAltName показывает имена, для которых сертификат действительно действителен. Если файл DER, добавьте -inform DER; этот единственный флаг решает большинство ошибок unable-to-load.
Сопоставление ключа с сертификатом — механическая работа, и её стоит выполнить перед любым импортом:
openssl x509 -in cert.pem -noout -pubkey | openssl sha256openssl pkey -in key.pem -pubout | openssl sha256
Одинаковые дайджесты означают, что пара подходит друг другу. Разные дайджесты означают, что у вас неверный ключ, и никакое преобразование формата это не изменит.
Что формат никогда не меняет
Решения о доверии принимаются над контейнером. Клиент проходит цепочку до корня в своём хранилище, проверяет окно действия, проверяет расширенное использование ключа для аутентификации клиента, а на стороне сервера проверяет, что имя, к которому он обратился, есть в сертификате. Ни один из этих шагов не читает расширение файла.
Поэтому переименование файла до исчезновения ошибки — такой надёжный способ терять время. Импорт удаётся, идентичность по-прежнему неверна, и сбой возвращается во время рукопожатия, где сообщение менее полезно. Поэтому же пакет, экспортированный два года назад, импортируется чисто и всё равно отклоняется: срок действия путешествовал внутри сертификата, а не в имени файла.
Порядок, избегающий переделки
Спросите в документации клиента, какие кодировки он принимает и какой контейнер ожидает для идентичности клиента, затем посмотрите, что вам дали. Если клиенту нужна одна импортируемая идентичность, а у вас отдельные файлы PEM, соберите пакет один раз с паролем, который вы контролируете, и сохраните исходники. Если нужен PEM, извлеките ключ и цепочку, а затем удалите промежуточные файлы тем процессом обработки учётных данных, который ваша команда уже использует.
После импорта проверяйте идентичность, а не диалог: субъект, издатель, срок действия и имя, которое клиент представит. Дружелюбное имя в списке — это метка, а не проверенная идентичность. Если затем туннель не работает, вы отлаживаете аутентификацию или маршрутизацию, а не упаковку, и это гораздо более короткий разговор.