Linux Apache 不会请求我的 SSL 客户端证书

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10252476/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 05:55:17  来源:igfitidea点击:

Apache Won't Request My SSL Client Certificate

phplinuxapachesslopenssl

提问by Kris Craig

First off, please note that I am new to configuring SSL. In the past, I've always been fortunate to have an IT department to set that up for me ahead of time. So be prepared for the possibility that I might need to ask for clarification on some of your answers. =)

首先,请注意我是配置 SSL 的新手。过去,我一直很幸运能有一个 IT 部门提前为我进行设置。因此,请为我可能需要对您的某些答案进行澄清的可能性做好准备。=)

What I'm Trying to Do

我想做什么

I'm setting-up a company intranet website for employees. For example, there will be a browser start page that shows content customized for each employee. As such, I need to be able to identify said employee without requiring any username/password or other prompting (one-time setup is ok though; I just don't want them having to be prompted every time). Naturally, SSL would seem to be the best way to go about doing this.

我正在为员工建立一个公司内联网网站。例如,将有一个浏览器起始页,显示为每个员工定制的内容。因此,我需要能够在不需要任何用户名/密码或其他提示的情况下识别所述员工(尽管一次性设置是可以的;我只是不想每次都提示他们)。自然,SSL 似乎是执行此操作的最佳方式。

I'll have a MySQL database setup to associate "user" accounts with SSL_CLIENT_M_SERIAL and SSL_CLIENT_I_DN, which I'm assuming will be unique for each client certificate(?). I got that idea from this article: http://cweiske.de/tagebuch/ssl-client-certificates.htm

我将有一个 MySQL 数据库设置来将“用户”帐户与 SSL_CLIENT_M_SERIAL 和 SSL_CLIENT_I_DN 相关联,我假设每个客户端证书(?)都是唯一的。我从这篇文章中得到了这个想法:http: //cweiske.de/tagebuch/ssl-client-certificates.htm

The first time the user goes to the internal website, they won't have a certificate (I do NOT want to be generating them manually for clients!), in which case $_SERVER["SSL_CLIENT_VERIFY"] == "NONE". If that happens, it'll go to the user account setup page, which will include a step where PHP generates an SSL client certificate and sends it to the browser for the user to install. Nice and simple. The user then installs the cert, the association is made, and after restarting the browser (for good measure), the user goes back to the internal website.

用户第一次访问内部网站时,他们将没有证书(我不想为客户端手动生成它们!),在这种情况下 $_SERVER["SSL_CLIENT_VERIFY"] == "NONE"。如果发生这种情况,它将转到用户帐户设置页面,其中包括一个步骤,PHP 生成 SSL 客户端证书并将其发送到浏览器供用户安装。好看又简单。然后用户安装证书,建立关联,并在重新启动浏览器后(为了更好的措施),用户返回内部网站。

At this point, Apache should request the client certificate, which the browser then sends. The PHP script then parses the necessary $_SERVER variables, compares against the MySQL database, and good times are had by all. Again, nice and simple.

此时,Apache 应请求客户端证书,然后浏览器发送该证书。PHP 脚本然后解析必要的 $_SERVER 变量,与 MySQL 数据库进行比较,所有人都度过了美好的时光。再次,漂亮而简单。

What's Working So Far

到目前为止的工作

I have the server-side certificates installed. And yes, they are self-signed (for obvious reasons). Apache has mod_ssl installed and all that seems to be working fine. I created a PHP script that just dumps the $_SERVER array, and all the SSL_SERVER_* key values match the certificate I created for it.

我安装了服务器端证书。是的,它们是自签名的(出于显而易见的原因)。Apache 已经安装了 mod_ssl 并且所有这些似乎都运行良好。我创建了一个 PHP 脚本,它只转储 $_SERVER 数组,并且所有 SSL_SERVER_* 键值都与我为其创建的证书相匹配。

The Problem

问题

I can't get the client certificates to work! In that same PHP script, no matter what I do, SSL_CLIENT_VERIFY == "NONE" and the other SSL_CLIENT_* keys are missing. This is what happens if I have SSLVerifyClient set to optional in ssl.conf. According to every tutorial I've read, they all say that the webserver should ask the browser for a client certificate. Thing is, I can't get it to do that! It just goes straight to the PHP script and assumes I have no client certificates at all. This happens in Firefox, Chrome, and IE.

我无法让客户端证书工作!在同一个 PHP 脚本中,无论我做什么,都缺少 SSL_CLIENT_VERIFY == "NONE" 和其他 SSL_CLIENT_* 键。如果我在 ssl.conf 中将 SSLVerifyClient 设置为可选,就会发生这种情况。根据我读过的每个教程,他们都说网络服务器应该向浏览器询问客户端证书。问题是,我不能让它这样做!它只是直接进入 PHP 脚本,并假设我根本没有客户端证书。这发生在 Firefox、Chrome 和 IE 中。

So I tried setting SSLVerifyClient to required and restarted the webserver. With that option in place, I can't even establish an SSL connection. Firefox just says the connection has been reset (other browsers display their own versions of that error as well). What's weird is that the logs don't show ANY activity on these connection attempts! I.e. access_log, error_log, ssl_access_log, ssl_error_log, AND ssl_request_log all don't show ANYTHING; it's as if the attempt never even occurred. This is frustrating because it means I don't even have an error message to work from. Just a webserver passive-aggressively telling me to go to hell.

所以我尝试将 SSLVerifyClient 设置为 required 并重新启动了网络服务器。有了这个选项,我什至无法建立 SSL 连接。Firefox 只是说连接已重置(其他浏览器也会显示他们自己的错误版本)。奇怪的是日志没有显示这些连接尝试的任何活动!即 access_log、error_log、ssl_access_log、ssl_error_log 和 ssl_request_log 都不显示任何内容;就好像这种尝试从未发生过一样。这令人沮丧,因为这意味着我什至没有可以处理的错误消息。只是一个网络服务器被动地告诉我去地狱。

I tried generating/installing my own client certificate manually using PHP's OpenSSL extension. The certificate installed just fine, though I can't find any information on how to associate that certificate with the server one (assuming I even need to?). Also, it doesn't seem to matter anyway, because Apache still won't even request a client certificate if optional is set. And if require is set, it just blows up without explanation. I need it to be set to optional anyway for this schema to work.

我尝试使用 PHP 的 OpenSSL 扩展手动生成/安装我自己的客户端证书。证书安装得很好,但我找不到有关如何将该证书与服务器相关联的任何信息(假设我什至需要这样做?)。此外,无论如何它似乎都无关紧要,因为如果设置了 optional ,Apache 甚至不会请求客户端证书。如果设置了 require,它就会在没有解释的情况下爆炸。无论如何,我需要将它设置为可选,以便此架构正常工作。

The Environment

环境

OS: CentOS 5.7 64-bit (VirtualBox)

操作系统:CentOS 5.7 64 位(VirtualBox)

Apache: 2.2.3

阿帕奇:2.2.3

PHP: 5.3.10

PHP:5.3.10

I'm guessing you might need more info to help me, so please ask! I'll provide you with whatever you need.

我猜您可能需要更多信息来帮助我,所以请询问!我会为你提供你需要的任何东西。

To summarize, I need to know how to get Apache to request an SSL client certificate given the conditions outlined above. Also, if there's any special signing/etc that has to be done to make the client certificate "compatible" with the server certificate (again, WITHOUT doing so manually via shell for each client cert!), I'll need to know that as well.

总而言之,我需要知道如何在上述条件下让 Apache 请求 SSL 客户端证书。此外,如果必须执行任何特殊的签名/等操作以使客户端证书与服务器证书“兼容”(同样,无需通过 shell 为每个客户端证书手动执行此操作!),我需要知道这一点好。

I am 100% stuck on this as of now. Can't find anything even remotely helpful on Google. Any help you can provide on this would be TREMENDOUSLY appreciated!! Thanks! =)

到目前为止,我 100% 坚持这一点。在谷歌上找不到任何远程帮助的东西。您可以在这方面提供的任何帮助将非常感谢!!谢谢!=)

回答by hornetbzz

You may have a look to the apache docif not done already.

如果尚未完成,您可以查看apache 文档

The general principle is that you create your self-signed cert and check it before trying to use it.

一般原则是您创建自签名证书并在尝试使用它之前检查它。

Then it looks like the client connects to your intranet site through http. From there, there are many different ways to switch to https using your ssl cert. The easiest way is to use the apache rewrite module. But in your case, as you are making php/mysql checks, you may redirect your client from http to to https, which is not the simple way.

然后看起来客户端通过http连接到您的Intranet站点。从那里,有许多不同的方法可以使用您的 ssl 证书切换到 https。最简单的方法是使用 apache 重写模块。但是在您的情况下,当您进行 php/mysql 检查时,您可能会将您的客户端从 http 重定向到 https,这不是简单的方法。

In any of both cases (apache automatic redirect through mod_rewrite, or redirection by cascading tests (php/javascript/html), you need to set up your 2 vhosts (one for http and one for https) in the proper way, but this assumes some hypothesis.

在这两种情况下(通过 mod_rewrite 进行 apache 自动重定向,或通过级联测试(php/javascript/html)进行重定向),您需要以正确的方式设置 2 个虚拟主机(一个用于 http,一个用于 https),但这假设一些假设。

For example (debian - apache 2.2), here is an automatic redirect, done by Apache (eg 1st case described above) :

例如(debian - apache 2.2),这是一个自动重定向,由 Apache 完成(例如上述第一种情况):

cat /etc/apache2/sites-available/test

cat /etc/apache2/sites-available/test

# VHOST test

<VirtualHost *:80>

    DocumentRoot /home/www/test
    ServerName www.test.dev

    # ######################
    # Redirect commons
    # ######################
    RewriteEngine on
    # Case of vhosts
    RewriteOptions Inherit

    # ######################
    # Redirect (empty index)
    # ######################

    # Condition 1 to redirect : request matching with the server
    RewriteCond %{HTTP_HOST}   ^www\.test\.dev [NC]

    # Condition 2 to redirect : non empty HOST
    RewriteCond %{HTTP_HOST}   !^$

    # Automatic Empty requests Redirect
    RewriteRule ^(.*)/$ /index.php


    # ######################
    # Redirect to SSL
    # ######################
    RewriteCond %{HTTP_HOST}   ^www\.test\.dev [NC]
    RewriteCond %{HTTP_HOST}   !^$
    RewriteCond %{SERVER_PORT} ^80$
    RewriteCond %{REQUEST_URI} /

    # Redirection
    RewriteRule ^/(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]

</VirtualHost>

The second virtual host for SSL : cat /etc/apache2/sites-available/test-ssl

SSL 的第二个虚拟主机: cat /etc/apache2/sites-available/test-ssl

# VHOST for ssl

DocumentRoot "/home/www/test"
ServerName www.test.dev

    # SSL
    SSLEngine on
    SSLCACertificateFile /etc/apache2/ssl/cur_cert/ca.pem
    SSLCertificateFile /etc/apache2/ssl/cur_cert/serveur.pem
    SSLCertificateKeyFile /etc/apache2/ssl/cur_cert/serveur.key


    <Directory "/home/www/test">
        Options FollowSymLinks MultiViews
        AllowOverride None
        Order allow,deny
        Allow from 127.0.0.1 192.168.0.0/16
    </Directory>


    <Directory "/home/www/test/cgi-bin">
        Options FollowSymLinks MultiViews
        AllowOverride None
        Order allow,deny
        Allow from 127.0.0.1 192.168.0.0/16
        Options +ExecCGI
        AddHandler cgi-script .cgi
    </Directory>

</VirtualHost>

Your case might defer slightly from this, eg you will not have the redirect portion in the 1st vhost, but only a simple vhost and the second one for https (ssl). The redirection will be done by php/javascript once you have achieved your mysql checks.

您的情况可能与此稍有不同,例如,您在第一个虚拟主机中不会有重定向部分,而只有一个简单的虚拟主机和用于 https (ssl) 的第二个虚拟主机。完成 mysql 检查后,重定向将由 php/javascript 完成。

Here is an example abstract from a php class for the way to cascade the switch from http to https, using php, then javascript, then html :

这是一个来自 php 类的示例摘要,用于将切换从 http 级联到 https,使用 php,然后是 javascript,然后是 html:

public function Redirect($url){

    if (TRUE !== Validator::isValidURL($url))
        die ("FATAL ERR: url not valid");

    // PHP ABSOLUTE URL REDIRECT (HTTP1.1)
    if (!headers_sent()) {

        header("Status: 200");
        header("Cache-Control: no-cache, must-revalidate"); // required for HTTP/1.1
        header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // past Date
        header("Pragma: no-cache");
        header('Location: '.$url); // note: 302 code return by default with "Location"
        flush();
        exit();

        // if headers are already sent... do javascript redirect... if javascript is disabled, do html redirect.
    } else {

        // Js redirect
        echo '<script type="text/javascript">';
        //echo "<!--";
        echo 'document.location="'. $url .'";';
        //echo "//-->";
        echo '</script>';

        // HTML redirect if js disabled
        echo '<noscript>';
        echo '<meta http-equiv="refresh" content="0;url="'.$url.'" />';
        echo '</noscript>';

        exit();
    }

    return FALSE;

} /* end of method (redirect) */

Hope it helps you to better understand how to proceed and adapt this approach to your specific case.

希望它可以帮助您更好地了解如何继续并根据您的具体情况调整这种方法。

回答by Bruno

First, you need to configure Apache Httpd to request a client certificate. For this, you need at least to use SSLVerifyClient optionalon the location/directory you want to be authenticated with this method.

首先,您需要配置 Apache Httpd 以请求客户端证书。为此,您至少需要在要使用SSLVerifyClient optional此方法进行身份验证的位置/目录上使用。

Secondly, the certificates sent by the client needs to be trusted by Apache Httpd too. (You could in principle use SSLVerifyClient optional_no_ca, let any client cert through at the Apache Httpd SSL/TLS stack, and only then verify the certificate within PHP, but that's quite a bit of work, for which you need be a bit more careful since that's not necessarily easy code; more importantly, this would be quite useless in this context, since you're in a scenario where you're in control of your CA.)

其次,客户端发送的证书也需要被Apache Httpd信任。(原则上您可以使用SSLVerifyClient optional_no_ca, 让任何客户端证书通过 Apache Httpd SSL/TLS 堆栈,然后才在 PHP 中验证证书,但这需要大量工作,因此您需要更加小心,因为这不是必然是简单的代码;更重要的是,在这种情况下,这将毫无用处,因为您处于可以控制 CA 的场景中。)

As far as I understand, SSL_CLIENT_VERIFY(a variable that I haven't used much myself) seems only really useful with the SSLVerifyClient optional_no_ca. It might work with SSLVerifyClient optional, but I doubt so. SSLVerifyClient requirewill reject connections using a client certificate that is not trusted (by one of the CAs in SSLCACertificateFile/SSLCACertificatePath), or if there is no certificate. As far as I know, SSLVerifyClient optionalwill let the client through without a certificate or with a trusted certificate, but will also reject the connection if the certificate is not trusted.

据我所知,SSL_CLIENT_VERIFY(我自己很少使用的变量)似乎只对SSLVerifyClient optional_no_ca. 它可能适用于SSLVerifyClient optional,但我对此表示怀疑。SSLVerifyClient require将拒绝使用不受信任的客户端证书(由SSLCACertificateFile/中的 CA 之一SSLCACertificatePath)或如果没有证书的连接。据我所知,SSLVerifyClient optional将让客户端在没有证书或受信任证书的情况下通过,但如果证书不受信任,也会拒绝连接。

Here, by rejecting the connection, I mean closing the SSL/TLS connection abruptly with an alert. There is no chance to produce an HTTP(S) error page. All you'll get in the standard browser error, something along the lines of ssl_error_unknown_certificate_.... (You should consider this in terms of usability.)

在这里,拒绝连接的意思是突然关闭 SSL/TLS 连接并发出警报。没有机会产生 HTTP(S) 错误页面。所有你会在标准浏览器错误中得到的,类似于ssl_error_unknown_certificate_.... (您应该从可用性的角度考虑这一点。)

From then onwards, what you need is to set up your own CA, possibly web-based with in-browser key-generation and within the same website. You wouldn't want SSLVerifyClient requirefor that, because you would need to let the users who don't have a certificate yet in (use optionalinstead). This being said, these directives need not apply to the entire host, but can be specific to certain locations/directories.

从那时起,您需要的是设置您自己的 CA,可能基于 Web 并在浏览器内生成密钥并在同一网站内。您不希望SSLVerifyClient require这样做,因为您需要让还没有证书的用户进入(optional改为使用)。话虽如此,这些指令不需要适用于整个主机,但可以特定于某些位置/目录。

Integrating your own web-based CA (or more generally, creating your own CA) isn't necessarily easy if you're new to all this. Ready-made tools exist (e.g. OpenCA), or you can build your own using various bits of JavaScript/ActiveX, and you would need the server-side code to handle the SPKAC or PKCS#10 requests (and to issue the actual certificate). (For such a CA to be useful, you'd want the users who apply for a new certificate to provide some proof of ID at the time of application, perhaps a password.)

如果您不熟悉这一切,那么集成您自己的基于 Web 的 CA(或更一般地说,创建您自己的 CA)并不一定容易。存在现成的工具(例如OpenCA),或者您可以使用各种JavaScript/ActiveX构建自己的工具,并且您需要服务器端代码来处理 SPKAC 或 PKCS#10 请求(并颁发实际证书) . (为了使这样的 CA 有用,您希望申请新证书的用户在申请时提供一些 ID 证明,可能是密码。)

When this is set up, you should configure SSLCACertificateFile(or ...Path) to point to the CA certificate of your internal CA (whether it's a web-based CA or not, on the same site or not). (Of course, keep the private key of your CA private, perhaps configured within your CA web-based application, but Apache Httpd itself doesn't need to know about it.) Browsers will only suggest certificates issued by those CAs or intermediates (unless you've also configured SSLCADNRequestFile, which would be used to send the list of accepted CAs instead).

设置完成后,您应该配置SSLCACertificateFile(或...Path)指向内部 CA 的 CA 证书(无论它是否是基于 Web 的 CA,是否在同一站点上)。(当然,将您的 CA 的私钥保密,可能是在您的基于 Web 的 CA 应用程序中配置的,但 Apache Httpd 本身不需要知道它。)浏览器只会建议由这些 CA 或中间机构颁发的证书(除非您还配置了SSLCADNRequestFile,它将用于发送已接受的 CA 列表)。

Note that these two steps (setting up your CA and setting up your website to use client-certificates) really are independent in fact. The fact that both can be part of the same site can be convenient, but isn't necessary. You could try out the Apache Httpd set up without deploying an entire CA on the site first (I'd recommend that, even if it's just to see what you're getting into). There are a number of tools to create your own little CA that are manageable with a handful of certificates: OpenSSL's CA.plor TinyCAfor example. You could also use these test certificates(localhostand testclient, testclient_ris revoked if you want to use the CRL, probably not necessary at first): all passwords are testtest.

请注意,这两个步骤(设置 CA 和设置网站以使用客户端证书)实际上是独立的。两者都可以属于同一个站点的事实可能很方便,但不是必需的。您可以在不首先在站点上部署整个 CA 的情况下尝试 Apache Httpd 设置(我建议这样做,即使只是为了看看您要进入什么)。有许多工具可以创建您自己的小型 CA,这些工具可以使用少量证书进行管理:例如OpenSSL 的 CA.plTinyCA。你也可以使用这些测试证书localhosttestclienttestclient_r被撤销,如果你想使用CRL,最初可能没有必要):所有密码都是testtest

As you've already anticipated (with your MySQL DB), you'll need to manage the certificates you issue and map them to users. SSL_CLIENT_M_SERIALand SSL_CLIENT_I_DNare not the right variables to use, though. SSL_CLIENT_I_DNis the Issuer DN (i.e. the CA's Subject DN). What you'd be looking for is SSL_CLIENT_S_DN: the client cert Subject DN. SSL_CLIENT_M_SERIALis the certificate serial number: don't use it, since it's unique per certificate: one user could have multiple certificates with the same Subject DN (e.g. if one expires or is revoked).

正如您已经预料到的(使用您的 MySQL 数据库),您需要管理您颁发的证书并将它们映射到用户。SSL_CLIENT_M_SERIAL但是,SSL_CLIENT_I_DN并不是要使用的正确变量。SSL_CLIENT_I_DN是颁发者 DN(即 CA 的主题 DN)。您要寻找的是SSL_CLIENT_S_DN:客户端证书主题 DN。SSL_CLIENT_M_SERIAL是证书序列号:不要使用它,因为它是每个证书唯一的:一个用户可以拥有多个具有相同主题 DN 的证书(例如,如果一个证书过期或被撤销)。



Despite all this, I'm not sure whether client-certificates are the best way to achieve your goal (letting the employees in your company log on without password).

尽管如此,我不确定客户证书是否是实现您目标的最佳方式(让您公司的员工无需密码即可登录)。

  • Firstly, the user should protect their own certificates with a password anyway. What you're really after is some form of Single-Sign On (SSO), I guess.

  • Secondly, depending on the degree of computer-literacy of your users, certificates can actually be quite difficult to manage.

    The fact that the word "certificate", strictly speaking, doesn't include the private key at all, but sometimes implies usage of the private key can be confusing for some. On the one hand, you sometimes hear "Import your certificate into your browser" and "Use your certificate to log in"; on the other hand, you can also hear "send me your certificate". The former implies usage and availability of the private key ("certificate" might just mean .p12in these expressions). The latter definitely shouldn't involve the private key.

    Browser user interfaces tend to be quite poor or confusing for managing the certificates or logging out. Again, if the certificate isn't recognised, the SSL/TLS connection will not be established, so the web server doesn't get a chance to display an HTML error page of any sort.

  • 首先,用户无论如何都应该使用密码保护自己的证书。我猜您真正想要的是某种形式的单点登录 (SSO)。

  • 其次,根据用户的计算机知识水平,证书实际上可能很难管理。

    严格来说,“证书”一词根本不包括私钥,但有时暗示使用私钥这一事实可能会让某些人感到困惑。一方面,您有时会听到“将您的证书导入浏览器”和“使用您的证书登录”;另一方面,您还可以听到“将您的证书发送给我”。前者意味着私钥的使用和可用性(“证书”可能只是.p12在这些表达中的意思)。后者绝对不应该涉及私钥。

    浏览器用户界面在管理证书或注销时往往非常糟糕或令人困惑。同样,如果证书未被识别,则不会建立 SSL/TLS 连接,因此 Web 服务器没有机会显示任何类型的 HTML 错误页面。

Perhaps you could also consider other forms of SSO (e.g. CAS, something SAML-based or Kerberos/SPNEGO.)

也许您还可以考虑其他形式的 SSO(例如 CAS、基于 SAML 的东西或 Kerberos/SPNEGO。)

回答by lgaggini

I have a similar problem with:

我有一个类似的问题:

  • CentOS 6.3
  • Apache 2.2.15
  • CentOS 6.3
  • 阿帕奇 2.2.15

After some tries i recognize my problem.

经过一些尝试,我认识到我的问题。

If I set SSLVerifyClient optionalor SSLVerifyClient optional_no_caand I specify also SSLCACertificateFileor SSLCACertificatePath, Apache acquires the client certificate only if it's released from CA found in the CA reference file/path specified in configuration.

如果我设置了SSLVerifyClient optionalorSSLVerifyClient optional_no_ca并且我还指定了SSLCACertificateFileor SSLCACertificatePath,则仅当客户端证书从配置中指定的 CA 参考文件/路径中的 CA 发布时,Apache 才会获取客户端证书。