HAProxy SSL 终止 + 客户端证书验证 + curl / java 客户端
HAProxy SSL termination + client certificate validation + curl / java client
我想使用我自己的自签名证书在 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.创建客户端证书
错误: openssl x509 -req -signkey
创建了一个 自签名 证书,根据定义,这意味着密钥 在 中,证书(主题密钥)是同一密钥的 public 一半,其私人一半签署证书。 cert (not req) 案例的文档很清楚,它用签名密钥替换了证书中先前的密钥。 -req
文档不太清楚,但它做了同样的事情;它在证书中放入来自 CSR 的主题名称,也作为颁发者,以及来自 -signkey
的密钥。您使用了包含客户端名称的 CSR,但 -signkey
包含 CA 密钥,产生了无法使用的嵌合体。
正确: 签署 "child"(非自签名)证书 x509
,使用 -CA
并可能使用 -CAkey
如文档 https://www.openssl.org/docs/apps/x509.html#SIGNING-OPTIONS 中所述(或 man [where] x509
在任何安装了 openssl doc 的 Unix 上)。如果给定 CA(由其 DN 定义)存在或曾经有多个子证书,请使用序列号文件方案自动方便地分配连续序列号,或使用 -set_serial
手动分配唯一序列号(顺序是实现唯一性的最简单方法,但如果您更喜欢其他方式也可以)。
旁白: 对于自签名 CA(和服务器?!)证书,您不需要单独的 req -new
和 x509 -req -signkey
步骤,您可以一次性完成 req -new -x509
。请参阅 req
的 doc/manpage。事实上,您不需要单独的 genrsa
步骤,req -newkey [-nodes] -x509
也可以做到这一点。一个注意事项:在 OpenSSL 1.0.0+ 中,这会生成通用的 PKCS#8 格式密钥文件,而不是 genrsa
(和 rsa
)使用的 "legacy" PKCS#1 格式;所有 OpenSSL 功能都可以接受,但有些其他功能可能不接受。特别是最后一次(不久前)我检查了使用 akRSA 的服务器密钥解密 SSL/TLS 的 Wireshark 选项(也有其他选项)只接受 PKCS#1 而不是 PKCS#8。
2。使用在Java (Jersey). 注意any SSL/TLS client 做客户端认证,包括Java,需要两个证书 和私钥,在大多数情况下,证书使用您还需要的 "chain" 或 "intermediate" 证书。有些人(咳)微软(咳)鼓励你误解和忽略这个重要的区别,但如果你试图只使用证书它根本不起作用。另一方面,truststore 条目只需要证书,几乎总是只需要 root (CA) 证书,通常 必须 只有证书。同一个人操作 CA and 服务器 and client(s) 的情况对于 PKC 来说有些不寻常。
2a。也许只是转换为 pkcs12。 Java 不直接支持密钥的 openssl 格式,但 Java 和 openssl 都支持 PKCS#12(Microsoft、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 作为密钥库,如果 你可以配置密钥库 "type"(作为 pkcs12 ).默认的 SSLSocketFactory
支持这个,我用过的其他应用程序也是如此,但我不使用 Jersey,也不知道它在这里做了什么。 PKCS#12 通常不支持携带 "separate" 证书(没有私钥),但在您的情况下,客户端的 CA 证书是 also 服务器的证书,所以它也将恰好作为您的信任库工作;否则,您需要将服务器 CA 或服务器自签名证书(仅证书而非私钥)导入 JKS 信任库(这可能是 JRE/lib/security/[jsse]cacerts 中的默认信任库)。
2b。可能会进一步转换为 JKS。 如果 Jersey 不能直接使用 PKCS#12,Java 可以将其转换为 JKS,any 理智的 Java 代码可以使用,例如:
keytool -importkeystore -srckeystore client.p12 -srcstoretype pkcs12 -destkeystore client.jks
2018 年更新:写完此答案后 Java 增加了对 PKCS12 的支持,从而减少了转换为 JKS 的必要性。 2017 年秋季及以后发布的 8u60 仍然默认为密钥库类型 JKS,但作为一个特殊功能(?)类型 JKS 实际上可以读取(尽管不能写入)PKCS12;请参阅文件 JRE/lib/security/java.security
中的 the release notes 和项目 keystore.type.compat
。 Java2017 年发布的 9 使默认密钥库类型 PKCS12(如预期的那样)读取和写入 PKCS12,尽管显式 JKS 不再读取 PKCS12。但如果出于某种原因确实需要使用 Java9 进行转换,您现在需要指定 -deststoretype jks
但不再需要指定 -srcstoretype pkcs12
.
我想使用我自己的自签名证书在 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.创建客户端证书
错误: openssl x509 -req -signkey
创建了一个 自签名 证书,根据定义,这意味着密钥 在 中,证书(主题密钥)是同一密钥的 public 一半,其私人一半签署证书。 cert (not req) 案例的文档很清楚,它用签名密钥替换了证书中先前的密钥。 -req
文档不太清楚,但它做了同样的事情;它在证书中放入来自 CSR 的主题名称,也作为颁发者,以及来自 -signkey
的密钥。您使用了包含客户端名称的 CSR,但 -signkey
包含 CA 密钥,产生了无法使用的嵌合体。
正确: 签署 "child"(非自签名)证书 x509
,使用 -CA
并可能使用 -CAkey
如文档 https://www.openssl.org/docs/apps/x509.html#SIGNING-OPTIONS 中所述(或 man [where] x509
在任何安装了 openssl doc 的 Unix 上)。如果给定 CA(由其 DN 定义)存在或曾经有多个子证书,请使用序列号文件方案自动方便地分配连续序列号,或使用 -set_serial
手动分配唯一序列号(顺序是实现唯一性的最简单方法,但如果您更喜欢其他方式也可以)。
旁白: 对于自签名 CA(和服务器?!)证书,您不需要单独的 req -new
和 x509 -req -signkey
步骤,您可以一次性完成 req -new -x509
。请参阅 req
的 doc/manpage。事实上,您不需要单独的 genrsa
步骤,req -newkey [-nodes] -x509
也可以做到这一点。一个注意事项:在 OpenSSL 1.0.0+ 中,这会生成通用的 PKCS#8 格式密钥文件,而不是 genrsa
(和 rsa
)使用的 "legacy" PKCS#1 格式;所有 OpenSSL 功能都可以接受,但有些其他功能可能不接受。特别是最后一次(不久前)我检查了使用 akRSA 的服务器密钥解密 SSL/TLS 的 Wireshark 选项(也有其他选项)只接受 PKCS#1 而不是 PKCS#8。
2。使用在Java (Jersey). 注意any SSL/TLS client 做客户端认证,包括Java,需要两个证书 和私钥,在大多数情况下,证书使用您还需要的 "chain" 或 "intermediate" 证书。有些人(咳)微软(咳)鼓励你误解和忽略这个重要的区别,但如果你试图只使用证书它根本不起作用。另一方面,truststore 条目只需要证书,几乎总是只需要 root (CA) 证书,通常 必须 只有证书。同一个人操作 CA and 服务器 and client(s) 的情况对于 PKC 来说有些不寻常。
2a。也许只是转换为 pkcs12。 Java 不直接支持密钥的 openssl 格式,但 Java 和 openssl 都支持 PKCS#12(Microsoft、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 作为密钥库,如果 你可以配置密钥库 "type"(作为 pkcs12 ).默认的 SSLSocketFactory
支持这个,我用过的其他应用程序也是如此,但我不使用 Jersey,也不知道它在这里做了什么。 PKCS#12 通常不支持携带 "separate" 证书(没有私钥),但在您的情况下,客户端的 CA 证书是 also 服务器的证书,所以它也将恰好作为您的信任库工作;否则,您需要将服务器 CA 或服务器自签名证书(仅证书而非私钥)导入 JKS 信任库(这可能是 JRE/lib/security/[jsse]cacerts 中的默认信任库)。
2b。可能会进一步转换为 JKS。 如果 Jersey 不能直接使用 PKCS#12,Java 可以将其转换为 JKS,any 理智的 Java 代码可以使用,例如:
keytool -importkeystore -srckeystore client.p12 -srcstoretype pkcs12 -destkeystore client.jks
2018 年更新:写完此答案后 Java 增加了对 PKCS12 的支持,从而减少了转换为 JKS 的必要性。 2017 年秋季及以后发布的 8u60 仍然默认为密钥库类型 JKS,但作为一个特殊功能(?)类型 JKS 实际上可以读取(尽管不能写入)PKCS12;请参阅文件 JRE/lib/security/java.security
中的 the release notes 和项目 keystore.type.compat
。 Java2017 年发布的 9 使默认密钥库类型 PKCS12(如预期的那样)读取和写入 PKCS12,尽管显式 JKS 不再读取 PKCS12。但如果出于某种原因确实需要使用 Java9 进行转换,您现在需要指定 -deststoretype jks
但不再需要指定 -srcstoretype pkcs12
.