围绕这个主题的整体 OpenSSL 文档相当有限,并且到处都有断开的链接,所以我的方法可能不是唯一的一种或最好的一种。据我所知,验证证书(链)是通过以下步骤完成的,以相反的顺序展开,因为我认为这样可以更好地理解。对于生成的代码,请参阅此答案的结尾。为简洁起见,所有代码都省略了错误检查。此外,没有解释证书吊销列表 (CRL) 的加载,我认为这超出了您的问题范围。
实际验证功能
验证证书(链)的功能由 OpenSSL 函数 X509_verify_cert() 提供。返回值1 表示验证成功,0 表示不成功。正如您在文档中看到的那样,该函数只需要一个X509_STORE_CTX 类型的参数,这是一个包含X509 证书集合的“上下文”(OpenSSL,IMO 中一个相当模糊和过度使用的术语)的结构参与。
设置证书存储上下文
证书存储上下文包含有关受信任证书、不受信任的中间证书和要验证的证书的信息。它的构造和初始化如下:
store_ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(store_ctx, store, cert, intermediates)
store 参数将用于包含有关受信任证书的信息,cert 参数包含要验证的证书,而intermediates 参数是一堆不受信任的中间证书。
store 参数
X509_STORE 类型能够包含一组 X509 证书,并且为了验证证书需要提供有关受信任证书的信息。由于您在/etc/ssl/certs 中表明您拥有受信任的证书,因此可以按如下方式进行:
store = X509_STORE_new();
lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
X509_LOOKUP_add_dir(lookup, "/etc/ssl/certs", X509_FILETYPE_PEM);
这假设您的本地信任存储已正确设置
cert 参数`
此参数包含要验证的实际证书。它可以通过多种方式从文件中加载,一种方法如下:
bio_in = BIO_new_file(certFileName, "r");
result = PEM_read_bio_X509(bio_in, NULL, NULL, NULL);
BIO_free(bio_in);
intermediates 参数
OpenSSL 提供了一个stack API 来处理对象集合。 intermediates 参数是一组 X509 对象,其中包含要测试的证书和受信任证书之间的中间证书。在伪代码中可以填写如下:
intermediates = sk_X509_new_null();
for (filename in certFilenames) do {
icert = readCert(filename);
sk_X509_push(intermediates, icert);
}
解释到此结束,这应该为您提供验证链所需的一切。
**关于下载链末尾的证书
下载链末尾的证书通常包含在您的本地信任存储中。一些实验表明,您实际上可以将其输入验证函数,就好像它是一个不受信任的中间体一样,或者您可以省略它。两者似乎都以经过适当验证的链条结束。
代码示例
最后:-)
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <openssl/pem.h>
const char *trustedCertsPath = "/etc/ssl/certs";
int main(
int argc,
char **argv)
{
X509 *cert = NULL;
X509 *icert = NULL;
STACK_OF(X509) *intermediates = NULL;
X509_STORE *store = NULL;
X509_LOOKUP *lookup = NULL;
X509_STORE_CTX *store_ctx = NULL;
BIO *bio_in = NULL;
int currentArg = 1;
int result = 0;
store = X509_STORE_new();
lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
X509_LOOKUP_add_dir(lookup, trustedCertsPath, X509_FILETYPE_PEM);
/* Certificate to be checked */
bio_in = BIO_new_file(argv[currentArg++], "r");
cert = PEM_read_bio_X509(bio_in, NULL, NULL, NULL);
BIO_free(bio_in);
/* Stack of untrusted intermediate certificates */
intermediates = sk_X509_new_null();
while (currentArg < argc) {
bio_in = BIO_new_file(argv[currentArg++], "r");
icert = PEM_read_bio_X509(bio_in, NULL, NULL, NULL);
BIO_free(bio_in);
sk_X509_push(intermediates, icert);
}
store_ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(store_ctx, store, cert, intermediates);
result = X509_verify_cert(store_ctx);
printf("Result from X509_verify_cert is %d\n", result);
sk_X509_pop_free(intermediates, X509_free);
X509_STORE_CTX_cleanup(store_ctx);
X509_STORE_CTX_free(store_ctx);
X509_STORE_free(store);
}
您可以按如下方式构建和运行它(.pem 参数是包含您的证书和 PEM 格式的中间体的文件的名称:
$ gcc main.c $(pkg-config openssl --libs) -o verify -Wall
$ ./verify \*.google.com.pem Google\ Internet\ Authority\ G3.pem
Result from X509_verify_cert is 1