<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>