HtmlUnit / htmlunit

HtmlUnit is a "GUI-Less browser for Java programs".
https://www.htmlunit.org
Apache License 2.0
842 stars 166 forks source link

Set SSL Client Certificate #684

Open alexvrv opened 7 months ago

alexvrv commented 7 months ago

Hi, is there a way to set a specific Certificate (X509Certificate) for a SSL connection? I see options to set one but it requires a InputStream which I don't have. In the KeyStore I have 3 certificates and I need to be able to use a specific one. Or maybe a SSLContext like JSOUP uses

SSLContext sc = SSLContext.getInstance("TLS"); sc.init(new X509ExtendedKeyManager[] {km}, null, null); Jsoup.connect(URL).sslSocketFactory(sc.getSocketFactory()).ignoreContentType(true).execute();

rbri commented 7 months ago

there was a similar thing - maybe you can have a look at https://github.com/HtmlUnit/htmlunit/issues/623

rbri commented 7 months ago

for filtering out you can have a look at the comment of method setSSLClientCertificate

alexvrv commented 7 months ago

Yeah I know. The problem is that if I load the entire KeyStore, it chooses the first Certificate in that KeyStore ... if I have multiple Certificates well I can't choose a specific one (they are all valid and usable). I have multiple certificates that I use for different websites, and of course they are on a USB stick so I can't store them and build another KeyStore.

rbri commented 7 months ago

but you can wrap the key store and provide only the one required

rbri commented 7 months ago

but i will have a look again, maybe we can provide a more convenient way to do that, or maybe we can have a default impl for that use case

alexvrv commented 7 months ago

What do you mean by wrapping the KeyStore? I don't really get that, sorry

rbri commented 7 months ago

Implement a keystore, that forwards most of the methods to the containing keystore but filters the certificates. See the wrapper pattern....

alexvrv commented 7 months ago
X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
InputStream is = new ByteArrayInputStream(cert.getEncoded());
webClient.getOptions().setSSLClientCertificate(is, "", "");

Not even this works because in the setSSLClientCertificate you retrieve the entire KeyStore again.

rbri commented 7 months ago

@alexvrv

  1. you are (more or less :-)) right - i think the naming of the method is confusing (e.g. setSSLClientCertificate should be named setSSLClientCertificateKeyStore)
  2. i like to change this a bit and provide more documentation

Can we do this together - i will update the impl and the documentation and you do the review and testing?

rbri commented 7 months ago

@alexvrv ok my hint with the wrapped keystore was totally wrong - there is no way to implement such a wrapper.

Instead you can create a new (in memory) KeyStore and add only the certificates you like to use.

see https://www.baeldung.com/java-keystore for some hints how to do this

Then you set this KeyStore using WebClientOptions.setSSLClientCertificateKeyStore(KeyStore, char[])

Does this work for you?

alexvrv commented 7 months ago

@alexvrv

  1. you are (more or less :-)) right - i think the naming of the method is confusing (e.g. setSSLClientCertificate should be named setSSLClientCertificateKeyStore)
  2. i like to change this a bit and provide more documentation

Can we do this together - i will update the impl and the documentation and you do the review and testing?

Sure!

alexvrv commented 7 months ago

@alexvrv ok my hint with the wrapped keystore was totally wrong - there is no way to implement such a wrapper.

Instead you can create a new (in memory) KeyStore and add only the certificates you like to use.

see https://www.baeldung.com/java-keystore for some hints how to do this

Then you set this KeyStore using WebClientOptions.setSSLClientCertificateKeyStore(KeyStore, char[])

Does this work for you?

I've tried this but it works only with PKCS12. I have PKCS11 (certificates on a USB memory stick) which cannot be stored... I can't save the private key and move it into a "in memory" keystore :( If I try to store the keystore it generates and empty file and from what I've read, this is the behavior of PKCS11 because the private key is stored only on the USB...

rbri commented 7 months ago

@alexvrv maybe is still on the wrong path:

My current understanding is

Correct?

rbri commented 7 months ago

and the solution from #623 does not work - reading all three and then remove the not used ones?

alexvrv commented 6 months ago

@alexvrv maybe is still on the wrong path:

My current understanding is

  • you have an usb stick holding some (3) PKCS11certificates - but in one 'file'
  • you are able to read the certificates together into one (java) key store

    • there is no way to read only one certificate
  • you like to provide only one certificate to HtmlUnit - not the whole key store with all three

Correct?

I have 3 USB sticks, each having their own certificate. The rest is correct.

alexvrv commented 6 months ago

and the solution from #623 does not work - reading all three and then remove the not used ones?

This method kind of works. I can delete the not used certificates, but they get deleted for good. If I want to use another certificate (that I've deleted earlier) I need to reinstall it xD

alexvrv commented 5 months ago

Any news about this? :(