简体   繁体   English

使用 OpenSSL API 以编程方式验证证书链

[英]Programmatically verify certificate chain using OpenSSL API

This is very similar to other questions but the ones I've looked at either don't have an answer or don't quite ask the same question.这与其他问题非常相似,但我看过的问题要么没有答案,要么没有问出相同的问题。 I have a self-signed CA certificate, and two other certificates that are signed with that CA certificate.我有一个自签名 CA 证书,以及另外两个使用该 CA 证书签名的证书。 I'm fairly sure the certificates are correct, because 'openssl verify' works:我相当确定证书是正确的,因为“openssl verify”有效:

$ openssl verify -CAfile ca.pem server.pem
server.pem: OK

(The above is from memory, I don't have them in front of me, so it may be slightly off). (以上是凭记忆,我面前没有,所以可能略有偏差)。

Now I want to verify the certificates programatically.现在我想以编程方式验证证书。 I have a utility function with pseudocode below:我有一个带有伪代码的实用程序函数:

int verify_cert(X509 *cert, X509 *cacert)
{
     int ret;
     X509_STORE *store;
     X509_STORE_CTX *ctx;

     store = X509_STORE_new();
     X590_STORE_add_cert(store, cacert);

     ctx = X509_STORE_CTX_new();
     X509_STORE_CTX_init(ctx, store, cert, NULL);

     ret = X590_verify_cert(ctx);

     /* check for errors and clean up */
}

My problem is that the above code always returns 'failed to find issuer certificate'.我的问题是上面的代码总是返回“无法找到颁发者证书”。 What have I done wrong?我做错了什么? I believe I am creating a new store, adding the cacert, creating a new context, and adding the child cert to be verified to the context with a pointer to the store which contains the CA.我相信我正在创建一个新商店,添加 cacert,创建一个新上下文,并使用指向包含 CA 的商店的指针将要验证的子证书添加到上下文中。 I'm pretty obviously doing something wrong, but I'm unsure what.我很明显做错了什么,但我不确定是什么。

Any ideas?有任何想法吗?

Update: I'm aware I can save these certs to disk and use something like X509_LOOKUP_file or something similar.更新:我知道我可以将这些证书保存到磁盘并使用类似 X509_LOOKUP_file 或类似的东西。 I'm looking for a solution that doesn't touch the disk unnecessarily.我正在寻找一种不会不必要地接触磁盘的解决方案。

You can use the normal validation routines (see How do you verify a public key was issued by your private CA? ), like the -verify function in OpenSSL does.您可以使用普通的验证例程(请参阅如何验证您的私有 CA 发布的公钥? ),就像 OpenSSL 中的 -verify 函数一样。 You need to create a lookup method (X509_LOOKUP_METHOD) like X509_LOOKUP_file(), but which works with a character string instead of a filename.您需要创建一个类似于 X509_LOOKUP_file() 的查找方法 (X509_LOOKUP_METHOD),但它使用字符串而不是文件名。 The code for X509_LOOKUP_buffer() is as follows. X509_LOOKUP_buffer() 的代码如下。

Header file by_buffer.h:头文件by_buffer.h:

/* File:   by_buffer.h */

#ifndef BY_BUFFER_H
#define    BY_BUFFER_H

#include <openssl/x509.h>

#ifdef    __cplusplus
extern "C" {
#endif
#define X509_L_BUF_LOAD    1
#define X509_LOOKUP_load_buf(x,name,type) \
        X509_LOOKUP_ctrl((x),X509_L_BUF_LOAD,(name),(long)(type),NULL)
X509_LOOKUP_METHOD *X509_LOOKUP_buffer(void);

#ifdef    __cplusplus
}
#endif

#endif    /* BY_BUFFER_H */

The c program by_buffer.c: c程序by_buffer.c:

/* by_buffer.c - copied and modified from crypto/x509/by_file.c */
/* Copyright (C) - should be the same as for OpenSSL
*/
#include "by_buffer.h"

#include <stdio.h>
#include <time.h>
#include <errno.h>

#include "../crypto/cryptlib.h"
#include <openssl/lhash.h>
#include <openssl/buffer.h>
#include <openssl/pem.h>
#include <openssl/err.h>

static int by_buffer_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc,
    long argl, char **ret);
X509_LOOKUP_METHOD x509_buffer_lookup=
    {
    "Load buffer into cache",
    NULL,        /* new */
    NULL,        /* free */
    NULL,         /* init */
    NULL,        /* shutdown */
    by_buffer_ctrl,    /* ctrl */
    NULL,        /* get_by_subject */
    NULL,        /* get_by_issuer_serial */
    NULL,        /* get_by_fingerprint */
    NULL,        /* get_by_alias */
    };

X509_LOOKUP_METHOD *X509_LOOKUP_buffer(void)
    {
    return(&x509_buffer_lookup);
    }

static int by_buffer_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
         char **ret)
    {
    int ok=0;
    char *certBuf;

    switch (cmd)
        {
    case X509_L_BUF_LOAD:
        if (argl == X509_FILETYPE_DEFAULT)
            {
            X509err(X509_F_BY_FILE_CTRL,X509_R_LOADING_DEFAULTS);
            }
        else
            {
            if(argl == X509_FILETYPE_PEM)
                ok = (X509_load_cert_crl_buf(ctx,argp,
                    X509_FILETYPE_PEM) != 0);
            else
                ok = (X509_load_cert_buf(ctx,argp,(int)argl) != 0);
            }
        break;
        }
    return(ok);
    }

int X509_load_cert_buf(X509_LOOKUP *ctx, const char *certBuf, int type)
    {
    int ret=0;
    BIO *in=NULL;
    int i,count=0;
    X509 *x=NULL;

    if (certBuf == NULL) return(1);
        in=BIO_new(BIO_s_mem());
        if(in==NULL) goto err;

    if (type == X509_FILETYPE_PEM)
        {
        for (;;)
            {
            x=PEM_read_bio_X509_AUX(in,NULL,NULL,NULL);
            if (x == NULL)
                {
                if ((ERR_GET_REASON(ERR_peek_last_error()) ==
                    PEM_R_NO_START_LINE) && (count > 0))
                    {
                    ERR_clear_error();
                    break;
                    }
                else
                    {
                    X509err(X509_F_X509_LOAD_CERT_FILE,
                        ERR_R_PEM_LIB);
                    goto err;
                    }
                }
            i=X509_STORE_add_cert(ctx->store_ctx,x);
            if (!i) goto err;
            count++;
            X509_free(x);
            x=NULL;
            }
        ret=count;
        }
    else if (type == X509_FILETYPE_ASN1)
        {
        x=d2i_X509_bio(in,NULL);
        if (x == NULL)
            {
            X509err(X509_F_X509_LOAD_CERT_FILE,ERR_R_ASN1_LIB);
            goto err;
            }
        i=X509_STORE_add_cert(ctx->store_ctx,x);
        if (!i) goto err;
        ret=i;
        }
    else
        {
        X509err(X509_F_X509_LOAD_CERT_FILE,X509_R_BAD_X509_FILETYPE);
        goto err;
        }
err:
    if (x != NULL) X509_free(x);
    if (in != NULL) BIO_free(in);
    return(ret);
    }

int X509_load_crl_buf(X509_LOOKUP *ctx, const char *certBuf, int type)
    {
    int ret=0;
    BIO *in=NULL;
    int i,count=0;
    X509_CRL *x=NULL;

    if (certBuf == NULL) return(1);
    //in=BIO_new(BIO_s_file_internal());
        in=BIO_new(BIO_s_mem());

        if(in==NULL) goto err;

    if (type == X509_FILETYPE_PEM)
        {
        for (;;)
            {
            x=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL);
            if (x == NULL)
                {
                if ((ERR_GET_REASON(ERR_peek_last_error()) ==
                    PEM_R_NO_START_LINE) && (count > 0))
                    {
                    ERR_clear_error();
                    break;
                    }
                else
                    {
                    X509err(X509_F_X509_LOAD_CRL_FILE,
                        ERR_R_PEM_LIB);
                    goto err;
                    }
                }
            i=X509_STORE_add_crl(ctx->store_ctx,x);
            if (!i) goto err;
            count++;
            X509_CRL_free(x);
            x=NULL;
            }
        ret=count;
        }
    else if (type == X509_FILETYPE_ASN1)
        {
        x=d2i_X509_CRL_bio(in,NULL);
        if (x == NULL)
            {
            X509err(X509_F_X509_LOAD_CRL_FILE,ERR_R_ASN1_LIB);
            goto err;
            }
        i=X509_STORE_add_crl(ctx->store_ctx,x);
        if (!i) goto err;
        ret=i;
        }
    else
        {
        X509err(X509_F_X509_LOAD_CRL_FILE,X509_R_BAD_X509_FILETYPE);
        goto err;
        }
err:
    if (x != NULL) X509_CRL_free(x);
    if (in != NULL) BIO_free(in);
    return(ret);
    }

int X509_load_cert_crl_buf(X509_LOOKUP *ctx, const char *certBuf, int type)
{
    STACK_OF(X509_INFO) *inf;
    X509_INFO *itmp;
    BIO *in;
    int i, count = 0;
    if(type != X509_FILETYPE_PEM)
        return X509_load_cert_buf(ctx, certBuf, type);
        in = BIO_new(BIO_s_mem());
    if(!in) {
        X509err(X509_F_X509_LOAD_CERT_CRL_FILE,ERR_R_SYS_LIB);
        return 0;
    }
        BIO_write(in, certBuf, strlen(certBuf));
    inf = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL);
    BIO_free(in);
    if(!inf) {
        X509err(X509_F_X509_LOAD_CERT_CRL_FILE,ERR_R_PEM_LIB);
        return 0;
    }
    for(i = 0; i < sk_X509_INFO_num(inf); i++) {
        itmp = sk_X509_INFO_value(inf, i);
        if(itmp->x509) {
            X509_STORE_add_cert(ctx->store_ctx, itmp->x509);
            count++;
        }
        if(itmp->crl) {
            X509_STORE_add_crl(ctx->store_ctx, itmp->crl);
            count++;
        }
    }
    sk_X509_INFO_pop_free(inf, X509_INFO_free);
    return count;
}

Routine in C++ which calls the above routines:调用上述例程的 C++ 例程:

#include "by_buffer.h"
static int check(X509_STORE *ctx, const char *certBuf);
static X509 *load_cert(const char *certBuf);

int validateKey(const char *rsaKeyCA, const char *rsaCertificate) {
    int ret=0;
    X509_STORE *cert_ctx=NULL;
    X509_LOOKUP *lookup=NULL;

    cert_ctx=X509_STORE_new();
    if (cert_ctx == NULL) goto end;

    OpenSSL_add_all_algorithms();

    lookup=X509_STORE_add_lookup(cert_ctx,X509_LOOKUP_buffer());
    if (lookup == NULL)
        goto end;

    if(!X509_LOOKUP_load_buf(lookup,rsaKeyCA,X509_FILETYPE_PEM))
        goto end;

    lookup=X509_STORE_add_lookup(cert_ctx,X509_LOOKUP_hash_dir());
    if (lookup == NULL)
        goto end;

    X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);

    ret = check(cert_ctx, rsaCertificate);
end:
    if (cert_ctx != NULL) X509_STORE_free(cert_ctx);

    return ret;
}

static X509 *load_cert(const char *certBuf)
{
    X509 *x=NULL;
    BIO *cert;

    if ((cert=BIO_new(BIO_s_mem())) == NULL)
        goto end;

    BIO_write(cert, certBuf, strlen(certBuf));

    x=PEM_read_bio_X509_AUX(cert,NULL, NULL, NULL);
end:
    if (cert != NULL) BIO_free(cert);
    return(x);
}

static int check(X509_STORE *ctx, const char *certBuf)
{
    X509 *x=NULL;
    int i=0,ret=0;
    X509_STORE_CTX *csc;

    x = load_cert(certBuf);
    if (x == NULL)
        goto end;

    csc = X509_STORE_CTX_new();
    if (csc == NULL)
        goto end;
    X509_STORE_set_flags(ctx, 0);
    if(!X509_STORE_CTX_init(csc,ctx,x,0))
        goto end;
    ////// See crypto/asn1/t_x509.c for ideas on how to access and print the values
    //printf("X.509 name: %s\n", x->name);
    i=X509_verify_cert(csc);
    X509_STORE_CTX_free(csc);

    ret=0;
end:
    ret = (i > 0);
    if (x != NULL)
        X509_free(x);

    return(ret);
}

I think, you can use "X509_STORE_set_verify_cb" to add a callback to identify the actual error:我认为,您可以使用“X509_STORE_set_verify_cb”添加回调来识别实际错误:

static int  verify_cb(int ok, X509_STORE_CTX *ctx)
{
    if (!ok)
    {
        /* check the error code and current cert*/
        X509 *currentCert = X509_STORE_CTX_get_current_cert(ctx);
        int certError = X509_STORE_CTX_get_error(ctx);
        int depth = X509_STORE_CTX_get_error_depth(ctx);
        printCert(currentCert);
        printf("Error depth %d, certError %d", depth, certError)
    }

    return(ok);
}

int verify_cert(X509 *cert, X509 *cacert)
{
     int ret;
     X509_STORE *store;
     X509_STORE_CTX *ctx;

     store = X509_STORE_new();
     X509_STORE_set_verify_cb(store, verify_cb);
     X590_STORE_add_cert(store, cacert);

     ctx = X509_STORE_CTX_new();
     X509_STORE_CTX_init(ctx, store, cert, NULL);

     ret = X590_verify_cert(ctx);

     /* check for errors and clean up */
}

Unless we know the error code it is difficult to guess the actual problem.除非我们知道错误代码,否则很难猜测实际问题。 The code otherwise looks OK.否则代码看起来没问题。

I encountered this problem myself and started off with code very close to the OP.我自己遇到了这个问题,并从非常接近 OP 的代码开始。 My certificate chain included 3 certificates: Certificate 1 (root-ca) Issuer: root-ca Subject: root-ca Certificate 2 (signing-ca) Issuer: root-ca Subject: signing-ca Certificate 3 (device) Issuer: signing-ca Subject: device我的证书链包括 3 个证书:证书 1 (root-ca) 颁发者:root-ca 主题:root-ca 证书 2 (signing-ca) 颁发者:root-ca 主题:signing-ca 证书 3(设备)颁发者:signing- ca 主题:设备

I wanted to verify the device certificate.我想验证设备证书。 My ca.pem equivalent (wrt OP) contained the root-ca and signing-ca.我的 ca.pem 等价物(wrt OP)包含根 ca 和签名 ca。

The X509_verify_cert function needs the entire certificate chain all the way to the root (root-ca & signing-ca) in the X509_store. X509_verify_cert 函数需要整个证书链一直到 X509_store 中的根(root-ca 和签名-ca)。

Below is my code that works for me.以下是适用于我的代码。 Checks on return values were omitted to lean the code down.省略了对返回值的检查以简化代码。

int getIssuerCert(X509_STORE *x509_store){
    STACK_OF(X509_INFO) *inf;
    X509_INFO *itmp;
    BIO *in;
    int i, count = 0;

    in = BIO_new(BIO_s_mem());
    BIO_write(in, issuerCertStr, strlen(issuerCertStr)); //string containing root-ca & signing-ca
    inf = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL);
    if(in != NULL) BIO_free(in);
    for(i = 0; i < sk_X509_INFO_num(inf); i++) {
        itmp = sk_X509_INFO_value(inf, i);
        if(itmp->x509) {
            X509_STORE_add_cert(x509_store, itmp->x509);
            count++;
        }
        if(itmp->crl) {
            X509_STORE_add_crl(x509_store, itmp->crl);
            count++;
        }
    }
    sk_X509_INFO_pop_free(inf, X509_INFO_free);
    return 0;
}


int verify_cert(){
    int ret = 0;
    X509 *devCert = NULL;
    X509_STORE *x509_store = NULL;
    X509_STORE_CTX *x509_store_ctx = NULL;

    OpenSSL_add_all_algorithms();
    devCert = getDeviceCert(); //  Returns X509 pointer

    x509_store = X509_STORE_new();
    X509_STORE_set_verify_cb(x509_store, verify_cb);
    X509_STORE_set_flags(x509_store, 0);

    x509_store_ctx = X509_STORE_CTX_new();

    X509_STORE_CTX_init(x509_store_ctx, x509_store, devCert, NULL)

    X509_STORE_CTX_set_purpose(x509_store_ctx, X509_PURPOSE_ANY);
    ret = X509_verify_cert(x509_store_ctx);

    if(x509_store_ctx != NULL) X509_STORE_CTX_free(x509_store_ctx);
    if(x509_store != NULL) X509_STORE_free(x509_store);
    if(devCert != NULL) X509_free(devCert);
    EVP_cleanup();
    return ret;
}

I didn't need to create any lookup methods.我不需要创建任何查找方法。 The key for me was looping through my certificates from my string in memory so I had all the certificates I need to complete the chain.对我来说,关键是从内存中的字符串遍历我的证书,因此我拥有完成链所需的所有证书。 The string is equivalent to what I would have fed into openssl verify for the option -CAfile.该字符串相当于我为选项 -CAfile 输入 openssl verify 的内容。

Also, make sure your X509 pointers are not null when the are used.此外,请确保您的 X509 指针在使用时不为空。

A possible answer (don't have the rep points to add a comment, sorry): the manpage for SSL_CTX_load_verify_locations(3) says,一个可能的答案(没有代表点添加评论,抱歉): SSL_CTX_load_verify_locations(3)的联机帮助页说,

When building its own certificate chain, an OpenSSL client/server will try to fill in
missing certificates from CAfile/CApath, if the certificate chain was not explicitly
specified (see SSL_CTX_add_extra_chain_cert(3), SSL_CTX_use_certificate(3).

(Failure to match parens theirs, not mine.) (未能匹配他们的父母,而不是我的。)

Which seems to mean that, as an alternative to SSL_CTX_load_verify_locations(3) , it should be possible to use SSL_CTX_add_extra_chain_cert(3) or SSL_CTX_use_certificate(3) -- both of which take a X509 * arg.这似乎意味着,作为SSL_CTX_load_verify_locations(3)的替代方案,应该可以使用SSL_CTX_add_extra_chain_cert(3)SSL_CTX_use_certificate(3) —— 两者都采用X509 * arg。 Thus obviating the need for Mr Ed's solution as seen above.从而消除了对 Ed 先生解决方案的需要,如上所示。

Please take a look at SSL_CTX_load_verify_locations () function: http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html请看一下SSL_CTX_load_verify_locations ()函数: http : //www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html

SSL_CTX_load_verify_locations() specifies the locations for ctx, at which CA certificates for verification purposes are located. SSL_CTX_load_verify_locations() 指定 ctx 的位置,用于验证目的的 CA 证书所在的位置。 The certificates available via CAfile and CApath are trusted.通过 CAfile 和 CApath 提供的证书是可信的。

You can generate a CA certificate file containing both ca.pem server.pem:您可以生成包含 ca.pem server.pem 的 CA 证书文件:

 #!/bin/sh
 rm CAfile.pem
 for i in ca.pem server.pem ; do
   openssl x509 -in $i -text >> CAfile.pem
 done

And then set CAfile variable to point to CAfile.pem file.然后将CAfile变量设置为指向CAfile.pem文件。

Hope it helps !希望能帮助到你 !

See the official source code: apps/verify.c看官方源码:apps/verify.c

static int check(X509_STORE *ctx, const char *file,
                 STACK_OF(X509) *uchain, STACK_OF(X509) *tchain,
                 STACK_OF(X509_CRL) *crls, int show_chain);

You can see how to output 'OK' here:您可以在此处查看如何输出“OK”:

    if (i > 0 && X509_STORE_CTX_get_error(csc) == X509_V_OK) {
        printf("%s: OK\n", (file == NULL) ? "stdin" : file);

The function dependence can be found in apps/apps.c函数依赖可以在apps/apps.c中找到

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM