0

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?

  • Why? The extent of the certificate chain you send is determined by JSSE in accordance with what the server requested in the `CertificateRequest` message. It's no business of the application what that might be. – user207421 Sep 08 '20 at 09:50
  • Did you see this question and the answer? https://stackoverflow.com/questions/36202894/gradle-use-certificate-authentication-for-repository – Guillaume Sep 08 '20 at 10:04
  • @Guillaume See it why? Different problem, different solution. Hard to see what the problem is here, if any. Chain length has nothign to do with 'no suitable certificate found', and certainly nothing to do with anything in your link. – user207421 Sep 08 '20 at 10:31
  • @MarquisofLorne, do you mean my certificate does not match certificate request? How may I learn/debug mismatch reason than? – Andrey Kuznetsov Sep 08 '20 at 13:25
  • @Guillaume, truststore is not my case: server's chain ends with well known root CA. – Andrey Kuznetsov Sep 08 '20 at 13:25
  • My understanding is that the certificate works (the server accepted it in your small test application) but now you're trying to get Gradle to use it. Is that correct? – Guillaume Sep 08 '20 at 13:30
  • @Guillaume, yes. Not Gradle itself, but JSSE. – Andrey Kuznetsov Sep 08 '20 at 15:52
  • this should help: https://stackoverflow.com/questions/63719397/should-i-always-load-keystore-explicitely-in-my-webclient-for-authorized-service – Guillaume Sep 08 '20 at 16:28

1 Answers1

0

After some debugging I found the reason. javax.net.ssl.keyStore* system properties do not affect Gradle Artifactory Plugin, it knew nothing about client certificate, and certificate chain was in fact empty (zero length).

If there is at least one certificate in a chain then it is sent to server successfully.