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.
This commit is contained in:
Vladimir Dubrovin 2026-08-26 09:42:21 +03:00
parent e8d6aa555a
commit fc544c4dff
2 changed files with 100 additions and 8 deletions

View File

@ -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
&#35; 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
</pre>
<p>
The extensions are not optional. Without <b>basicConstraints=CA:TRUE</b> and
<b>keyCertSign</b> the certificate is not accepted as a CA, and clients report
that they cannot get the local issuer certificate. <b>subjectKeyIdentifier</b>
is what certificates signed by this CA point back at.
</p>
<p>
They are given in a file rather than with <b>-addext</b> because LibreSSL, the
<b>openssl</b> command on macOS and some BSDs, does not apply -addext the same
way OpenSSL does. The form above behaves the same on both.
</p>
<p>
For MITM, import ca.crt into client browsers/OS as a trusted root CA.
</p>
<p>
@ -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
&#35; Check it the way a current client will
openssl verify -x509_strict -CAfile ca.crt server.crt
</pre>
<p>
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
<b>authorityKeyIdentifier</b>; other clients are moving the same way.
</p>
<p>
For a public https:// proxy, use a CA like Let's Encrypt instead of self-signed.
</p>
<p>
@ -886,6 +916,8 @@ cat > client.ext << 'EOF'
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment
extendedKeyUsage = clientAuth
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
EOF
&#35; Sign with CA
@ -908,8 +940,14 @@ Import client1.p12 into the client browser or OS certificate store.
&#35; 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
&#35; 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
&#35; 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
</pre>
<li><a name="PCRE"><i>How to use PCRE filtering (regular expressions)</i></a>
<p>

View File

@ -838,12 +838,32 @@ ssl_nocli
&#35; Генерация закрытого ключа CA
openssl genrsa -out ca.key 4096
&#35; Расширения, без которых сертификат не годится как CA
cat > ca.ext << 'EOF'
basicConstraints=critical,CA:TRUE
keyUsage=critical,keyCertSign,cRLSign
subjectKeyIdentifier=hash
EOF
&#35; Генерация сертификата 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
</pre>
<p>
Расширения обязательны. Без <b>basicConstraints=CA:TRUE</b> и
<b>keyCertSign</b> сертификат не принимается как CA, и клиент сообщает, что не
может получить сертификат издателя. <b>subjectKeyIdentifier</b> — то, на что
ссылаются подписанные этим CA сертификаты.
</p>
<p>
Расширения задаются файлом, а не через <b>-addext</b>, потому что LibreSSL —
команда <b>openssl</b> в macOS и некоторых BSD — обрабатывает -addext иначе,
чем OpenSSL. Приведённый вариант одинаково работает в обоих.
</p>
<p>
Для MITM импортируйте ca.crt в браузеры/ОС клиентов как доверенный корневой CA.
</p>
<p>
@ -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
&#35; Проверка так же, как это делает современный клиент
openssl verify -x509_strict -CAfile ca.crt server.crt
</pre>
<p>
Проверять следует строго, потому что именно так проверяет клиент. OpenSSL 3
добавляет идентификаторы ключей при подписании, а LibreSSL — нет, поэтому файл
расширений запрашивает их явно. Python начиная с 3.13 проверяет строго и
отвергает сертификат без <b>authorityKeyIdentifier</b>; другие клиенты идут тем
же путём.
</p>
<p>
Для публичного https:// прокси используйте CA вроде Let's Encrypt вместо самоподписанного.
</p>
<p>
@ -896,6 +926,8 @@ cat > client.ext << 'EOF'
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment
extendedKeyUsage = clientAuth
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
EOF
&#35; Подписание CA
@ -918,8 +950,14 @@ openssl pkcs12 -export -out client1.p12 \
&#35; 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
&#35; Сервер
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
&#35; Оба должны пройти проверку, которую делает современный клиент
openssl verify -x509_strict -CAfile ca.crt server.crt
openssl verify -x509_strict -CAfile ca.crt client.crt
</pre>
<li><a name="PCRE"><i>Как использовать PCRE-фильтрацию (регулярные выражения)</i></a>