fix(mbedtls): Raise error for certificate files with unsupported extension

gen_crt_bundle.py only parses files ending in .pem or .der, but silently
ignored anything else. A PEM certificate named e.g. ca.crt was skipped
without a word, and since the build invokes the script with -q, even the
"Successfully added 0 certificates" hint was suppressed. The build then
succeeded and embedded a bundle without the certificate, and the problem
only surfaced at runtime as a TLS verification failure.

A file passed directly via --input, which is what
CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH points at, is now expected
to be a certificate: an unsupported extension raises an InputError and
fails the build with a message naming the file and the two accepted
extensions. Files found while scanning a directory keep being skipped,
as a certificate directory may legitimately contain other files, but a
warning is now printed unconditionally so it is visible in the build log.

Also document the requirement in the Kconfig help text and in the
esp_crt_bundle documentation.

Closes https://github.com/espressif/esp-idf/issues/18933
This commit is contained in:
Pranav Sonawane
2026-08-04 15:44:22 +05:30
committed by Mahavir Jain
parent 4ddc55b7a1
commit d385023b85
6 changed files with 97 additions and 4 deletions

View File

@@ -516,6 +516,11 @@ menu "mbedTLS"
Name of the custom certificate directory or file. This path is evaluated
relative to the project root directory.
Certificates must be either PEM encoded with a .pem extension, or DER
encoded with a .der extension. Files with any other extension are not
parsed: pointing this option directly at such a file fails the build,
while such files inside a directory are skipped with a warning.
config MBEDTLS_CERTIFICATE_BUNDLE_DEPRECATED_LIST
bool "Add deprecated root certificates"
depends on MBEDTLS_CERTIFICATE_BUNDLE && !MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE

View File

@@ -46,6 +46,10 @@ except ImportError:
ca_bundle_bin_file = 'x509_crt_bundle'
# Only files with these extensions are parsed, the extension has to match the encoding of the file
PEM_FILE_EXTENSION = '.pem'
DER_FILE_EXTENSION = '.der'
quiet = False
@@ -70,27 +74,44 @@ class CertificateBundle:
def add_from_path(self, crts_path):
found = False
for file_path in os.listdir(crts_path):
found |= self.add_from_file(os.path.join(crts_path, file_path))
found |= self.add_from_file(os.path.join(crts_path, file_path), strict=False)
if found is False:
raise InputError('No valid x509 certificates found in %s' % crts_path)
def add_from_file(self, file_path):
def add_from_file(self, file_path, strict=True):
"""Parse a certificate file
Only files with a .pem or .der extension are parsed. If strict is True (the file was
given directly on the command line), any other extension is an error, otherwise
(while scanning a directory) the file is skipped with a warning.
"""
try:
if file_path.endswith('.pem'):
if file_path.endswith(PEM_FILE_EXTENSION):
status('Parsing certificates from %s' % file_path)
with open(file_path, 'r', encoding='utf-8') as f:
crt_str = f.read()
self.add_from_pem(crt_str)
return True
elif file_path.endswith('.der'):
elif file_path.endswith(DER_FILE_EXTENSION):
status('Parsing certificates from %s' % file_path)
with open(file_path, 'rb') as f:
crt_str = f.read()
self.add_from_der(crt_str)
return True
elif os.path.isfile(file_path):
msg = (
'Unsupported file extension in %s, certificates are only parsed from files with a '
'%s (PEM encoded) or %s (DER encoded) extension. '
'Please rename the file to match its encoding.'
% (file_path, PEM_FILE_EXTENSION, DER_FILE_EXTENSION)
)
if strict:
raise InputError(msg)
critical('Warning: skipping file. %s' % msg)
except ValueError:
critical('Invalid certificate in %s' % file_path)
raise InputError('Invalid certificate')

View File

@@ -1,6 +1,8 @@
#!/usr/bin/env python
import os
import shutil
import sys
import tempfile
import unittest
try:
@@ -22,6 +24,7 @@ verified_der_bundle = 'baltimore_crt_bundle'
verified_pem_bundle = 'entrust_crt_bundle'
invalid_test_file = 'invalid_crt.pem'
non_ascii_file = 'non_ascii_crt.pem'
unsupported_file = 'unsupported_ext_crt.crt'
ca_crts_all_file = 'cacrt_all.pem'
@@ -73,6 +76,37 @@ class GenCrtBundleTests(Py23TestCase):
bundle.add_from_pem('')
self.assertEqual(len(bundle.certificates), bundle_prev_len)
# A file given directly is expected to be a certificate, so an unknown extension is an error
def test_unsupported_extension_input(self):
bundle = gen_crt_bundle.CertificateBundle()
with self.assertRaisesRegex(gen_crt_bundle.InputError, 'Unsupported file extension'):
bundle.add_from_file(test_crts_path + unsupported_file)
self.assertEqual(len(bundle.certificates), 0)
# While scanning a directory, files with an unknown extension are skipped instead
def test_unsupported_extension_in_dir(self):
bundle = gen_crt_bundle.CertificateBundle()
self.assertFalse(bundle.add_from_file(test_crts_path + unsupported_file, strict=False))
self.assertEqual(len(bundle.certificates), 0)
# A directory holding both valid and unsupported files adds only the valid ones
with tempfile.TemporaryDirectory() as crts_dir:
shutil.copy(test_crts_path + unsupported_file, crts_dir)
shutil.copy(test_crts_path + pem_test_file, crts_dir)
bundle.add_from_path(crts_dir)
self.assertEqual(len(bundle.certificates), 1)
# A directory without any supported file is an error
with tempfile.TemporaryDirectory() as crts_dir:
shutil.copy(test_crts_path + unsupported_file, crts_dir)
with self.assertRaisesRegex(gen_crt_bundle.InputError, 'No valid x509 certificates found'):
bundle.add_from_path(crts_dir)
def test_non_ascii_crt_input(self):
bundle = gen_crt_bundle.CertificateBundle()

View File

@@ -0,0 +1,25 @@
Entrust Root Certification Authority
====================================
-----BEGIN CERTIFICATE-----
MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV
BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw
b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG
A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0
MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu
MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu
Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v
dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz
A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww
Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68
j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN
rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw
DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1
MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH
hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM
Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa
v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS
W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
-----END CERTIFICATE-----

View File

@@ -30,6 +30,10 @@ Most configuration is done through menuconfig. CMake generates the bundle accord
* :menuitem:`CONFIG_MBEDTLS_DEFAULT_CERTIFICATE_BUNDLE`: decide which certificates to include from the complete root certificate list.
* :menuitem:`CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH`: specify the path of any additional certificates to embed in the bundle.
.. note::
Only PEM encoded certificates with a ``.pem`` extension and DER encoded certificates with a ``.der`` extension are parsed. The extension must match the encoding of the file, for example a PEM encoded certificate saved as ``.crt`` is not accepted. If :ref:`CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH` points directly at a file with any other extension, the build fails; if such a file is found inside a certificate directory, it is skipped and a warning is printed.
To enable the bundle when using ESP-TLS simply pass the function pointer to the bundle attach function:
.. code-block:: c

View File

@@ -30,6 +30,10 @@ ESP x509 证书包 API 提供了一种简便的方法,帮助你安装自定义
* :menuitem:`CONFIG_MBEDTLS_DEFAULT_CERTIFICATE_BUNDLE`:决定添加证书列表中的哪些证书。
* :menuitem:`CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH`:指定要在证书包中嵌入的其他证书的路径。
.. note::
只有扩展名为 ``.pem`` 的 PEM 编码证书和扩展名为 ``.der`` 的 DER 编码证书会被解析。扩展名必须与文件的编码格式一致,例如保存为 ``.crt`` 的 PEM 编码证书不会被接受。如果 :ref:`CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH` 直接指向其他扩展名的文件,构建将失败;如果证书目录中存在此类文件,该文件会被跳过并打印警告。
要在使用 ESP-TLS 时启用证书包,将函数指针指向证书包的 attach 函数:
.. code-block:: c