问题描述
Gradle项目需要将构建结果部署到私有Artifactory服务器。后期需要客户端TLS身份验证。服务器所有者将用户证书作为具有1个私钥条目的密钥库提供,并且该条目具有大小为1的认证链:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
Alias name: myalias
Creation date: 08.09.2020
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=myuser,O=Sharaga Inc,C=Far far away
Issuer: CN=CA,C=Far far away
...
当Gradle尝试连接到服务器时,Java TLS实现拒绝发送客户端证书:
*** CertificateRequest
Cert Types: RSA,DSS,ECDSA
Supported Signature Algorithms: SHA256withRSA,SHA256withDSA,SHA256withECDSA,SHA384withRSA,Unknown (hash:0x5,signature:0x2),SHA384withECDSA,SHA512withRSA,Unknown (hash:0x6,SHA512withECDSA,SHA1withRSA,SHA1withDSA,SHA1withECDSA
Cert Authorities:
<CN=CA,C=Far far away>
pool-1-thread-1,READ: TLSv1.2 Handshake,length = 4
*** ServerHelloDone
Warning: no suitable certificate found - continuing without client authentication
*** Certificate chain
<Empty>
在Java 8和Java 11(在Linux和Windows上)都可以观察到此行为。
我试图通过修改密钥管理器实现来使用测试应用程序访问服务器,以便它始终选择“ myalias”发送给服务器,并且有效:
// Uses Artifactory client library:
// https://github.com/jfrog/artifactory-client-java
PrivateKeyStrategy aliasStrategy = new PrivateKeyStrategy() {
@Override
public String chooseAlias(Map<String,PrivateKeyDetails> aliases,Socket socket) {
return "myalias";
}
};
char[] password = "keystorepassword".toCharArray();
Artifactory artifactory = ArtifactoryClientBuilder.create()
.setUrl("https://theserver/artifactory")
.setUsername("myuser")
.setPassword("mypassword")
.setSslContextBuilder(SSLContexts.custom().loadKeyMaterial(new File("mykeystore.pfx"),password,aliasStrategy))
.build();
TLS调试输出:
*** CertificateRequest
Cert Types: RSA,C=Far far away>
main,length = 4
*** ServerHelloDone
matching alias: myalias
*** Certificate chain
chain [0] = [
[
Version: V3
Subject:CN=myuser,C=Far far away
Signature Algorithm: SHA256withRSA,OID = 1.2.840.113549.1.1.11
...
(这里的链长为1,但是握手成功。)
是否有一种方法可以在现有Java应用程序中强制发送不带证书链的客户端证书,而无需更改/重新编译它?也许有一些系统属性可以调整此行为?
解决方法
经过一些调试后,我找到了原因。 javax.net.ssl.keyStore*
系统属性不会影响Gradle Artifactory插件,它对客户端证书一无所知,并且证书链实际上是空的(长度为零)。
如果链中至少有一个证书,则该证书将成功发送到服务器。