Verify errorcode = 20 : unable to get local issuer certificate

让人想犯罪 __ 提交于 2020-01-20 04:23:05

问题


I have a certificate chain in server:

Certificate chain
 0 s:/******/O=Foobar International BV/OU****
   i:/C=US/O=Symantec Corporation/OU=Symantec Trust Network/****

 1 s:/C=US/O=Symantec Corporation/OU=Symantec Trust Network/****
   i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=**** - G5

 2 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=**** - G5
   i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority

And my local root CA certificate is:

   s:/C=US/O=Symantec Corporation/OU=Symantec Trust Network/****
   i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=**** - G5

And I am using this snippet to verify the certificate:

//gcc -lssl -lcrypto -o certverify certverify.c 

#include <openssl/ssl.h>
#include <openssl/asn1.h>
#include <openssl/bio.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <openssl/pem.h>
#include <openssl/x509v3.h>
#include <openssl/err.h>
#include <openssl/conf.h>
#include <string.h>

int main() {

  const char ca_bundlestr[] = "./ca-bundle.pem";
  const char cert_filestr[] = "./cert-file.pem";

  BIO              *certbio = NULL;
  BIO               *outbio = NULL;
  X509          *error_cert = NULL;
  X509                *cert = NULL;
  X509_NAME    *certsubject = NULL;
  X509_STORE         *store = NULL;
  X509_STORE_CTX  *vrfy_ctx = NULL;
  int ret;

  /* ---------------------------------------------------------- *
   * These function calls initialize openssl for correct work.  *
   * ---------------------------------------------------------- */
  OpenSSL_add_all_algorithms();
  ERR_load_BIO_strings();
  ERR_load_crypto_strings();

  /* ---------------------------------------------------------- *
   * Create the Input/Output BIO's.                             *
   * ---------------------------------------------------------- */
  certbio = BIO_new(BIO_s_file());
  outbio  = BIO_new_fp(stdout, BIO_NOCLOSE);

  /* ---------------------------------------------------------- *
   * Initialize the global certificate validation store object. *
   * ---------------------------------------------------------- */
  if (!(store=X509_STORE_new()))
     BIO_printf(outbio, "Error creating X509_STORE_CTX object\n");

  /* ---------------------------------------------------------- *
   * Create the context structure for the validation operation. *
   * ---------------------------------------------------------- */
  vrfy_ctx = X509_STORE_CTX_new();

  /* ---------------------------------------------------------- *
   * Load the certificate and cacert chain from file (PEM).     *
   * ---------------------------------------------------------- */
  ret = BIO_read_filename(certbio, cert_filestr);
  if (! (cert = PEM_read_bio_X509(certbio, NULL, 0, NULL))) {
    BIO_printf(outbio, "Error loading cert into memory\n");
    exit(-1);
  }

  ret = X509_STORE_load_locations(store, ca_bundlestr, NULL);
  if (ret != 1)
    BIO_printf(outbio, "Error loading CA cert or chain file\n");

  /* ---------------------------------------------------------- *
   * Initialize the ctx structure for a verification operation: *
   * Set the trusted cert store, the unvalidated cert, and any  *
   * potential certs that could be needed (here we set it NULL) *
   * ---------------------------------------------------------- */
  X509_STORE_CTX_init(vrfy_ctx, store, cert, NULL);

  /* ---------------------------------------------------------- *
   * Check the complete cert chain can be build and validated.  *
   * Returns 1 on success, 0 on verification failures, and -1   *
   * for trouble with the ctx object (i.e. missing certificate) *
   * ---------------------------------------------------------- */
  ret = X509_verify_cert(vrfy_ctx);
  BIO_printf(outbio, "Verification return code: %d\n", ret);

  if(ret == 0 || ret == 1)
  BIO_printf(outbio, "Verification result text: %s\n",
             X509_verify_cert_error_string(vrfy_ctx->error));

  /* ---------------------------------------------------------- *
   * The error handling below shows how to get failure details  *
   * from the offending certificate.                            *
   * ---------------------------------------------------------- */
  if(ret == 0) {
    /*  get the offending certificate causing the failure */
    error_cert  = X509_STORE_CTX_get_current_cert(vrfy_ctx);
    certsubject = X509_NAME_new();
    certsubject = X509_get_subject_name(error_cert);
    BIO_printf(outbio, "Verification failed cert:\n");
    X509_NAME_print_ex(outbio, certsubject, 0, XN_FLAG_MULTILINE);
    BIO_printf(outbio, "\n");
  }

  /* ---------------------------------------------------------- *
   * Free up all structures                                     *
   * ---------------------------------------------------------- */
  X509_STORE_CTX_free(vrfy_ctx);
  X509_STORE_free(store);
  X509_free(cert);
  BIO_free_all(certbio);
  BIO_free_all(outbio);
  exit(0);
}

But this code return following output:

Verification return code: 0
Verification result text: unable to get issuer certificate
Verification failed cert:
countryName               = US
organizationName          = Symantec Corporation
organizationalUnitName    = Symantec Trust Network
commonName                = Symantec Class 3 Secure Server CA - G4

What's wrong here?


回答1:


Your root CA uses probably the same public key as the first intermediate CA in chain (below the host certificate) and you have probably no root-CA which can be used to trust the last chain certificate. Such setups are not very common, but actually happen. Unfortunately OpenSSL has problems with this setup and will only try to verify the longest chain, even if a shorter chain provides already the necessary trust.

There is a bug entry for this OpenSSL problem, but nobody from the OpenSSL developers ever took care of it. You can also find a patch if you are looking for X509_V_FLAG_TRUSTED_FIRST. It looks like that OpenSSL 1.0.2 (not yet released) will have this option too.

From my understanding only OpenSSL has this kind of problem, i.e. neither NSS (Firefox, Chrome on Desktop) nor SChannel (Microsoft).




回答2:


I think Steffen probably helped you solve the problem. But here's a small nitpick that may have side stepped the bug you are experiencing and improved your security posture.

const char ca_bundlestr[] = "./ca-bundle.pem";

You don't need the CA bundle. You only need Verisign's Class 3 Public Primary Certification Authority (G5). You can get the one CA cert needed from Verisign at Use of Root Certificates.

Its an improvement in your security posture because you're allowing any CA to certify the server's certificate (even wrong ones), and not using the one known to certify the server's certificate (Verisign).


And I am using this snippet to verify the certificate ...

If you want to see an example of a simple TLS client, then check out SSL/TLS Client on the OpenSSL wiki. It provides an example of fetching random numbers from random.org. It won't take much work to change it to example.com.

Note well: OpenSSL does not perform hostname matching during validation. You still need to do it yourself if you are using OpenSSL 1.0.2, 1.0.1, 1.0.0 and lesser versions. OpenSSL provides hostname matching in 1.1.0, but its not available yet.

The sample code to extract the hostnames from the Common Name (CN) and Subject Alt Names (SAN) in the X.509 certificate is provided in the SSL/TLS Client, but you will have to provided the actual matching code.


Based on the information in the comments, you need the certificate: "Symantec Class 3 Secure Server CA - G5". Below is what it looks like when providing the proper anchor - it ends in a Verify return code: 0 (ok) (and not the error 20).

The "Symantec Class 3 Secure Server CA - G5" is the one with the fingerprint 4e b6 d5 78 49 9b 1c cf 5f 58 1e ad 56 be 3d 9b 67 44 a5 e5. You can fetch it from Verisign's Use of Root Certificates.

The CAfile option being used by s_client (below) is set inside s_client.c with a call to SSL_CTX_load_verify_locations. Its set to the one CA needed to certify the server's certificate, and not the CA Zoo (i.e., cacerts.pem).


You can check the Subject Alternate Names (SAN) in the certificate with $ openssl s_client -connect www.smartbabymonitor.ugrow.example.com:443 | openssl x509 -text -noout. You will be OK because the host www.smartbabymonitor.ugrow.example.com is listed in the SAN. You could even add the -servername option to the command to use Server Name Indication (SNI).


$ openssl s_client -showcerts -connect www.smartbabymonitor.ugrow.example.com:443 -CAfile VeriSign-Class\ 3-Public-Primary-Certification-Authority-G5.pem 
CONNECTED(00000003)
depth=3 C = US, O = "VeriSign, Inc.", OU = Class 3 Public Primary Certification Authority
verify return:1
depth=2 C = US, O = "VeriSign, Inc.", OU = VeriSign Trust Network, OU = "(c) 2006 VeriSign, Inc. - For authorized use only", CN = VeriSign Class 3 Public Primary Certification Authority - G5
verify return:1
depth=1 C = US, O = Symantec Corporation, OU = Symantec Trust Network, CN = Symantec Class 3 Secure Server CA - G4
verify return:1
depth=0 C = NL, ST = Netherlands, L = Eindhoven, O = Example International BV, OU = Consumer Lifestyle, CN = smartbabymonitor.ugrow.example.com
verify return:1
---
Certificate chain
 0 s:/C=NL/ST=Netherlands/L=Eindhoven/O=Example International BV/OU=Consumer Lifestyle/CN=smartbabymonitor.ugrow.example.com
   i:/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 Secure Server CA - G4
-----BEGIN CERTIFICATE-----
MIIF+DCCBOCgAwIBAgIQa0fyuH2bp1ucngiNHVoV4jANBgkqhkiG9w0BAQsFADB+
MQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAd
...
+eGxGqm8e1jgxB/fQePrh1vG4V40nr0cBKh6t52HmksBCfM0wOlMMJyUYiO0p44W
s4nxNrvMJS6e4bwdECI0UNhJznWr0tAu+ilFoTsfOlQpngCBDJEkZYr3mRjpIjX8
Sz4+hGzIhZVyjDvbcVCrsvCpM67cU2rQpJ2nkYM4ol/z6VDRs/G5aPiXe7o=
-----END CERTIFICATE-----
 1 s:/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 Secure Server CA - G4
   i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
-----BEGIN CERTIFICATE-----
MIIFODCCBCCgAwIBAgIQUT+5dDhwtzRAQY0wkwaZ/zANBgkqhkiG9w0BAQsFADCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
...
QGX0InLNmfiIEfXzf+YzguaoxX7+0AjiJVgIcWjmzaLmFN5OUiQt/eV5E1PnXi8t
TRttQBVSK/eHiXgSgW7ZTaoteNTCLD0IX4eRnh8OsN4wUmSGiaqdZpwOdgyA8nTY
Kvi4Os7X1g8RvmurFPW9QaAiY4nxug9vKWNmLT+sjHLF+8fk1A/yO0+MKcc=
-----END CERTIFICATE-----
 2 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
   i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
-----BEGIN CERTIFICATE-----
MIIE0DCCBDmgAwIBAgIQJQzo4DBhLp8rifcFTXz4/TANBgkqhkiG9w0BAQUFADBf
MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
...
A4GBABMC3fjohgDyWvj4IAxZiGIHzs73Tvm7WaGY5eE43U68ZhjTresY8g3JbT5K
lCDDPLq9ZVTGr0SzEK0saz6r1we2uIFjxfleLuUqZ87NMwwq14lWAyMfs77oOghZ
tOxFNfeKW/9mz1Cvxm1XjRl4t7mi0VfqH5pLr7rJjhJ+xr3/
-----END CERTIFICATE-----
---
Server certificate
subject=/C=NL/ST=Netherlands/L=Eindhoven/O=Example International BV/OU=Consumer Lifestyle/CN=smartbabymonitor.ugrow.example.com
issuer=/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 Secure Server CA - G4
---
No client certificate CA names sent
---
SSL handshake has read 4805 bytes and written 434 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: F1B9C9DFA3CFC6CB3F958FAD4ECBBAFA0E72EA8A86F6AC9601CF8204819DB0F0
    Session-ID-ctx: 
    Master-Key: EC4C5B32E60B5A0458BC85CC02529EA18DE61AFB8583D85D275C2822AC84E0E5E0C5B5E2C3C2D90F8B6E0EBB518EAA99
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 27 e0 fb b8 dd c9 9f 29-31 85 2b 6c d1 5a b3 d1   '......)1.+l.Z..
    0010 - 55 d6 e4 8a 4d f5 ef 2e-51 95 21 90 47 9d b6 0a   U...M...Q.!.G...
    0020 - df a5 d2 10 3d 03 e5 07-41 81 92 09 30 0e 08 3d   ....=...A...0..=
    0030 - fc ea 24 93 29 ed 60 9a-d0 d9 57 88 e4 4d 18 e3   ..$.).`...W..M..
    0040 - ba aa 97 ee bf 39 9e 5b-76 5b 76 f7 81 c4 03 08   .....9.[v[v.....
    0050 - fb b9 a3 4f 11 b0 99 4c-8c f2 a6 8a 9a e4 fe c6   ...O...L........
    0060 - 0d 7b 6d a7 5b 53 b5 33-15 4f c4 ab 6b 29 7b 8f   .{m.[S.3.O..k){.
    0070 - ec 00 7f b2 6f 91 e4 ca-63 45 58 73 3a 78 8b 29   ....o...cEXs:x.)
    0080 - 44 fc d5 e8 ad 4d dd 9c-22 df 50 eb d5 bf b9 90   D....M..".P.....
    0090 - d8 6a 7d 6d bd 61 f2 63-07 75 8b d0 fc 40 64 76   .j}m.a.c.u...@dv
    00a0 - 2b 97 53 aa 47 bc 3d d1-76 aa 8a 07 e1 60 14 d1   +.S.G.=.v....`..
    00b0 - f7 88 8f f6 d9 b9 6b 0c-64 96 b5 f0 46 73 27 d6   ......k.d...Fs'.

    Start Time: 1419835334
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---


来源:https://stackoverflow.com/questions/27599985/verify-errorcode-20-unable-to-get-local-issuer-certificate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!