<div dir="ltr"><a class="gmail_plusreply" id="plusReplyChip-8" href="mailto:dev@ddvo.net" tabindex="-1">@David von Oheimb</a><div> Thank you so much for your deep investigation! <br></div><div>With subjectKeyIdentifier and authorityKeyIdentifier extensions, it works like a charm! <br></div><div><br></div><div>So, the former statements I found on <a href="https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_load_verify_locations.html">this page</a> only applies to CA cert, not EE cert. <br></div><div>How to pick up cert from trust store(or cert container as you say) <br></div><div>is decided by different implementation themselves, do I understand correctly?</div><div><br></div><div>Since GnuTls and golang could pick up the right cert in this kind of scenario, <br></div><div>they must implement their own logic to pick up the right cert, do you think OpenSSL</div><div>will implement this logic too? Or it's a more appropriate approach to just <br></div><div>use the extensions you suggested?<br></div><div><br></div><div>Regards,</div><div>Dingping<br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">David von Oheimb <<a href="mailto:dev@ddvo.net">dev@ddvo.net</a>> 于2020年12月26日周六 下午5:17写道:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div>
<div>On 25.12.20 00:35, 定平袁 wrote:<br>
</div>
<blockquote type="cite">
<div dir="ltr">
<div><a class="gmail_plusreply" id="gmail-m_-7525524058374870488m_1380141081388625801plusReplyChip-2" href="mailto:dev@ddvo.net" target="_blank">@David von Oheimb</a> I will update
to a new version and try again.</div>
</div>
</blockquote>
<p>Good. Ideally try also a current 3.0.0 alpha release because
there have been some changes to cert chain building and
verification recently.<br>
<br>
</p>
<blockquote type="cite">
<div dir="ltr">
<div> To append cert is to make sure new cert and old cert both
exist in trust store, thus when server switches cert, it can
be trusted by client.<br>
</div>
</div>
</blockquote>
Understood, but my point was on a different aspect:<br>
The chain building will take the first matching cert, so if you want
to prefer the new cert, it must be in the list *before* the old one
-<br>
in other words, prepend the new cert to the list rather than
appending to it.<br>
<br>
<blockquote type="cite">
<div dir="ltr">
<div>
<div>@Jochen actually, the certs have different SN, which
indeed is not consistent with the man doc</div>
</div>
</div>
</blockquote>
<p>Different certs with the same issuer indeed <b>must</b> have
different SNs (except in the special case I mention below). <br>
See also RFC 5280 section 4.1.2.2 <a href="https://tools.ietf.org/html/rfc5280#section-4.1.2.2" target="_blank">https://tools.ietf.org/html/rfc5280#section-4.1.2.2</a>:<br>
</p>
<pre> It MUST be unique for each certificate issued by a given CA
(i.e., the issuer name and serial number identify a unique certificate). </pre>
<p><br>
Yet there is a different inconsistency in what you write:</p>
<blockquote type="cite">
<div dir="ltr">
<div>
<div>The thing that confuses me is that CURL (compiled with
gnutls) and Golang works. <br>
</div>
<div>below is my ca.crt file, I am not sure where it went
wrong, maybe just my wrong behavior?</div>
</div>
</div>
</blockquote>
You refer to them as CA certs, but they are not: they do no have a
basicConstraints field with the cA bit set.<br>
And as far as I understand your scenario, they are not used to issue
other certs but by some (TLS) server,<br>
so they really are end-entity (EE) certs, not CA certs, and it looks
like this is correct in your application scenario.<br>
<br>
Directly trusted self-issued EE certs (which may be self-signed or
not) are a special situation.<br>
This has been clarified in RFC 6818 (which updates RFC 5280) <a href="https://tools.ietf.org/html/rfc6818#section-2" target="_blank">https://tools.ietf.org/html/rfc6818#section-2</a>:<br>
<pre>| Consistent with <a href="https://tools.ietf.org/html/rfc6818#section-3.4.61" target="_blank">Section 3.4.61</a> of X.509 (11/2008) [<a href="https://tools.ietf.org/html/rfc6818#ref-X.509" target="_blank">X.509</a>], we note
| that use of self-issued certificates and self-signed certificates
| issued by entities other than CAs are outside the scope of this
| specification. Thus, for example, a web server or client might
| generate a self-signed certificate to identify itself. These
| certificates and how a relying party uses them to authenticate
| asserted identities are both outside the scope of <a href="https://tools.ietf.org/html/rfc5280" target="_blank">RFC 5280</a>.</pre>
So the path building and verification, as well as other checks
defined RFC 5280, does not apply to them at all!<br>
They are essentially just a convenient container for a public key,
where it is optional to check expiration etc.<br>
<p><br>
Unfortunately, when using such certs for TLS connections etc.,
still verification is done on them, which may fail.<br>
After renaming your ca.crt file to ee.crt for clarity and
extracting the first cert in ee1.crt and the second one in
ee2.crt,<br>
when verifying these directly trusted certs one gets the problem
you reported:<br>
<br>
openssl verify -x509_strict -trusted ee.crt ee1.crt<br>
ee1.crt: OK<br>
</p>
<p>openssl verify -x509_strict -trusted ee.crt ee2.crt<br>
C = US, ST = CA, L = Palo Alto, O = VMware, CN =
nsxmanager.pks.vmware.local<br>
error 18 at 0 depth lookup: self signed certificate<br>
error ee2.crt: verification failed<br>
</p>
<p>So as I wrote before, unfortunately the path building picks up
the first matching cert from ee.crt,<br>
which is the one in ee1.crt (i.e., your old one), and does not try
the second one (i.e., your new one).<br>
This happens also with the latest OpenSSL pre-3.0.0 master.<br>
</p>
<p><br>
A solution is to add both the subjectKeyIdentifier and
authorityKeyIdentifier extensions to your certs,<br>
for instance like this:<br>
</p>
<p>echo >ee.cnf "<br>
prompt = no<br>
distinguished_name = my_server<br>
x509_extensions = my_exts<br>
[my_server]<br>
commonName = test<br>
[my_exts]<br>
basicConstraints = CA:false<br>
subjectKeyIdentifier=hash<br>
authorityKeyIdentifier = keyid"<br>
</p>
<p>openssl req -config ee.cnf -new -x509 -out ee1.crt -nodes -keyout
ee1.pem<br>
openssl req -config ee.cnf -new -x509 -out ee2.crt -nodes -keyout
ee2.pem<br>
cat ee1.crt ee2.crt >ee.crt</p>
<p>The subjectKeyIdentifier and authorityKeyIdentifier extensions
are generally recommend<br>
(and actually required to add for certs that are RFC 5280
compliant) <br>
because they help for correct chain building, and indeed also in
this case they do:<br>
</p>
<p>openssl verify -x509_strict -trusted ee.crt ee1.crt<br>
ee1.crt: OK<br>
openssl verify -x509_strict -trusted ee.crt ee2.crt<br>
ee2.crt: OK<br>
<br>
</p>
<p>Regards,</p>
<p> David<br>
</p>
<br>
</div>
</blockquote></div>