From fc544c4dff98bde4163da8b10c3b33eb427aba12 Mon Sep 17 00:00:00 2001 From: Vladimir Dubrovin <3proxy@3proxy.ru> Date: Wed, 26 Aug 2026 09:42:21 +0300 Subject: [PATCH] Fix the certificate recipes in the howtos The CA was created with no extensions, so it is not usable as a CA and clients report that they cannot get the local issuer certificate. Add basicConstraints, keyCertSign and a subject key identifier, in a file rather than through -addext, which LibreSSL - the openssl on macOS and some BSDs - does not apply the same way. Ask for the key identifiers on the signed certificates too: OpenSSL 3 adds them when it signs and LibreSSL does not, and Python has verified strictly since 3.13, refusing a chain whose certificate carries no authorityKeyIdentifier. Finish with openssl verify -x509_strict, which is the check the client will make. Both recipes were run against OpenSSL 3.6 and LibreSSL 3.3: the old one fails strict verification, the new one passes on both. --- doc/html/howtoe.html | 54 ++++++++++++++++++++++++++++++++++++++++---- doc/html/howtor.html | 54 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 100 insertions(+), 8 deletions(-) diff --git a/doc/html/howtoe.html b/doc/html/howtoe.html index 117e460..14b9747 100644 --- a/doc/html/howtoe.html +++ b/doc/html/howtoe.html @@ -828,12 +828,32 @@ This creates an HTTPS proxy (ssl_serv) that accepts TLS connections from clients # Generate CA private key openssl genrsa -out ca.key 4096 +# Extensions that make the certificate usable as a CA +cat > ca.ext << 'EOF' +basicConstraints=critical,CA:TRUE +keyUsage=critical,keyCertSign,cRLSign +subjectKeyIdentifier=hash +EOF + # Generate CA certificate (valid for 10 years) -openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \ +openssl req -new -nodes -key ca.key \ -subj "/C=US/ST=State/L=City/O=MyOrg/CN=My CA" \ - -out ca.crt + -out ca.csr +openssl x509 -req -in ca.csr -signkey ca.key -sha256 -days 3650 \ + -extfile ca.ext -out ca.crt

+The extensions are not optional. Without basicConstraints=CA:TRUE and +keyCertSign the certificate is not accepted as a CA, and clients report +that they cannot get the local issuer certificate. subjectKeyIdentifier +is what certificates signed by this CA point back at. +

+

+They are given in a file rather than with -addext because LibreSSL, the +openssl command on macOS and some BSDs, does not apply -addext the same +way OpenSSL does. The form above behaves the same on both. +

+

For MITM, import ca.crt into client browsers/OS as a trusted root CA.

@@ -866,8 +886,18 @@ EOF openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -out server.crt -days 365 -sha256 \ -extfile server.ext + +# Check it the way a current client will +openssl verify -x509_strict -CAfile ca.crt server.crt

+Verify strictly, because that is what the client does. OpenSSL 3 adds the +subject and authority key identifiers when it signs and LibreSSL does not, +which is why the extensions file asks for them by name. Python has verified +strictly since 3.13 and refuses a certificate carrying no +authorityKeyIdentifier; other clients are moving the same way. +

+

For a public https:// proxy, use a CA like Let's Encrypt instead of self-signed.

@@ -886,6 +916,8 @@ cat > client.ext << 'EOF' basicConstraints=CA:FALSE keyUsage = digitalSignature, nonRepudiation, keyEncipherment extendedKeyUsage = clientAuth +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer EOF # Sign with CA @@ -908,8 +940,14 @@ Import client1.p12 into the client browser or OS certificate store. # CA openssl genrsa -out ca.key 4096 -openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \ - -subj "/CN=3proxy CA" -out ca.crt +cat > ca.ext << 'EOF' +basicConstraints=critical,CA:TRUE +keyUsage=critical,keyCertSign,cRLSign +subjectKeyIdentifier=hash +EOF +openssl req -new -nodes -key ca.key -subj "/CN=3proxy CA" -out ca.csr +openssl x509 -req -in ca.csr -signkey ca.key -sha256 -days 3650 \ + -extfile ca.ext -out ca.crt # Server openssl genrsa -out server.key 2048 @@ -919,6 +957,8 @@ basicConstraints=CA:FALSE keyUsage = keyEncipherment extendedKeyUsage = serverAuth subjectAltName = DNS:localhost,DNS:proxy,IP:127.0.0.1 +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer EOF openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -out server.crt -days 365 -sha256 -extfile server.ext @@ -929,11 +969,17 @@ openssl req -new -key client.key -subj "/CN=client" -out client.csr cat > client.ext << 'EOF' basicConstraints=CA:FALSE extendedKeyUsage = clientAuth +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer EOF openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -out client.crt -days 365 -sha256 -extfile client.ext openssl pkcs12 -export -out client.p12 -passout pass: \ -inkey client.key -in client.crt -certfile ca.crt + +# Both must pass the checks a current client applies +openssl verify -x509_strict -CAfile ca.crt server.crt +openssl verify -x509_strict -CAfile ca.crt client.crt

  • How to use PCRE filtering (regular expressions)

    diff --git a/doc/html/howtor.html b/doc/html/howtor.html index 2e55ad0..4513e2b 100644 --- a/doc/html/howtor.html +++ b/doc/html/howtor.html @@ -838,12 +838,32 @@ ssl_nocli # Генерация закрытого ключа CA openssl genrsa -out ca.key 4096 +# Расширения, без которых сертификат не годится как CA +cat > ca.ext << 'EOF' +basicConstraints=critical,CA:TRUE +keyUsage=critical,keyCertSign,cRLSign +subjectKeyIdentifier=hash +EOF + # Генерация сертификата CA (действителен 10 лет) -openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \ +openssl req -new -nodes -key ca.key \ -subj "/C=RU/ST=Region/L=City/O=MyOrg/CN=My CA" \ - -out ca.crt + -out ca.csr +openssl x509 -req -in ca.csr -signkey ca.key -sha256 -days 3650 \ + -extfile ca.ext -out ca.crt

    +Расширения обязательны. Без basicConstraints=CA:TRUE и +keyCertSign сертификат не принимается как CA, и клиент сообщает, что не +может получить сертификат издателя. subjectKeyIdentifier — то, на что +ссылаются подписанные этим CA сертификаты. +

    +

    +Расширения задаются файлом, а не через -addext, потому что LibreSSL — +команда openssl в macOS и некоторых BSD — обрабатывает -addext иначе, +чем OpenSSL. Приведённый вариант одинаково работает в обоих. +

    +

    Для MITM импортируйте ca.crt в браузеры/ОС клиентов как доверенный корневой CA.

    @@ -876,8 +896,18 @@ EOF openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -out server.crt -days 365 -sha256 \ -extfile server.ext + +# Проверка так же, как это делает современный клиент +openssl verify -x509_strict -CAfile ca.crt server.crt

    +Проверять следует строго, потому что именно так проверяет клиент. OpenSSL 3 +добавляет идентификаторы ключей при подписании, а LibreSSL — нет, поэтому файл +расширений запрашивает их явно. Python начиная с 3.13 проверяет строго и +отвергает сертификат без authorityKeyIdentifier; другие клиенты идут тем +же путём. +

    +

    Для публичного https:// прокси используйте CA вроде Let's Encrypt вместо самоподписанного.

    @@ -896,6 +926,8 @@ cat > client.ext << 'EOF' basicConstraints=CA:FALSE keyUsage = digitalSignature, nonRepudiation, keyEncipherment extendedKeyUsage = clientAuth +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer EOF # Подписание CA @@ -918,8 +950,14 @@ openssl pkcs12 -export -out client1.p12 \ # CA openssl genrsa -out ca.key 4096 -openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \ - -subj "/CN=3proxy CA" -out ca.crt +cat > ca.ext << 'EOF' +basicConstraints=critical,CA:TRUE +keyUsage=critical,keyCertSign,cRLSign +subjectKeyIdentifier=hash +EOF +openssl req -new -nodes -key ca.key -subj "/CN=3proxy CA" -out ca.csr +openssl x509 -req -in ca.csr -signkey ca.key -sha256 -days 3650 \ + -extfile ca.ext -out ca.crt # Сервер openssl genrsa -out server.key 2048 @@ -929,6 +967,8 @@ basicConstraints=CA:FALSE keyUsage = keyEncipherment extendedKeyUsage = serverAuth subjectAltName = DNS:localhost,DNS:proxy,IP:127.0.0.1 +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer EOF openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -out server.crt -days 365 -sha256 -extfile server.ext @@ -939,11 +979,17 @@ openssl req -new -key client.key -subj "/CN=client" -out client.csr cat > client.ext << 'EOF' basicConstraints=CA:FALSE extendedKeyUsage = clientAuth +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer EOF openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -out client.crt -days 365 -sha256 -extfile client.ext openssl pkcs12 -export -out client.p12 -passout pass: \ -inkey client.key -in client.crt -certfile ca.crt + +# Оба должны пройти проверку, которую делает современный клиент +openssl verify -x509_strict -CAfile ca.crt server.crt +openssl verify -x509_strict -CAfile ca.crt client.crt

  • Как использовать PCRE-фильтрацию (регулярные выражения)