如何在不修改Java应用程序源的情况下发送不带证书链的TLS客户端证书?

问题描述

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插件,它对客户端证书一无所知,并且证书链实际上是空的(长度为零)。

如果链中至少有一个证书,则该证书将成功发送到服务器。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...