验证errorcode = 20:无法获得本地颁发者证书

时间:2014-12-22 09:37:37

标签: ssl openssl ssl-certificate x509 ca

我在服务器上有一个证书链:

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

我的本​​地根CA证书是:

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

我正在使用此代码段来验证证书:

//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);
}

但是此代码返回以下输出:

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

这里有什么问题?

2 个答案:

答案 0 :(得分:9)

您的根CA可能使用与链中的第一个中间CA(在主机证书下方)相同的公钥,并且您可能没有可用于信任最后一个链证书的root-CA。这样的设置并不常见,但确实发生了。不幸的是,OpenSSL在此设置方面存在问题,并且只会尝试验证最长的链,即使较短的链已经提供了必要的信任。

这个OpenSSL问题有a bug entry,但OpenSSL开发人员从未处理过这个问题。如果您正在寻找X509_V_FLAG_TRUSTED_FIRST,也可以找到补丁。看起来OpenSSL 1.0.2(尚未发布)也会有这个选项。

据我所知,只有OpenSSL存在这类问题,即NSS(Firefox,桌面版Chrome)和SChannel(微软)都没有。

答案 1 :(得分:2)

我认为斯蒂芬可能帮助你解决了这个问题。但是这里有一个小小的挑剔可能会让你遇到的问题变得更糟,并改善了你的安全状况。

  

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

您不需要CA捆绑包。您只需要Verisign的3级公共主要证书颁发机构(G5)。您可以在Use of Root Certificates获得Verisign所需的一个CA证书。

它改善了您的安全状况,因为您允许任何CA证明服务器的证书(甚至是错误的证书),而不是使用已知的证明服务器证书的证书(Verisign)。


  

我正在使用此代码段来验证证书......

如果您想查看简单TLS客户端的示例,请查看OpenSSL wiki上的SSL/TLS Client。它提供了从random.org获取随机数的示例。将其更改为example.com不会花费太多工作。

注意 :OpenSSL在验证期间执行执行主机名匹配。如果您使用的是OpenSSL 1.0.2,1.0.1,1.0.0及更低版本,则仍需要自己动手。 OpenSSL在1.1.0中提供主机名匹配,但尚未提供。

SSL/TLS Client中提供了从X.509证书中的公用名(CN)和主题备用名称(SAN)中提取主机名的示例代码,但您必须提供实际的匹配代码


根据评论中的信息,您需要证书:“Symantec Class 3 Secure Server CA - G5”。下面是提供正确锚点时的样子 - 它以Verify return code: 0 (ok)结束(而不是错误20)。

“Symantec Class 3安全服务器CA-G5”是指纹4e b6 d5 78 49 9b 1c cf 5f 58 1e ad 56 be 3d 9b 67 44 a5 e5。您可以从Verisign的Use of Root Certificates获取它。

CAfile(下面)使用的s_client选项设置在s_client.cSSL_CTX_load_verify_locations。它设置为证明服务器证书所需的CA,而不是CA Zoo(即cacerts.pem)。


您可以使用$ openssl s_client -connect www.smartbabymonitor.ugrow.example.com:443 | openssl x509 -text -noout检查证书中的主题备用名称(SAN)。您将没问题,因为SAN中列出了主机www.smartbabymonitor.ugrow.example.com。您甚至可以在命令中添加-servername选项以使用服务器名称指示(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)
---