首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >相互身份验证和将用户证书限制在服务器上的特定设置

相互身份验证和将用户证书限制在服务器上的特定设置
EN

Stack Overflow用户
提问于 2016-04-24 09:32:58
回答 1查看 889关注 0票数 2

我正在寻找一种使用OpenSSL API将客户端证书限制为服务器端特定的自签名证书集的方法。

有一组可信的自签名证书,比如./dir/*.pem。我想拒绝连接,如果它们不提供其中一个证书。

通过比较SSL上下文验证回调中的服务器和客户端证书指纹,我可以实现几乎期望的行为:

代码语言:javascript
复制
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()

代码语言:javascript
复制
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 .
代码语言:javascript
复制
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包)实际上有效

代码语言:javascript
复制
if (!preverify_ok && err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
  preverify_ok = 1;
}

所以没有这个街区,一切都能正常工作。服务器响应与CAbundle.pem中列出的证书之一连接的客户端。如果客户端使用不同的证书连接,则服务器将关闭连接。

然而,有一件奇怪的事情。在这两种情况下,openssl s_client输出:

代码语言:javascript
复制
Verify return code: 18 (self signed certificate)

那也许

代码语言:javascript
复制
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包含服务器证书,否则它不信任服务器证书。服务器证书是自签名的。

EN

回答 1

Stack Overflow用户

发布于 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结果的匹配,也可以在搜索在索引中找到匹配之后,通过验证完整的证书来进行双重检查。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36821430

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档