我正在寻找一种使用OpenSSL API将客户端证书限制为服务器端特定的自签名证书集的方法。
有一组可信的自签名证书,比如./dir/*.pem。我想拒绝连接,如果它们不提供其中一个证书。
通过比较SSL上下文验证回调中的服务器和客户端证书指纹,我可以实现几乎期望的行为:
SSL_CTX *ctx;
...
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback);
static inline int get_fingerprint(X509* cert, unsigned char *md, unsigned int *n)
{
return X509_digest(cert, EVP_sha1(), md, n);
}
static inline int compare_certificates(X509 *c1, X509 *c2)
{
unsigned char md1[EVP_MAX_MD_SIZE], md2[EVP_MAX_MD_SIZE];
unsigned int n1, n2;
if (!(get_fingerprint(c1, md1, &n1) && get_fingerprint(c2, md2, &n2))) {
return -1;
}
return memcmp(md1, md2, n1);
}
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
{
SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
int err = X509_STORE_CTX_get_error(ctx);
/* Allow self-signed certificates */
if (!preverify_ok && err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
preverify_ok = 1;
}
if (0 != compare_certificates(ctx->current_cert, SSL_CTX_get0_certificate(ssl->ctx))) {
/* Peer certificate doesn't match the server certificate */
preverify_ok = 0;
}
/* More checks ... */
return preverify_ok;
}因此,如果服务器和客户端证书指纹匹配,则验证通过。否则,服务器将关闭连接。
我可能会在初始化阶段的某个地方计算受信任证书的指纹,然后在verify_callback中的一个循环中检查它们。不过,我不喜欢这个主意。应该有更简单的方法来做到这一点。
我以为SSL_CTX_load_verify_locations()正是我想要的(但看起来并非如此,我会解释原因):
SSL_CTX_load_verify_locations()指定用于验证目的的CA证书所在的ctx位置。..。如果CAfile不是NULL,它将指向一个文件的CA证书的PEM格式。该文件可以包含多个CA证书.只有在需要时才会查找CApath中的证书,例如在构建证书链或实际执行对等证书验证时。
(位置)
嗯,我想SSL_VERIFY_FAIL_IF_NO_PEER_CERT意味着验证对等证书。然后,看起来我所需要做的就是制作一捆受信任的证书,并将其传递给SSL_CTX_load_verify_locations()。
bundle_file=CAbundle.pem
cd ./dir
rm -f $bundle_file
for i in *.pem; do
openssl x509 -in $i -text >> $bundle_file
done
c_rehash .SSL_CTX *ctx;
const char *cafile = "dir/CAbundle.pem";
const char *capath = NULL;
...
if (!SSL_CTX_load_verify_locations(ctx, cafile, capath)) {
/* Unable to set verify locations ... */
}
cert_names = SSL_load_client_CA_file(cafile);
if (cert_names != NULL) {
SSL_CTX_set_client_CA_list(ctx, cert_names);
} else {
/* Handle error ... */
}一切看起来都很好。但是服务器仍然接受具有不同对等证书的连接。
我在这里使用标准的OpenSSL实用程序再现了这种行为:https://gist.github.com/rosmanov/d960a5d58a96bdb730303c5b8e86f951
因此,我的问题是:如何将服务器配置为只接受仅提供特定证书的对等服务器?
更新
当我从verify_callback中删除以下内容时,我发现证书的“白名单”(CA包)实际上有效
if (!preverify_ok && err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
preverify_ok = 1;
}所以没有这个街区,一切都能正常工作。服务器响应与CAbundle.pem中列出的证书之一连接的客户端。如果客户端使用不同的证书连接,则服务器将关闭连接。
然而,有一件奇怪的事情。在这两种情况下,openssl s_client输出:
Verify return code: 18 (self signed certificate)那也许
if (!preverify_ok
&& err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
&& allow_self_signed
&& !cafile
&& !capath) {
preverify_ok = 1;
}更新2
现在我明白了为什么openssl s_client输出Verify return code: 18 (self signed...)。除非-CAfile或-CApath包含服务器证书,否则它不信任服务器证书。服务器证书是自签名的。
发布于 2016-04-25 21:18:20
如果需要特定客户端证书的白名单,则可以在初始化时在内存中准备索引列表。
例如,可以使用PEM_X509_INFO_read读取所有客户端证书的连接文件。这将给您一个证书的STACK_OF(X509_INFO)*。在sk_X509_INFO_num中可以找到证书的数量,您可以在sk_X509_INFO_value(..)->x509上看到每个证书。
然后,例如,您可以简单地构建一个内存中的索引和qsort通过compare_x509.
现在,当您的验证回调被调用时,只需通过bsearch对您的索引执行一个compare_x509,或者证书在您的白名单上,或者没有。
您可以接受compare_x5099结果的匹配,也可以在搜索在索引中找到匹配之后,通过验证完整的证书来进行双重检查。
https://stackoverflow.com/questions/36821430
复制相似问题