Gradle project needs to deploy build results to private Artifactory server. Latter requires client TLS authentication. User certificates are provided by server owner as a keystore with 1 private key entry, and that entry has certification chain of size 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, O=Sharaga Inc, C=Far far away
...
When Gradle tries to connect to server, Java TLS implementation refuses to send client certificate:
*** CertificateRequest
Cert Types: RSA, DSS, ECDSA
Supported Signature Algorithms: SHA256withRSA, SHA256withDSA, SHA256withECDSA, SHA384withRSA, Unknown (hash:0x5, signature:0x2), SHA384withECDSA, SHA512withRSA, Unknown (hash:0x6, signature:0x2), SHA512withECDSA, SHA1withRSA, SHA1withDSA, SHA1withECDSA
Cert Authorities:
<CN=CA, O=Sharaga Inc, 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>
This behavior is observed under Java 8 and Java 11, both on Linux and Windows.
I tried to access the server with test application by modifying key manager implementation so that it always chooses "myalias" to send to server, and it worked:
// 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, password, aliasStrategy))
.build();
TLS debug output:
*** CertificateRequest
Cert Types: RSA, DSS, ECDSA
Supported Signature Algorithms: SHA256withRSA, SHA256withDSA, SHA256withECDSA, SHA384withRSA, Unknown (hash:0x5, signature:0x2), SHA384withECDSA, SHA512withRSA, Unknown (hash:0x6, signature:0x2), SHA512withECDSA, SHA1withRSA, SHA1withDSA, SHA1withECDSA
Cert Authorities:
<CN=CA, O=Sharaga Inc, C=Far far away>
main, READ: TLSv1.2 Handshake, length = 4
*** ServerHelloDone
matching alias: myalias
*** Certificate chain
chain [0] = [
[
Version: V3
Subject:CN=myuser, O=Sharaga Inc, C=Far far away
Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
...
(Chain length is 1 here, but handshake succeeds.)
Is there a way to send client certificate without certification chain forcefully in existing Java application without changing/recompiling it? Maybe there are some system properties to tune this behavior?