OpenSSL API를 사용하여 인증서 체인을 프로그래밍 방식으로 확인
이것은 다른 질문들과 매우 비슷하지만 제가 본 질문들은 답이 없거나 같은 질문을 하지 않습니다.자기 서명된 CA 인증서와 해당 CA 인증서로 서명된 다른 두 개의 인증서가 있습니다.opensl verify'가 작동하므로 인증서가 정확하다고 확신합니다.
$ openssl verify -CAfile ca.pem server.pem
server.pem: OK
(위 내용은 기억에서 나온 것으로, 앞에 없는 것이라 살짝 꺼졌을 수 있습니다.)
이제 나는 프로그램적으로 자격증을 확인하고 싶습니다.아래 의사코드를 가진 유틸리티 기능이 있습니다.
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 = X509_verify_cert(ctx);
/* check for errors and clean up */
}
문제는 위 코드가 항상 '발급자 인증서를 찾을 수 없음'을 반환한다는 것입니다.제가 무엇을 잘못했지요?CA가 포함된 스토어에 대한 포인터로 새로운 스토어를 생성하고, 캐서트를 추가하고, 새로운 컨텍스트를 생성하고, 컨텍스트에 검증할 자식 인증서를 추가하는 것으로 생각합니다.제가 뭔가 잘못하고 있는 게 분명한데, 뭐가 뭔지 모르겠어요.
무슨 생각 있어요?
업데이트: 이 인증서들을 디스크에 저장하고 X509와 같은 것을 사용할 수 있다는 것을 알고 있습니다.LOUNK_file 등이 있습니다.디스크에 불필요하게 닿지 않는 솔루션을 찾고 있습니다.
OpenSSL의 -verify 기능처럼 일반 유효성 검사 루틴(개인 CA에서 공개 키를 발급한 경우 어떻게 확인합니까? 참조)을 사용할 수 있습니다.X509와 같은 룩업 메소드(X509_LOOKUP_Method)를 생성해야 합니다.LOUNK_file()이지만 파일 이름 대신 문자열과 함께 작동합니다.X509 코드_LOUNK_buffer()는 다음과 같습니다.
헤더 파일 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 */
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;
}
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);
}
저는 이 문제를 직접 접했고 OP에 매우 가까운 코드로 시작했습니다.제 인증서 체인에는 3개의 인증서가 포함되어 있었습니다.인증서1(root-ca) 발급자 : root-ca 발급자 : root-ca 발급자 : root-ca 발급자 : signing-ca 인증서3(기기) 발급자 : signing-ca 발급자 : device
저는 기기 인증서를 확인하고 싶었습니다.내 ca.pem 등가물(wrt OP)에는 root-ca와 signing-ca가 포함되어 있습니다.
X509_verify_cert 함수는 X509_store의 루트(root-ca & signing-ca)까지 전체 인증서 체인이 필요합니다.
아래는 나에게 맞는 나의 코드입니다.반환 값에 대한 검사는 코드를 아래로 기울이기 위해 생략되었습니다.
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;
}
조회 방법을 만들 필요가 없었습니다.저에게 중요한 것은 메모리에 있는 문자열에서 인증서를 반복 검색하는 것이었기 때문에 체인을 완성하는 데 필요한 모든 인증서를 가지고 있었습니다.문자열은 옵션인 CA 파일에 대해 openssl verify에 입력했을 때와 동일합니다.
또한 를 사용할 때 X509 포인터가 null이 아닌지 확인합니다.
제 생각에는 "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 */
}
오류 코드를 알지 못하면 실제 문제를 추측하기 어렵습니다.그렇지 않으면 코드가 정상으로 보입니다.
가능한 답변 (댓글을 추가할 담당자 포인트가 없습니다, 죄송합니다): manpage forSSL_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).
(부모님을 나의 부모님이 아닌 그들의 부모님과 일치시키지 못하는 것.
그것은 그에 대한 대안으로서SSL_CTX_load_verify_locations(3)
, 사용할 수 있어야 합니다.SSL_CTX_add_extra_chain_cert(3)
아니면SSL_CTX_use_certificate(3)
-- 둘 다 필요합니다.X509 *
. 따라서에서 본 . arg. 따라서 위에서 본 에드 씨의 해결책의 필요성을 제거합니다.
한번 봐주세요.SSL_CTX_load_verify_locations ()
기능 : http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html
SSL_CTX_load_verify_locations()는 검증 목적의 CA 인증서가 위치하는 ctx의 위치를 지정합니다.CA 파일 및 CA 경로를 통해 사용 가능한 인증서는 신뢰할 수 있습니다.
ca.pem 서버를 모두 포함하는 CA 인증서 파일을 생성할 수 있습니다.펨:
#!/bin/sh
rm CAfile.pem
for i in ca.pem server.pem ; do
openssl x509 -in $i -text >> CAfile.pem
done
그다음에 세트CAfile
가리킬 변수CAfile.pem
일.
도움이 되길 바랍니다!
공식 소스 코드 참조: 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);
여기서 'OK'를 출력하는 방법을 확인할 수 있습니다.
if (i > 0 && X509_STORE_CTX_get_error(csc) == X509_V_OK) {
printf("%s: OK\n", (file == NULL) ? "stdin" : file);
기능 의존성은 apps/apps.c에서 확인할 수 있습니다.
언급URL : https://stackoverflow.com/questions/16291809/programmatically-verify-certificate-chain-using-openssl-api
'programing' 카테고리의 다른 글
슬래시를 닫기 전의 공간? (0) | 2023.10.28 |
---|---|
특정 속성을 가진 모든 요소를 선택하는 방법?TinyXPath로 (0) | 2023.10.28 |
IE10에서 스크롤 바 오버레이 콘텐츠를 방지하려면 어떻게 해야 합니까? (0) | 2023.10.28 |
동일한 이름의 형상화된 뷰 및 테이블 (0) | 2023.10.28 |
jquery 요소를 html 요소로 변환 (0) | 2023.10.23 |