iOS 共钥验签 证书验签

坑爹的共钥验签浪费了我一周时间!!!!!!!!!!

由于公司为了某些代码的安全性决定采用证书的方式解决。但是验证证书的问题还必须本地去做,尽量减少或者说避免与服务器的任何交互,所以研究本地证书验签的重任还是落到了我的头上。

首先我曾经试过使用AF的方式,但是这种工作一般都是与服务器交互服务器去做的验签工作。后来发现Secrty.framework的官方框架也可以做,但是发现此框架功能太少了,根本难以满足我的验签需求。最后只能求助于OpenSSL这个框架,不过值得注意的是这个框架很大。真的很大。。。因为我是提供SDK的方式,原本一百多K的SDK静态库直接增加到了十八M左右。

下面还是讲讲我是怎么被他坑的吧。

其实验签很简单
1.证书验证书
在你的主程序中加入下面代码

int i, ret = 1;

X509_STORE *cert_ctx = NULL;
X509_LOOKUP *lookup = NULL;

NSString *Userfile = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"User.pem"];
NSString *CAfile = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Root.pem"];

if (bio_err == NULL)
    if ((bio_err = BIO_new(BIO_s_file())) != NULL)
        BIO_set_fp(bio_err, stderr, BIO_NOCLOSE | BIO_FP_TEXT);

        cert_ctx = X509_STORE_new();
        if (cert_ctx == NULL)
            goto end;
        X509_STORE_set_verify_cb(cert_ctx, cb);
        ERR_load_crypto_strings();
        OpenSSL_add_all_algorithms();

        lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_file());
        if (lookup == NULL)
            abort();
        if (CAfile) {
            i = X509_LOOKUP_load_file(lookup, [CAfile UTF8String], X509_FILETYPE_PEM);
            if (!i) {
                BIO_printf(bio_err, "Error loading file %s\n", CAfile);
                ERR_print_errors(bio_err);
                goto end;
            }
        }

        lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_hash_dir());
        if (lookup == NULL)
            abort();

        ERR_clear_error();

        ret = 0;

        if (1 != check(cert_ctx, [Userfile UTF8String], NULL, NULL, NULL, NULL))
            ret = -1;

    end:
        if (cert_ctx != NULL)
            X509_STORE_free(cert_ctx);

好了骚年,你可以去验证一下了

static int check(X509_STORE *ctx, char *file,
                 STACK_OF(X509) *uchain, STACK_OF(X509) *tchain,
                 STACK_OF(X509_CRL) *crls, ENGINE *e)
{
    X509 *x = NULL;

//    loadCert(file, x);


    int i = 0, ret = 0;
    X509_STORE_CTX *csc;
    //FIXME:这里根据你加在的证书格式动态调整FORMAT_PEM
    x = load_cert(bio_err, file, FORMAT_PEM, NULL, e, "certificate file");
    if (x == NULL)
        goto end;
    fprintf(stdout, "%s: ", (file == NULL) ? "stdin" : file);

    csc = X509_STORE_CTX_new();
    if (csc == NULL) {
        ERR_print_errors(bio_err);
        goto end;
    }
    X509_STORE_set_flags(ctx, vflags);
    if (!X509_STORE_CTX_init(csc, ctx, x, NULL)) {
        ERR_print_errors(bio_err);
        goto end;
    }
    if (tchain)
        X509_STORE_CTX_trusted_stack(csc, tchain);
    if (crls)
        X509_STORE_CTX_set0_crls(csc, crls);
    i = X509_verify_cert(csc);
    X509_STORE_CTX_free(csc);

    ret = 0;
end:
    if (i > 0) {
        fprintf(stdout, "OK\n");
        ret = 1;
    }
    else
        ERR_print_errors(bio_err);
    if (x != NULL)
        X509_free(x);

    return (ret);
}

当然中间你需要把证书加在进来

//定义你的证书是cer格式还是pem格式
//pem格式
#define FORMAT_PEM 0
//cer格式
#define FORMAT_ASN1 1

X509 *load_cert(BIO *err, const char *file, int format,
                const char *pass, ENGINE *e, const char *cert_descrip)
{
    X509 *x = NULL;
    BIO *cert;

    if ((cert = BIO_new(BIO_s_file())) == NULL) {
        ERR_print_errors(err);
        goto end;
    }

    if (file == NULL) {
#ifdef _IONBF
# ifndef OPENSSL_NO_SETVBUF_IONBF
        setvbuf(stdin, NULL, _IONBF, 0);
# endif                         /* ndef OPENSSL_NO_SETVBUF_IONBF */
#endif
        BIO_set_fp(cert, stdin, BIO_NOCLOSE);
    }
    else {
        if (BIO_read_filename(cert, file) <= 0) {
            BIO_printf(err, "Error opening %s %s\n", cert_descrip, file);
            ERR_print_errors(err);
            goto end;
        }
    }

    if (format == FORMAT_ASN1)
        x = d2i_X509_bio(cert, NULL);
    else if (format == FORMAT_PEM)
        x = PEM_read_bio_X509_AUX(cert, NULL,
                                  (pem_password_cb *)password_callback, NULL);
    else {
        BIO_printf(err, "bad input format specified for %s\n", cert_descrip);
        goto end;
    }
end:
    if (x == NULL) {
        BIO_printf(err, "unable to load certificate\n");
        ERR_print_errors(err);
    }
    if (cert != NULL)
        BIO_free(cert);
    return (x);
}

当然错误日志你也是可以看到的

#include <stdio.h>
#include <string.h>

#include <rsa.h>
#include <ui.h>
#include <ui_compat.h>
#include <err.h>
#include <pem.h>

#include <x509v3.h>

# ifdef OPENSSL_SYS_WIN16
#  define MS_CALLBACK     _far _loadds
# else
#  define MS_CALLBACK
# endif
static int v_verbose = 0, vflags = 0;
BIO *bio_err = NULL;
#define FORMAT_PEM 0
#define FORMAT_ASN1 1

# define PW_MIN_LENGTH 4
typedef struct pw_cb_data {
    const void *password;
    const char *prompt_info;
} PW_CB_DATA;

static UI_METHOD *ui_method = NULL;
typedef int pem_password_cb(char *buf, int size, int rwflag, void *userdata);


static void nodes_print(BIO *out, const char *name,
                        STACK_OF(X509_POLICY_NODE) *nodes)
{
    X509_POLICY_NODE *node;
    int i;
    BIO_printf(out, "%s Policies:", name);
    if (nodes) {
        BIO_puts(out, "\n");
        for (i = 0; i < sk_X509_POLICY_NODE_num(nodes); i++) {
            node = sk_X509_POLICY_NODE_value(nodes, i);
            X509_POLICY_NODE_print(out, node, 2);
        }
    }
    else
        BIO_puts(out, " <empty>\n");
}

void policies_print(BIO *out, X509_STORE_CTX *ctx)
{
    X509_POLICY_TREE *tree;
    int explicit_policy;
    int free_out = 0;
    if (out == NULL) {
        out = BIO_new_fp(stderr, BIO_NOCLOSE);
        free_out = 1;
    }
    tree = X509_STORE_CTX_get0_policy_tree(ctx);
    explicit_policy = X509_STORE_CTX_get_explicit_policy(ctx);

    BIO_printf(out, "Require explicit Policy: %s\n",
               explicit_policy ? "True" : "False");

    nodes_print(out, "Authority", X509_policy_tree_get0_policies(tree));
    nodes_print(out, "User", X509_policy_tree_get0_user_policies(tree));
    if (free_out)
        BIO_free(out);
}

int password_callback(char *buf, int bufsiz, int verify, PW_CB_DATA *cb_tmp)
{
    UI *ui = NULL;
    int res = 0;
    const char *prompt_info = NULL;
    const char *password = NULL;
    PW_CB_DATA *cb_data = (PW_CB_DATA *)cb_tmp;

    if (cb_data) {
        if (cb_data->password)
            password = cb_data->password;
        if (cb_data->prompt_info)
            prompt_info = cb_data->prompt_info;
    }

    if (password) {
        res = strlen(password);
        if (res > bufsiz)
            res = bufsiz;
        memcpy(buf, password, res);
        return res;
    }

    ui = UI_new_method(ui_method);
    if (ui) {
        int ok = 0;
        char *buff = NULL;
        int ui_flags = 0;
        char *prompt = NULL;

        prompt = UI_construct_prompt(ui, "pass phrase", prompt_info);
        if (!prompt) {
            BIO_printf(bio_err, "Out of memory\n");
            UI_free(ui);
            return 0;
        }

        ui_flags |= UI_INPUT_FLAG_DEFAULT_PWD;
        UI_ctrl(ui, UI_CTRL_PRINT_ERRORS, 1, 0, 0);

        if (ok >= 0)
            ok = UI_add_input_string(ui, prompt, ui_flags, buf,
                                     PW_MIN_LENGTH, bufsiz - 1);
        if (ok >= 0 && verify) {
            buff = (char *)OPENSSL_malloc(bufsiz);
            if (!buff) {
                BIO_printf(bio_err, "Out of memory\n");
                UI_free(ui);
                OPENSSL_free(prompt);
                return 0;
            }
            ok = UI_add_verify_string(ui, prompt, ui_flags, buff,
                                      PW_MIN_LENGTH, bufsiz - 1, buf);
        }
        if (ok >= 0)
            do {
                ok = UI_process(ui);
            } while (ok < 0 && UI_ctrl(ui, UI_CTRL_IS_REDOABLE, 0, 0, 0));

        if (buff) {
            OPENSSL_cleanse(buff, (unsigned int)bufsiz);
            OPENSSL_free(buff);
        }

        if (ok >= 0)
            res = strlen(buf);
        if (ok == -1) {
            BIO_printf(bio_err, "User interface error\n");
            ERR_print_errors(bio_err);
            OPENSSL_cleanse(buf, (unsigned int)bufsiz);
            res = 0;
        }
        if (ok == -2) {
            BIO_printf(bio_err, "aborted!\n");
            OPENSSL_cleanse(buf, (unsigned int)bufsiz);
            res = 0;
        }
        UI_free(ui);
        OPENSSL_free(prompt);
    }
    return res;
}

static int MS_CALLBACK cb(int ok, X509_STORE_CTX *ctx)
{
    int cert_error = X509_STORE_CTX_get_error(ctx);
    X509 *current_cert = X509_STORE_CTX_get_current_cert(ctx);

    if (!ok) {
        if (current_cert) {
            X509_NAME_print_ex_fp(stdout,
                                  X509_get_subject_name(current_cert),
                                  0, XN_FLAG_ONELINE);
            printf("\n");
        }
        printf("%serror %d at %d depth lookup:%s\n",
               X509_STORE_CTX_get0_parent_ctx(ctx) ? "[CRL path]" : "",
               cert_error,
               X509_STORE_CTX_get_error_depth(ctx),
               X509_verify_cert_error_string(cert_error));
        switch (cert_error) {
            case X509_V_ERR_NO_EXPLICIT_POLICY:
                policies_print(NULL, ctx);
            case X509_V_ERR_CERT_HAS_EXPIRED:

                /*
                 * since we are just checking the certificates, it is ok if they
                 * are self signed. But we should still warn the user.
                 */

            case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
                /* Continue after extension errors too */
            case X509_V_ERR_INVALID_CA:
            case X509_V_ERR_INVALID_NON_CA:
            case X509_V_ERR_PATH_LENGTH_EXCEEDED:
            case X509_V_ERR_INVALID_PURPOSE:
            case X509_V_ERR_CRL_HAS_EXPIRED:
            case X509_V_ERR_CRL_NOT_YET_VALID:
            case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
                ok = 1;
        }
        return ok;
    }
    if (cert_error == X509_V_OK && ok == 2)
        policies_print(NULL, ctx);
    if (!v_verbose)
        ERR_clear_error();
    return (ok);
}

有什么疑问可以联系私信我。

猜你喜欢

转载自blog.csdn.net/longlongvalue/article/details/77720762