锐英源软件
第一信赖

精通

英语

开源

擅长

开发

培训

胸怀四海 

第一信赖

当前位置:锐英源 / 英语翻译 / 用openssl和C解析X.509证书
服务方向
人工智能数据处理
人工智能培训
kaldi数据准备
小语种语音识别
语音识别标注
语音识别系统
语音识别转文字
kaldi开发技术服务
软件开发
运动控制卡上位机
机械加工软件
软件开发培训
Java 安卓移动开发
VC++
C#软件
汇编和破解
驱动开发
联系方式
固话:0371-63888850
手机:138-0381-0136
Q Q:396806883
微信:ryysoft

锐英源精品开源心得,转载请注明:“锐英源www.wisestudy.cn,孙老师作品,电话13803810136。”需要全文内容也请联系孙老师。


用openssl和C解析X.509证书


前言


证书是安全机制的基础,相当于开门钥匙数据,证书数据有不同格式,X.509是常用的一种格式。证书和通信过程中的安全密切相关,比如某个HTTPS网站如果没有证书,或者证书不合法,本地浏览器就不能访问这些HTTS网站。证书里的数据有可能会被加密和转化,保证不会被轻易识破。


Parsing X.509 Certificates with OpenSSL and C

While OpenSSL has become one of the defacto libraries for performing SSL and TLS operations, the library is surprisingly opaque and its documentation is, at times, abysmal. As part of our recent research, we have been performing Internet-wide scans of HTTPS hosts in order to better understand the HTTPS ecosystem (Analysis of the HTTPS Certificate EcosystemZMap: Fast Internet-Wide Scanning and its Security Applications). We use OpenSSL for many of these operations including parsing X.509 certificates. However, in order to parse and validate certificates, our team had to dig through parts of the OpenSSL code base and multiple sources of documention to find the correct functions to parse each piece of data. This post is intended to document many of these operations in a single location in order to hopefully alleviate this painful process for others.

If you have found other pieces of code particularly helpful, please don’t hesitate to send them along and we’ll update the post. I want to note that if you’re starting to develop against OpenSSL, O’Reilly’s Network Security with OpenSSL is an incredibly helpful resource; the book contains many snippets and pieces of documentation that I was not able to find anywhere online. I also want to thank James Kastenwho helped find and document several of these solutions.

使用OpenSSL和C解析X.509证书

虽然OpenSSL已成为执行SSL和TLS操作的事实库之一,但该库令人惊讶地不透明,其文档有时非常糟糕。作为我们最近研究的一部分,我们一直在对HTTPS主机执行Internet范围扫描,以便更好地了解HTTPS生态系统(HTTPS证书生态系统分析,ZMap:快速Internet扫描及其安全应用程序))。我们将OpenSSL用于许多这些操作,包括解析X.509证书。但是,为了解析和验证证书,我们的团队不得不深入挖掘OpenSSL代码库的部分内容和多个文档源,以找到解析每个数据的正确函数。本文旨在将许多这些操作记录在一个位置,以便有希望减轻其他人的痛苦过程。


Creating an OpenSSL X509 Object

All of the operations we discuss start with either a single X.509 certificate or a “stack” of certificates. OpenSSL represents a single certificate with an X509 struct and a list of certificates, such as the certificate chain presented during a TLS handshake as a STACK_OF(X509). Given that the parsing and validation stems from here, it only seems reasonable to start with how to create or access an X509 object. A few common scenarios are:

创建OpenSSL X509对象

我们讨论的所有操作都以单个X.509证书或证书“堆栈”开头。OpenSSL表达了具有X509结构和证书列表的单个证书 ,例如在TLS握手期间呈现的证书链STACK_OF(X509)。鉴于解析和验证源于此处,从如何创建或访问X509对象开始介绍似乎是合理的。一些常见的情况是:


1. You have initiated an SSL or TLS connection using OpenSSL.

In this case, you have access to an OpenSSL SSL struct from which you can extract the presented certificate as well as the entire certificate chain that the server presented to the client. In our specific case, we use libevent to perform TLS connections and can access the SSL struct from the libevent bufferevent: SSL *ssl = bufferevent_openssl_get_ssl(bev). This will clearly be different depending on how you complete your connection. However, once you have your SSL context, the server certificate and presented chain can be extracted as follows:

创建OpenSSL X509对象

我们讨论的所有操作都以单个X.509证书或“堆栈”证书开头。OpenSSL表示具有X509结构和证书列表的单个证书 ,例如在TLS握手期间呈现的证书链STACK_OF(X509)。鉴于解析和验证源于此处,从开始如何创建或访问X509对象开始似乎是合理的。一些常见的情况是:

 

#include 
#include 

X509 *cert = SSL_get_peer_certificate(ssl);
STACK_OF(X509) *sk = SSL_get_peer_cert_chain(ssl);

We have found that at times, OpenSSL will produce an empty certificate chain (SSL_get_peer_cert_chain will come back NULL) even though a client certificate has been presented (the server certificate is generally presented as the first certificate in the stack along with the remaining chain). It’s unclear to us why this happens, but it’s not a deal breaker, as it’s easy to create a new stack of certificates:

我们发现,有时,即使已经提供了客户端证书,OpenSSL也会生成一个空的证书链(SSL_get_peer_cert_chain将返回NULL)(服务器证书通常作为堆栈中的第一个证书与剩余的链一起提供)。我们不清楚为什么会发生这种情况,但这不是一个交易破坏者,因为很容易创建一堆新的证书:

X509 *cert = SSL_get_peer_certificate(ssl);
STACK_OF(X509) *sk = sk_X509_new_null();
sk_X509_push(sk, cert);

2. You have stored a certificate on disk as a PEM file.

For reference, a PEM file is the Base64-encoded version of an X.509 certificate, which should look similar to the following:

2.您已将证书存储在磁盘上作为PEM文件。

作为参考,PEM文件是X.509证书的Base64编码版本,它应类似于以下内容:


-----BEGIN CERTIFICATE-----
MIIHIDCCBgigAwIBAgIIMrM8cLO76sYwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl
iftrJvzAOMAPY5b/klZvqH6Ddubg/hUVPkiv4mr5MfWfglCQdFF1EBGNoZSFAU7y
ZkGENAvDmv+5xVCZELeiWA2PoNV4m/SW6NHrF7gz4MwQssqP9dGMbKPOF/D2nxic
TnD5WkGMCWpLgqDWWRoOrt6xf0BPWukQBDMHULlZgXzNtoGlEnwztLlnf0I/WWIS
eBSyDTeFJfopvoqXuws23X486fdKcCAV1n/Nl6y2z+uVvcyTRxY2/jegmV0n0kHf
gfcKzw==
-----END CERTIFICATE----- 

In this case, you can access the certificate as follows:在这种情况下,您可以按如下方式访问证书:

#include 
#include 
#include 

FILE *fp = fopen(path, "r");
if (!fp) {
	fprintf(stderr, "unable to open: %s\n", path);
	return EXIT_FAILURE;
}

X509 *cert = PEM_read_X509(fp, NULL, NULL, NULL);
if (!cert) {
	fprintf(stderr, "unable to parse certificate in: %s\n", path);
	fclose(fp);
	return EXIT_FAILURE;
}

// any additional processing would go here..

X509_free(cert);
fclose(fp);

3. You have access to the raw certificate in memory.

In the case that you have access to the raw encoding of the certificate in memory, you can parse it as follows. This is useful if you have stored raw certificates in a database or similar data store.

3.您可以访问内存中的原始证书。

如果您可以访问内存中证书的原始编码,则可以按如下方式解析它。如果已将原始证书存储在数据库或类似的数据存储中,则此选项非常有用。

#include 
#include 
#include 

const unsigned char *data = ... ;
size_t len = ... ;

X509 *cert = d2i_X509(NULL, &data, len);
if (!cert) {
	fprintf(stderr, "unable to parse certificate in memory\n");
	return EXIT_FAILURE;
}

// any additional processing would go here..

X509_free(cert);

4. You have access to the Base64 encoded PEM in memory.

4.您可以在内存中访问Base64编码的PEM。

char* pemCertString = ..... (includes "-----BEGIN/END CERTIFICATE-----")
size_t certLen = strlen(pemCertString);

BIO* certBio = BIO_new(BIO_s_mem());
BIO_write(certBio, pemCertString, certLen);
X509* certX509 = PEM_read_bio_X509(certBio, NULL, NULL, NULL);
if (!certX509) {
    fprintf(stderr, "unable to parse certificate in memory\n");
    return EXIT_FAILURE;
}

// do stuff

BIO_free(certBio);
X509_free(certX509);

Parsing Certificates

Now that we have access to a certificate in OpenSSL, we’ll focus on how to extract useful data from the certificate. We don’t include the #includes in every statement, but use the following headers throughout our codebase:

解析证书

现在我们可以访问OpenSSL中的证书,我们将专注于如何从证书中提取有用的数据。我们不在每个文件里重复#include块,但在整个代码库中使用以下块:

#include <openssl/x509v3.h>
#include <openssl/bn.h>
#include <openssl/asn1.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
OpenSSL_add_all_algorithms();

You will also need the development versions of the OpenSSL libraries and to compile with -lssl.您还需要OpenSSL库的开发版本并进行编译-lssl。

Subject and Issuer

The certificate subject and issuer can be easily extracted and represented as a single string as follows:

主题和发行人

可以轻松提取证书主题和颁发者,并将其表示为单个字符串,如下所示:

char *subj = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
char *issuer = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);

These can be freed by calling OPENSSL_free.

By default, the subject and issuer are returned in the following form:

这些可以通过调用释放OPENSSL_free。

默认情况下,主题和颁发者以下列形式返回:

/C=US/ST=California/L=Mountain View/O=Google Inc/CN=*.google.com  

If you want to convert these into a more traditional looking DN, such as:如果您想将这些转换为更传统的DN,例如:

C=US, ST=Texas, L=Austin, O=Polycom Inc., OU=Video Division, CN=a.digitalnetbr.net  

they can be converted with the following code:可以使用以下代码转换它们:

int i, curr_spot = 0;
char *s = tmpBuf + 1; /* skip the first slash */
char *c = s;
while (1) {
	if (((*s == '/') && ((s[1] >= 'A') && (s[1] <= 'Z') &&
			((s[2] == '=') || ((s[2] >= 'A') && (s[2] <= 'Z')
			&& (s[3] == '='))))) || (*s == '\0')) {
		i = s - c;
		strncpy(destination + curr_spot, c, i);
		curr_spot += i;
		assert(curr_spot < size);
		c = s + 1; /* skip following slash */
		if (*s != '\0') {
			strncpy(destination + curr_spot, ", ", 2);
			curr_spot += 2;
		}
	}
	if (*s == '\0')
		break;
	++s;
}

It is also possible to extract particular elements from the subject. For example, the following code will iterate over all the values in the subject:

还可以从受试者中提取特定元素。例如,以下代码将迭代主题中的所有值:

X509_NAME *subj = X509_get_subject_name(cert);

for (int i = 0; i < X509_NAME_entry_count(subj); i++) {
	X509_NAME_ENTRY *e = X509_NAME_get_entry(subj, i);
	ASN1_STRING *d = X509_NAME_ENTRY_get_data(e);
	char *str = ASN1_STRING_data(d);
}

or要么

for (;;) {
    int lastpos = X509_NAME_get_index_by_NID(subj, NID_commonName, lastpos);
    if (lastpos == -1)
        break;
    X509_NAME_ENTRY *e = X509_NAME_get_entry(subj, lastpos);
    /* Do something with e */
}

Cryptographic (e.g. SHA-1) Fingerprint

We can calculate the SHA-1 fingerprint (or any other fingerprint) with the following code:

加密(例如SHA-1)指纹

我们可以使用以下代码计算SHA-1指纹(或任何其他指纹):

#define SHA1LEN 20
char buf[SHA1LEN];

const EVP_MD *digest = EVP_sha1();
unsigned len;

int rc = X509_digest(cert, digest, (unsigned char*) buf, &len);
if (rc == 0 || len != SHA1LEN) {
	return EXIT_FAILURE;
}
return EXIT_SUCCESS;

This will produce the raw fingerprint. This can be converted to the human readable hex version as follows:

这将产生原始指纹。这可以转换为人类可读的十六进制版本,如下所示:

void hex_encode(unsigned char* readbuf, void *writebuf, size_t len)
{
	for(size_t i=0; i < len; i++) {
		char *l = (char*) (2*i + ((intptr_t) writebuf));
		sprintf(l, "%02x", readbuf[i]);
	}
}

char strbuf[2*SHA1LEN+1];
hex_encode(buf, strbuf, SHA1LEN);

Version

Parsing the certificate version is straight-foward; the only oddity is that it is zero-indexed:

版本

解析证书版本是直接的; 唯一奇怪的是它是零索引的:

int version = ((int) X509_get_version(cert)) + 1;

Serial Number

Serial numbers can be arbitrarily large as well as positive or negative. As such, we handle it as a string instead of a typical integer in our processing.

序列号

序列号可以是任意大的,也可以是正的或负的。因此,我们将其作为字符串处理,而不是处理中的典型整数。

#define SERIAL_NUM_LEN 1000;
char serial_number[SERIAL_NUM_LEN+1];

ASN1_INTEGER *serial = X509_get_serialNumber(cert);

BIGNUM *bn = ASN1_INTEGER_to_BN(serial, NULL);
if (!bn) {
	fprintf(stderr, "unable to convert ASN1INTEGER to BN\n");
	return EXIT_FAILURE;
}

char *tmp = BN_bn2dec(bn);
if (!tmp) {
	fprintf(stderr, "unable to convert BN to decimal string.\n");
	BN_free(bn);
	return EXIT_FAILURE;
}

if (strlen(tmp) >= len) {
	fprintf(stderr, "buffer length shorter than serial number\n");
	BN_free(bn);
	OPENSSL_free(tmp);
	return EXIT_FAILURE;
}

strncpy(buf, tmp, len);
BN_free(bn);
OPENSSL_free(tmp);

Signature Algorithm

The signature algorithm on a certificate is stored as an OpenSSSL NID:

签名算法

证书上的签名算法存储为OpenSSSL NID:

int pkey_nid = OBJ_obj2nid(cert->cert_info->key->algor->algorithm);

if (pkey_nid == NID_undef) {
	fprintf(stderr, "unable to find specified signature algorithm name.\n");
	return EXIT_FAILURE;
}

This can be translated into a string representation (either short name or long description):这可以转换为字符串表示(短名称或长描述):

char sigalgo_name[SIG_ALGO_LEN+1];
const char* sslbuf = OBJ_nid2ln(pkey_nid);

if (strlen(sslbuf) > PUBKEY_ALGO_LEN) {
	fprintf(stderr, "public key algorithm name longer than allocated buffer.\n");
	return EXIT_FAILURE;
}

strncpy(buf, sslbuf, PUBKEY_ALGO_LEN);

This will result in a string such as sha1WithRSAEncryption or md5WithRSAEncryption.这将导致一个字符串,如sha1WithRSAEncryption或md5WithRSAEncryption。

Public Key

Parsing the public key on a certificate is type-specific. Here, we provide information on how to extract which type of key is included and to parse RSA and DSA keys:

公钥

解析证书上的公钥是特定于类型的。在这里,我们提供有关如何提取包含哪种类型的密钥以及解析RSA和DSA密钥的信息:

char pubkey_algoname[PUBKEY_ALGO_LEN];

int pubkey_algonid = OBJ_obj2nid(cert->cert_info->key->algor->algorithm);

if (pubkey_algonid == NID_undef) {
	fprintf(stderr, "unable to find specified public key algorithm name.\n");
	return EXIT_FAILURE;
}

const char* sslbuf = OBJ_nid2ln(pubkey_algonid);
assert(strlen(sslbuf) < PUBKEY_ALGO_LEN);
strncpy(buf, sslbuf, PUBKEY_ALGO_LEN);

if (pubkey_algonid == NID_rsaEncryption || pubkey_algonid == NID_dsa) {

	EVP_PKEY *pkey = X509_get_pubkey(cert);
	IFNULL_FAIL(pkey, "unable to extract public key from certificate");

	RSA *rsa_key;
	DSA *dsa_key;
	char *rsa_e_dec, *rsa_n_hex, *dsa_p_hex, *dsa_p_hex,
			 *dsa_q_hex, *dsa_g_hex, *dsa_y_hex;

	switch(pubkey_algonid) {

		case NID_rsaEncryption:

			rsa_key = pkey->pkey.rsa;
			IFNULL_FAIL(rsa_key, "unable to extract RSA public key");

			rsa_e_dec = BN_bn2dec(rsa_key->e);
			IFNULL_FAIL(rsa_e_dec,  "unable to extract rsa exponent");

			rsa_n_hex = BN_bn2hex(rsa_key->n);
			IFNULL_FAIL(rsa_n_hex,  "unable to extract rsa modulus");

			break;

		case NID_dsa:

			dsa_key = pkey->pkey.dsa;
			IFNULL_FAIL(dsa_key, "unable to extract DSA pkey");

			dsa_p_hex = BN_bn2hex(dsa_key->p);
			IFNULL_FAIL(dsa_p_hex, "unable to extract DSA p");

			dsa_q_hex = BN_bn2hex(dsa_key->q);
			IFNULL_FAIL(dsa_q_hex, "unable to extract DSA q");

			dsa_g_hex = BN_bn2hex(dsa_key->g);
			IFNULL_FAIL(dsa_g_hex, "unable to extract DSA g");

			dsa_y_hex = BN_bn2hex(dsa_key->pub_key);
			IFNULL_FAIL(dsa_y_hex, "unable to extract DSA y");

			break;

		default:
			break;
	}

	EVP_PKEY_free(pkey);
}

Validity Period

OpenSSL represents the not-valid-after (expiration) and not-valid-before as ASN1_TIME objects, which can be extracted as follows:

有效期

OpenSSL表示无效之后(到期)和无效之前作为ASN1_TIME对象,可以按如下方式提取:

ASN1_TIME *not_before = X509_get_notBefore(cert);
ASN1_TIME *not_after = X509_get_notAfter(cert);

These can be converted into ISO-8601 timestamps using the following code:可以使用以下代码将它们转换为ISO-8601时间戳:

#define DATE_LEN 128

int convert_ASN1TIME(ASN1_TIME *t, char* buf, size_t len)
{
	int rc;
	BIO *b = BIO_new(BIO_s_mem());
	rc = ASN1_TIME_print(b, t);
	if (rc <= 0) {
		log_error("fetchdaemon", "ASN1_TIME_print failed or wrote no data.\n");
		BIO_free(b);
		return EXIT_FAILURE;
	}
	rc = BIO_gets(b, buf, len);
	if (rc <= 0) {
		log_error("fetchdaemon", "BIO_gets call failed to transfer contents to buf");
		BIO_free(b);
		return EXIT_FAILURE;
	}
	BIO_free(b);
	return EXIT_SUCCESS;
}

char not_after_str[DATE_LEN];
convert_ASN1TIME(not_after, not_after_str, DATE_LEN);

char not_before_str[DATE_LEN];
convert_ASN1TIME(not_before, not_before_str, DATE_LEN);

CA Status

Checking whether a certificate is a valid CA certificate is not a boolean operation as you might expect. There are several avenues through

which a certificate can be interpreted as CA certificate. As such, instead of directly checking various X.509 extensions,

it is more reliable to use X509_check_ca. Any value >= 1 is considered a CA certificate whereas 0 is not a CA certificate.

CA状态

检查证书是否是有效的CA证书不是您可能期望的布尔操作。有几种途径可以将证书解释为CA证书。因此,不是直接检查各种X.509扩展,更可靠的方法而是使用X509_check_ca。

任何值> = 1都被视为CA证书,而0不是CA证书。

int raw = X509_check_ca(cert);

Other X.509 Extensions

Certificates can contain any other arbitrary extensions. The following code will loop through all of the extensions on a certificate and print them out:

其他X.509扩展

证书可以包含任何其他任意扩展名。以下代码将遍历证书上的所有扩展并将其打印出来:

STACK_OF(X509_EXTENSION) *exts = cert->cert_info->extensions;

int num_of_exts;
if (exts) {
	num_of_exts = sk_X509_EXTENSION_num(exts);
} else {
	num_of_exts = 0
}

IFNEG_FAIL(num_of_exts, "error parsing number of X509v3 extensions.");

for (int i=0; i < num_of_exts; i++) {

	X509_EXTENSION *ex = sk_X509_EXTENSION_value(exts, i);
	IFNULL_FAIL(ex, "unable to extract extension from stack");
	ASN1_OBJECT *obj = X509_EXTENSION_get_object(ex);
	IFNULL_FAIL(obj, "unable to extract ASN1 object from extension");

	BIO *ext_bio = BIO_new(BIO_s_mem());
	IFNULL_FAIL(ext_bio, "unable to allocate memory for extension value BIO");
	if (!X509V3_EXT_print(ext_bio, ex, 0, 0)) {
		M_ASN1_OCTET_STRING_print(ext_bio, ex->value);
	}

	BUF_MEM *bptr;
	BIO_get_mem_ptr(ext_bio, &bptr);
	BIO_set_close(ext_bio, BIO_NOCLOSE);

	// remove newlines
	int lastchar = bptr->length;
	if (lastchar > 1 && (bptr->data[lastchar-1] == '\n' || bptr->data[lastchar-1] == '\r')) {
		bptr->data[lastchar-1] = (char) 0;
	}
	if (lastchar > 0 && (bptr->data[lastchar] == '\n' || bptr->data[lastchar] == '\r')) {
		bptr->data[lastchar] = (char) 0;
	}

	BIO_free(ext_bio);

	unsigned nid = OBJ_obj2nid(obj);
	if (nid == NID_undef) {
		// no lookup found for the provided OID so nid came back as undefined.
		char extname[EXTNAME_LEN];
		OBJ_obj2txt(extname, EXTNAME_LEN, (const ASN1_OBJECT *) obj, 1);
		printf("extension name is %s\n", extname);
	} else {
		// the OID translated to a NID which implies that the OID has a known sn/ln
		const char *c_ext_name = OBJ_nid2ln(nid);
		IFNULL_FAIL(c_ext_name, "invalid X509v3 extension name");
		printf("extension name is %s\n", c_ext_name);
	}

	printf("extension length is %u\n", bptr->length)
	printf("extension value is %s\n", bptr->data)
}

Misordered Certificate Chains

At times, we’ll receive misordered certificate chains. The following code will attempt to reorder certificates to construct a rational certificate chain

based on each certificate’s subject and issuer string. The algorithm is O(n^2), but we generally only receive two or three certificates and in the majority-case,

they will already be in the correct order.

顺序错误的证书链

有时,我们会收到错误的证书链。以下代码将尝试重新排序证书,以根据每个证书的主题和颁发者字符串构建合理的证书链。

该算法是O(n ^ 2),但我们通常只收到两个或三个证书,在大多数情况下,它们已经按正确的顺序排列。

STACK_OF(X509) *r_sk = sk_X509_new_null();
	sk_X509_push(r_sk, sk_X509_value(st, 0));

	for (int i=1; i < sk_X509_num(st); i++) {
		X509 *prev = sk_X509_value(r_sk, i-1);
		X509 *next = NULL;
		for (int j=1; j < sk_X509_num(st); j++) {
			X509 *cand = sk_X509_value(st, j);
			if (!X509_NAME_cmp(cand->cert_info->subject, prev->cert_info->issuer)
					|| j == sk_X509_num(st) - 1) {
				next = cand;
				break;
			}
		}
		if (next) {
			sk_X509_push(r_sk, next);
		} else {
			// we're unable to figure out the correct stack so just use the original one provided.
			sk_X509_free(r_sk);
			r_sk = sk_X509_dup(st);
			break;
		}
	}

Validating Certificates

In our scans, we oftentimes use multiple CA stores in order to emulate different browsers. Here, we describe how we create specialized stores and

validate against them.

We can create a store based on a particular file with the following:

验证证书

在我们的扫描中,我们经常使用多个CA存储来模拟不同的浏览器。在这里,我们描述了如何创建专门的商店并对其进行验证。

我们可以使用以下内容基于特定文件创建商店:

X509_STORE *s = X509_STORE_new();
if (s == NULL) {
	fprintf(stderr, "unable to create new X509 store.\n");
	return NULL;
}
int rc = X509_STORE_load_locations(s, store_path, NULL);
if (rc != 1) {
	fprintf(stderr, "unable to load certificates at %s to store\n", store_path);
	X509_STORE_free(s);
	return NULL;
}
return s;

And then validate certificates against the store with the following:然后使用以下内容验证商店的证书:

X509_STORE_CTX *ctx = X509_STORE_CTX_new();
if (!ctx) {
	fprintf(stderr, "unable to create STORE CTX\n");
	return -1;
}
if (X509_STORE_CTX_init(ctx, store, cert, st) != 1) {
	fprintf(stderr, "unable to initialize STORE CTX.\n");
	X509_STORE_CTX_free(ctx);
	return -1;
}
int rc = X509_verify_cert(ctx);
X509_STORE_CTX_free(ctx);
return rc;

It’s worth noting that self-signed certificates will always fail OpenSSL’s validation.

While this might make sense in most client applications, we are oftentimes interested in other errors that might be present.

We validate self-signed certificates by adding them into a temporary store and then validating against it. It’s a bick hackish,

but is much easier than re-implementing OpenSSL’s validation techniques.

值得注意的是,自签名证书将始终无法通过OpenSSL验证。虽然这在大多数客户端应用程序中可能有意义,

但我们通常会对可能存在的其他错误感兴趣。

我们通过将自签名证书添加到临时存储中然后验证它来验证它们。这是一个狂热的hackish,但比重新实现OpenSSL的验证技术要容易得多。

X509_STORE *s = X509_STORE_new();
int num = sk_X509_num(sk);
X509 *top = sk_X509_value(st, num-1);
X509_STORE_add_cert(s, top);
X509_STORE_CTX *ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(ctx, s, cert, st);
int rc = X509_verify_cert(ctx);
if (rc == 1) {
	// validated OK. either trusted or self signed.
} else {
	// validation failed
	int err = X509_STORE_CTX_get_error(ctx);
}

// any additional processing..

X509_STORE_CTX_free(ctx);
X509_STORE_free(s);

Sometimes you will also find that you just need to check whether a certificate has been issued

by a trusted source instead of just considering whether it is currently valid, which can be done using X509_check_issued.

For example, if you wanted to check whether a certificate was self-signed:

有时您还会发现您只需要检查证书是否由受信任的来源发出,而不是仅考虑它是否当前有效,这可以使用X509_check_issued。例如,如果要检查证书是否是自签名的:

if (X509_check_issued(cert, cert) == X509_V_OK) {
	is_self_signed = 1;
} else {
	is_self_signed = 0;
}

Helper Functions

There are several other functions that were used in troubleshooting and might be of help while you’re developing code against OpenSSL.

Print out the basic information about a certificate:

助手功能

还有一些其他功能用于故障排除,在您针对OpenSSL开发代码时可能会有所帮助。

打印出有关证书的基本信息:

#define MAX_LENGTH 1024

void print_certificate(X509* cert) {
	char subj[MAX_LENGTH+1];
	char issuer[MAX_LENGTH+1];
	X509_NAME_oneline(X509_get_subject_name(cert), subj, MAX_LENGTH);
	X509_NAME_oneline(X509_get_issuer_name(cert), issuer, MAX_LENGTH);
	printf("certificate: %s\n", subj);
	printf("\tissuer: %s\n\n", issuer);
}

Print out each certificate in a given stack:打印出给定堆栈中的每个证书:

void print_stack(STACK_OF(X509)* sk)
{
  	unsigned len = sk_X509_num(sk);
  	printf("Begin Certificate Stack:\n");
  	for(unsigned i=0; i<len; i++)
       {
  		    X509 *cert = sk_X509_value(sk, i);
    		print_certificate(cert);
    	}
  	printf("End Certificate Stack\n");
  }

Check whether two certificate stacks are identical:检查两个证书堆栈是否相同:

int certparse_sk_X509_cmp(STACK_OF(X509) *a, STACK_OF(X509) *b)
{
	int a_len = sk_X509_num(a);
	int b_len = sk_X509_num(b);
	if (a_len != b_len) {
		return 1;
	}
	for (int i=0; i < a_len; i++) {
		if (X509_cmp(sk_X509_value(a, i), sk_X509_value(b, i))) {
			return 1;
		}
	}
	return 0;
}

Check whether the subject and issuer string on a certificate are identical:检查证书上的主题和颁发者字符串是否相同:

int certparse_subjeqissuer(X509 *cert)
{
	char *s = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
	char *i = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
	int rc = strcmp(s, i);
	OPENSSL_free(s);
	OPENSSL_free(i);
	return (!rc);
}

Convert an OpenSSL error constant into a human readable string:将OpenSSL错误常量转换为人类可读的字符串:

const char* get_validation_errstr(long e) {
	switch ((int) e) {
		case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
			return "ERR_UNABLE_TO_GET_ISSUER_CERT";
		case X509_V_ERR_UNABLE_TO_GET_CRL:
			return "ERR_UNABLE_TO_GET_CRL";
		case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
			return "ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE";
		case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
			return "ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE";
		case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
			return "ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY";
		case X509_V_ERR_CERT_SIGNATURE_FAILURE:
			return "ERR_CERT_SIGNATURE_FAILURE";
		case X509_V_ERR_CRL_SIGNATURE_FAILURE:
			return "ERR_CRL_SIGNATURE_FAILURE";
		case X509_V_ERR_CERT_NOT_YET_VALID:
			return "ERR_CERT_NOT_YET_VALID";
		case X509_V_ERR_CERT_HAS_EXPIRED:
			return "ERR_CERT_HAS_EXPIRED";
		case X509_V_ERR_CRL_NOT_YET_VALID:
			return "ERR_CRL_NOT_YET_VALID";
		case X509_V_ERR_CRL_HAS_EXPIRED:
			return "ERR_CRL_HAS_EXPIRED";
		case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
			return "ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD";
		case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
			return "ERR_ERROR_IN_CERT_NOT_AFTER_FIELD";
		case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
			return "ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD";
		case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
			return "ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD";
		case X509_V_ERR_OUT_OF_MEM:
			return "ERR_OUT_OF_MEM";
		case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
			return "ERR_DEPTH_ZERO_SELF_SIGNED_CERT";
		case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
			return "ERR_SELF_SIGNED_CERT_IN_CHAIN";
		case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
			return "ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY";
		case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
			return "ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE";
		case X509_V_ERR_CERT_CHAIN_TOO_LONG:
			return "ERR_CERT_CHAIN_TOO_LONG";
		case X509_V_ERR_CERT_REVOKED:
			return "ERR_CERT_REVOKED";
		case X509_V_ERR_INVALID_CA:
			return "ERR_INVALID_CA";
		case X509_V_ERR_PATH_LENGTH_EXCEEDED:
			return "ERR_PATH_LENGTH_EXCEEDED";
		case X509_V_ERR_INVALID_PURPOSE:
			return "ERR_INVALID_PURPOSE";
		case X509_V_ERR_CERT_UNTRUSTED:
			return "ERR_CERT_UNTRUSTED";
		case X509_V_ERR_CERT_REJECTED:
			return "ERR_CERT_REJECTED";
		case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
			return "ERR_SUBJECT_ISSUER_MISMATCH";
		case X509_V_ERR_AKID_SKID_MISMATCH:
			return "ERR_AKID_SKID_MISMATCH";
		case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
			return "ERR_AKID_ISSUER_SERIAL_MISMATCH";
		case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
			return "ERR_KEYUSAGE_NO_CERTSIGN";
		case X509_V_ERR_INVALID_EXTENSION:
			return "ERR_INVALID_EXTENSION";
		case X509_V_ERR_INVALID_POLICY_EXTENSION:
			return "ERR_INVALID_POLICY_EXTENSION";
		case X509_V_ERR_NO_EXPLICIT_POLICY:
			return "ERR_NO_EXPLICIT_POLICY";
		case X509_V_ERR_APPLICATION_VERIFICATION:
			return "ERR_APPLICATION_VERIFICATION";
		default:
			return "ERR_UNKNOWN";
	}
}
友情链接
版权所有 Copyright(c)2004-2021 锐英源软件
公司注册号:410105000449586 豫ICP备08007559号 最佳分辨率 1024*768
地址:郑州大学北校区院(文化路97号院)内