1. 创建客户端证书
错误方式:使用openssl x509 -req -signkey
命令创建自签名证书,这意味着证书中的密钥(主题密钥)与用于签署证书的私有密钥的公共半部分相同。证书(而不是 req)的文档清楚地说明,它将原来在证书中的密钥替换为签名密钥。虽然-req
的说明不太清楚,但它也会完成同样的操作;它将CSR中的主题名称以及作为颁发者和-signkey
中的密钥放入证书中。您使用一个包含客户端名称的CSR,但是使用包含CA密钥的-signkey
生成了一个无法使用的合成体。
正确方式:要使用x509
签署“子”(非自签名)证书,请按照文档https://www.openssl.org/docs/apps/x509.html#SIGNING-OPTIONS中描述的方式使用-CA
和可能的-CAkey
选项(或在安装了openssl文档的任何Unix上使用man [where] x509
)。如果给定CA(由其DN定义)有一个或多个子证书,则使用序列号文件方案自动和方便地分配顺序编号,或者使用-set_serial
手动分配唯一序列号(顺序是实现唯一的最简单方式,但如果您喜欢其他方式也可以)。
备注:对于自签名CA(以及服务器?!)证书,您无需单独使用req -new
和x509 -req -signkey
步骤,可以在req
命令中一次性完成:req -new -x509
。请参阅req
的文档/手册。实际上,您也不需要单独使用genrsa
步骤,req -newkey [-nodes] -x509
也可以执行此操作。请注意:在OpenSSL 1.0.0+中,这将生成通用PKCS#8格式密钥文件,而不是genrsa
(和rsa
)使用的“传统”PKCS#1格式。所有OpenSSL函数都可以接受这两种格式,但某些其他事情可能不行。特别是,在我上次检查(一段时间前),Wireshark使用服务器密钥进行解密SSL/TLS的选项仅接受PKCS#1而不是PKCS#8。
2. 在Java中使用(Jersey)。请注意,任何进行客户端身份验证的SSL/TLS客户端,包括Java,都需要证书和私钥,并且在大多数情况下,证书使用“链”或“中间”证书,您也需要这些证书。一些人(咳咳)微软(咳咳)鼓励您误解并忽略这个重要的区别,但如果您尝试仅使用证书,则根本无法工作。另一方面,信任库条目仅需要证书,几乎始终只需要根(CA)证书,并且通常必须只有证书。您的情况有点不同寻常,因为同一个人操作CA、服务器和客户端。
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作为密钥库,如果您可以配置密钥库的“类型”(如pkcs12)。默认的SSLSocketFactory
支持此功能,其他我使用过的应用程序也支持,但我不使用Jersey也不知道它在这里做了什么。通常不支持使用PKCS#12携带“单独”的证书(无私钥),但在您的情况下,客户端的CA证书也是服务器的证书,因此它将同时作为您的信任库工作;否则,您需要将服务器CA或服务器自签名证书(只有证书而没有私钥)导入到JKS信任库中(这可能是JRE/lib/security/[jsse]cacerts中的默认信任库)。
2b. 可能进一步转换为JKS。 如果Jersey不能直接使用PKCS#12,则Java可以将其转换为JKS,任何合理的Java代码都可以使用,例如:
keytool -importkeystore -srckeystore client.p12 -srcstoretype pkcs12 -destkeystore client.jks
UPDATE 2018: 在本回答撰写之后,Java对PKCS12的支持增强了,因此不再需要经常转换为JKS。 2017年秋季发布的8u60及更高版本仍然默认使用密钥库类型JKS,但是作为一种特殊功能(?),JKS实际上可以读取(但无法写入)PKCS12;请参阅
发行说明和文件
JRE/lib/security/java.security
中的项目
keystore.type.compat
。 Java9在2017年发布,将默认密钥库类型设置为PKCS12,它(如预期)可以读取和写入PKCS12,尽管显式的JKS不再读取PKCS12。但是,如果您出于某种原因确实需要使用Java9进行转换,现在需要指定
-deststoretype jks
,但不再需要指定
-srcstoretype pkcs12
。