HAProxy SSL终止+客户端证书验证+ curl / java客户端

时间:2015-05-25 18:44:18

标签: java ssl curl haproxy keytool

我希望在HAProxy上使用自己的自签名证书进行SSL终止,并使用我创建的客户端证书验证客户端访问权限。

我通过以下方式创建服务器(也是CA)证书:

openssl genrsa -out ca.key 1024
openssl req -new -key ca.key -out ca.csr
openssl x509 -req -days 365 -in ca.csr -signkey ca.key -out ca.crt

cat ca.crt ca.key > haproxy.pem

在HAProxy,我配置: bind *:443 ssl crt /path/server.pem ca-file /path/ca.crt验证所需的crt-ignore-err all

我以类似的方式创建客户端证书:

openssl req -new -key client.key -out client.csr
openssl x509 -req -days 365 -in client.csr -signkey ca.key -out client.crt
cat client.crt client.key > client.pem

我的逻辑是:我正在创建一个客户端密钥,一个证书签名请求,然后我使用CA(也是服务器证书)对其进行签名,因此有一个简单的链条,服务器会识别。)

要进行测试,我首先尝试使用服务器证书作为客户端证书:

curl https://my.service:443/ping -E ./haproxy.pem -k
pong
好的,它有效。现在我尝试使用客户端证书作为客户端证书:

curl https://my.service:443/ping -E ./client.pem -k
curl: (58) unable to set private key file: './client.pem' type PEM

我的问题: 1)我想创建一个该服务器将使用的客户端证书,并使用curl进行测试。 2)我想使用keytool将此证书和CA导入新的Java密钥库/信任库,以便Java(Jersey客户端)代码可以访问相同的内容。

我在1/2上花了2天。 我很确定之前完成此操作的人可以在5米内回答这个问题。或者我希望如此。 :)

谢谢!

1 个答案:

答案 0 :(得分:2)

<强> 1。创建客户证书

错误: openssl x509 -req -signkey创建自签名证书,根据定义,该证书表示 证书中的密钥 key)是同一个密钥的公共一半,其私有一半签署证书。 cert(而不是req)情况的文档很明显,它使用签名密钥替换了以前在cert中的密钥。 -req doc不太清楚,但它做同样的事情;它将来自CSR的主题名称(也作为发行者)和来自-signkey的密钥放入证书中。您已使用包含客户端名称的CSR,但包含CA密钥的-signkey,会产生无法使用的嵌合体。

正确:使用x509签署“子”(非自签名)证书,使用-CA,可能-CAkey,如文档中所述https://www.openssl.org/docs/apps/x509.html#SIGNING-OPTIONS(或安装了openssl doc的任何Unix上的man [where] x509)。如果给定CA(由其DN定义)有或将有多个子证书,请使用序列号文件方案自动方便地分配顺序序列号,或使用-set_serial手动分配唯一的序列号(顺序是最简单的独特方式,但如果你更喜欢另一种方式,那就没问题。)

对于自签名CA(和服务器?!)证书

一边,您不需要单独的req -newx509 -req -signkey步骤,您可以在一个req -new -x509。请参阅req的doc /联机帮助页。实际上,您不需要单独的genrsa步骤,req -newkey [-nodes] -x509也可以执行此操作。需要注意的是:在OpenSSL 1.0.0+中,这会生成通用的PKCS#8格式密钥文件,而不是genrsa(和rsa)使用的“遗留”PKCS#1格式;所有OpenSSL函数都可以接受,但其他一些东西可能不接受。特别是最后我检查了(前一段时间)Wireshark选项,使用服务器密钥解密SSL / TLS for akRSA(还有其他选项)只接受PKCS#1而不是PKCS#8。

<强> 2。在Java(Jersey)中使用。请注意,执行客户端身份验证的任何 SSL / TLS客户端(包括Java)都需要证书和私钥,并且在大多数情况下都是如此。证书使用您需要的“链”或“中间”证书。有些人(咳嗽)微软(咳嗽)鼓励你误解并忽略这个重要的区别,但如果你试图只使用证书就根本不起作用。另一方面, truststore 条目只需要证书,几乎总是只有 root (CA)证书,并且通常必须只有证书。您的情况是,同一个人操作CA 服务器客户端对于PKC来说有点不寻常。

<强> 2a上。也许只是转换为pkcs12。 Java不直接支持密钥的openssl格式,但Java和openssl都支持PKCS#12(微软,Mozilla,Apple等可能也是如此)。由于您在client.pem执行

中组合了客户端密钥和(叶)证书
openssl pkcs12 -export <client.pem -CA ca.crt [-name whatever] >client.p12
# if you use separate key,cert files see the doc about -in and -inkey

Java加密(JCE和JSSE)可以将此PKCS#12用作密钥库, if 可以配置密钥库“type”(如pkcs12)。默认的SSLSocketFactory支持这一点,我使用过的其他应用也是如此,但我不使用Jersey,也不知道它在这里做了什么。 PKCS#12通常不支持携带“单独”证书(没有私钥),但在您的情况下,客户端的CA证书服务器的证书,因此它将碰巧工作作为你的信任库;否则,您需要将服务器CA或服务器自签名证书(仅限cert而非私钥)导入JKS信任库(可能是JRE / lib / security / [jsse] cacerts中的缺省信任库)。

<强> 2B。可能会进一步转换为JKS。如果Jersey不能直接使用PKCS#12,Java可以将其转换为JKS,其中任何理智的Java代码可以使用,例如:

keytool -importkeystore -srckeystore client.p12 -srcstoretype pkcs12 -destkeystore client.jks 

更新2018年:在写完这个答案后,PKCS12的Java支持增加,使得转换为JKS的频率降低。 2017年秋季发布的8u60仍然默认为密钥库类型JKS,但作为一个特殊功能(?)类型JKS实际上可以读取(虽然不写)PKCS12;请参阅文件keystore.type.compat中的the release notes和项JRE/lib/security/java.security。 2017发布的Java9使得默认密钥库类型PKCS12(正如预期的那样)读写PKCS12,尽管显式JKS不再读取PKCS12。但是,如果由于某种原因需要使用Java9进行转换,则现在需要指定-deststoretype jks,但不再需要指定-srcstoretype pkcs12