eclipse.platform icon indicating copy to clipboard operation
eclipse.platform copied to clipboard

javax.net.ssl.trustStoreType=Windows-ROOT is not working with jgit

Open markusschaefer opened this issue 10 months ago • 39 comments

Let's make sure issue is not already fixed in latest builds first.

Steps to reproduce

From a fresh installation and clean workspace:

  • Import an existing git repository as eclipse project from the locale filesystem
  • Run a git operation against the remote origin

I expected: the certificate from the git server is trusted. When I remove the new VM args -Djavax.net.ssl.trustStoreType=Windows-ROOT -Djavax.net.ssl.trustStore=NONE everything is fine.

But got: I got an SunCertPathBuilderException: unable to find valid certification path to requested target

Here is some relevant log output

From <workspace>/.metadata/.log

Caused by: org.eclipse.jgit.errors.TransportException: https://myGitHost/myRepo.git: Secure connection to https://myGitHost/myRepo.git could not be established because of SSL problems
	at org.eclipse.jgit.transport.TransportHttp.handleSslFailure(TransportHttp.java:840)
	at org.eclipse.jgit.transport.TransportHttp.connect(TransportHttp.java:741)
	at org.eclipse.jgit.transport.TransportHttp.openFetch(TransportHttp.java:465)
	at org.eclipse.jgit.transport.FetchProcess.executeImp(FetchProcess.java:153)
	at org.eclipse.jgit.transport.FetchProcess.execute(FetchProcess.java:105)
	at org.eclipse.jgit.transport.Transport.fetch(Transport.java:1458)
	at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:238)
	... 3 more
Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:130)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:383)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:326)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:321)
	at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1318)
	at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1195)
	at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1138)
	at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:393)
	at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:476)
	at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:447)
	at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:206)
	at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:172)
	at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1506)
	at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1421)
	at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:455)
	at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:426)
	at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:436)
	at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:384)
	at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
	at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:376)
	at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393)
	at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
	at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
	at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
	at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
	at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
	at org.eclipse.jgit.transport.http.apache.HttpClientConnection.execute(HttpClientConnection.java:281)
	at org.eclipse.jgit.transport.http.apache.HttpClientConnection.getResponseCode(HttpClientConnection.java:260)
	at org.eclipse.jgit.util.HttpSupport.response(HttpSupport.java:232)
	at org.eclipse.jgit.transport.TransportHttp.connect(TransportHttp.java:663)
	... 8 more
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:388)
	at java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:271)
	at java.base/sun.security.validator.Validator.validate(Validator.java:256)
	at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:230)
	at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132)
	at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1302)
	... 36 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at java.base/sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:148)
	at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:129)
	at java.base/java.security.cert.CertPathBuilder.build(CertPathBuilder.java:297)
	at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:383)
	... 41 more

Tested under this environment:

  • OS & version: Windows 10
  • Eclipse IDE/Platform version (as shown in Help > About): Version: 2024-12 (4.34.0) Build id: 20241128-0757
  • Eclipse.ini contains vmargs: -Djavax.net.ssl.trustStoreType=Windows-ROOT -Djavax.net.ssl.trustStore=NONE

Community

  • [X] I understand reporting an issue to this OSS project does not mandate anyone to fix it. Other contributors may consider the issue, or not, at their own convenience. The most efficient way to get it fixed is that I fix it myself and contribute it back as a good quality patch to the project.

markusschaefer avatar Jan 16 '25 16:01 markusschaefer

Does your Windows certificate store contain the root/intermediate certificates? Press Win + R and enter certmgr.msc to check.

Mailaender avatar Jul 29 '25 12:07 Mailaender

Why not report it directly in JGit?

fedejeanne avatar Jul 30 '25 05:07 fedejeanne

@sratz

Are there any updates on plans to provide a store that merges the windows root cacerts and the JRE built-in cacerts? The M3 cutoff is rapidly approaching.

merks avatar Jul 30 '25 07:07 merks

Does your Windows certificate store contain the root/intermediate certificates? Press Win + R and enter certmgr.msc to check.

I can‘t check at the moment, but I think so. The git url is trusted in the browser and on cli.

markusschaefer avatar Jul 30 '25 10:07 markusschaefer

Why not report it directly in JGit?

The root cause is not in jgit. Other SSL Connections within Eclipse to internal systems are rejected too. This Change is cause of the behavior.

markusschaefer avatar Jul 30 '25 10:07 markusschaefer

Yes, that redirects the Java application to use the system cacerts instead of the JRE/JDK's cacerts. There is the general problem that many users are behind a corporate firewall and see traffic in which their firewall inserts content based on the corporate certificates and those certificates are often rooted on certificates not in the JRE/JDK but are available in the Windows system root certificates. So we have a situation where no matter what we do, someone will not be happy. Therefore we hope to eventually have a solution to support a store that combines these two so that everyone is happy. It's somewhat easier to remove the bad magic than it is for users to add what's for them the good magic, so we're kind of torn.

But, if you add the right certificate to your system root the problem will also go away forever. Don't assume the certificate you see on the web page is the same one being used when jgit is talking to a git repository...

merks avatar Jul 30 '25 10:07 merks

I agree that using the system store is the most likely best approach here. Some companies by intend disallow some certificates to be trusted and using the java defaults might be seen as a way to circumvent these rules.

So the more interesting question would be why your system does not seem to trust the host you are about to be reaching?

laeubi avatar Jul 30 '25 10:07 laeubi

I agree that using the system store is the most likely best approach here. Some companies by intend disallow some certificates to be trusted and using the java defaults might be seen as a way to circumvent these rules.

So the more interesting question would be why your system does not seem to trust the host you are about to be reaching?

I will try to check this in about 2 weeks 🏝️

markusschaefer avatar Jul 30 '25 10:07 markusschaefer

I agree that using the system store is the most likely best approach here. Some companies by intend disallow some certificates to be trusted and using the java defaults might be seen as a way to circumvent these rules.

So the more interesting question would be why your system does not seem to trust the host you are about to be reaching?

We have been analyzing this issue internally and with Microsoft, as some our customers also ran into issues with javax.net.ssl.trustStoreType=Windows-ROOT.

Sadly, there is some Windows magic involved here.

Here is the gist:

  • Windows-ROOT is actually a view on several physical/logical stores: Image

  • It essentially comprises

    1. Trusted Root Certification Authorities (marked in blue) This is where enterprises store the internal / custom CA roots (used for internal services and/or in SSL inspection scenarios). This is what we are typically interested in and why Windows-ROOT is used in the first place.
    2. Third-Party Root Certification Authorities (marked in green) This is the publicly trusted list maintained by Microsoft: https://learn.microsoft.com/en-us/security/trusted-root/participants-list containing all the default certificates such as VeriSign / Let's Encrypt / ...
  • The problem is with ii): This list is NOT necessarily pre-populated on the client machines. Instead, it is fetched implicitly whenever a TLS request is made using Microsoft's SChannel TLS library.

    Statement from MS:

    The automatic installation of root certificates in Windows is governed by the Microsoft Trusted Root Certificate Program and the Automatic Root Certificates Update mechanism. This mechanism is on-demand—meaning the root certificate is only downloaded and installed when a certificate chain validation process encounters a missing trusted root that is listed in Microsoft’s Certificate Trust List (CTL). However, this behavior depends on several conditions:

    • The application must use the Windows CryptoAPI (SChannel) for TLS validation. If the app uses its own certificate validation logic (like Java, OpenSSL, or custom libraries), it may not trigger the Windows mechanism.
    • The system must be online and able to reach Windows Update. If the machine is offline or behind a proxy/firewall that blocks access to Windows Update, the root certificate will not be downloaded.
    • The certificate must be part of the Microsoft CTL.

    This can be seen e.g. via

    • deleting the DigiCert Global Root G2 certificate from the list
    • then running, e.g. in PowerShell: Invoke-WebRequest "https://eclipse.org"
    • the certificate re-appears in the list
  • The problem is that when using the JDK's TLS implementation - JSSE - this automatic fetching of TLS certifiactes does not happen. Instead, when the SSLContext is created, it simply reads a static list of CAs once, and uses that list to validate trust going forward.

  • That javax.net.ssl.trustStoreType=Windows-ROOT works simply means that something else (using SChannel) has previously loaded the certificate and Eclipse can see it.

  • When new root certificates are added and Eclipse/JDK is the first to attempt to open a connection to such a server, it will fail. So this can be a ticking time bomb and we will run into more of these issues in the future.

Workaround:

You can load the full Third-Party Root Certification Authorities list via the following command:

CertUtil -VerifyCTL AuthRoot

Suggested Solution:

Do what the browsers also do (https://learn.microsoft.com/en-us/deployedge/microsoft-edge-security-cert-verification) and build the union of

  • an included trust store (the JVM truststore)
  • the Windows-ROOT trust store

There is no out-of-the-box solution that would allow one to combine multiple javax.net.ssl.trustStores, so this would need to be done in a custom implementation early-on after Eclipse startup which builds a union trust store and then uses javax.net.ssl.SSLContext.setDefault(SSLContext).

See also the discussion here: https://github.com/eclipse-packaging/packages/pull/224#issuecomment-2627576123

sratz avatar Aug 12 '25 09:08 sratz

Does your Windows certificate store contain the root/intermediate certificates? Press Win + R and enter certmgr.msc to check.

Yes, root and intermediate Certificate are in the Windows Store, but as @sratz mentioned in different folders.

markusschaefer avatar Aug 18 '25 06:08 markusschaefer

Here is a concrete proposal of what to do:

  • Somewhere early in the Eclipse startup phase, build an SSLContext as follows:

  • TrustManager:

    • If javax.net.ssl.trustStore* properties are set -> respect this, do not interfere by adding additional trust stores
    • else -> build a TrustManager that combines default (JVM) + OS trust stores
  • KeyManager: Do not change behavior, i.e. do not attempt to load any private keys from the OS trust store by default. Instead, only consider the javax.net.ssl.keyStore* properties if configured by the user just like the JVM default.

  • Set this SSLContext as default.

  • In the EPP packages, revert https://github.com/eclipse-packaging/packages/pull/224, so that the properties are not set, and we make use of the merged store.

We have had an internal implementation running that does this in our product that we would contribute.

@merks: Do you know what a good place for somewhere early would be?

sratz avatar Oct 24 '25 08:10 sratz

Equinox launcher?

iloveeclipse avatar Oct 24 '25 08:10 iloveeclipse

Certainly you cannot get much earlier than org.eclipse.equinox.launcher.Main.run(String[]). I'm not sure if you'd need to consider which properties might be set in the config.ini such that processing should maybe happen after that. Maybe it could be done right before org.eclipse.equinox.launcher.Main.invokeFramework(String[], URL[]) is called.

merks avatar Oct 24 '25 08:10 merks

For the technical side, you can create a system.bundle fragment and then hook into the framework startup procedure see for example https://equinox.eclipseprojects.io/articles/Adaptor_Hooks.html

We also have https://help.eclipse.org/latest/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fextension-points%2Forg_eclipse_ui_startup.html

laeubi avatar Oct 24 '25 09:10 laeubi

Certainly you cannot get much earlier than org.eclipse.equinox.launcher.Main.run(String[]). I'm not sure if you'd need to consider which properties might be set in the config.ini such that processing should maybe happen after that. Maybe it could be done right before org.eclipse.equinox.launcher.Main.invokeFramework(String[], URL[]) is called.

This looks like the right place.

For the technical side, you can create a system.bundle fragment and then hook into the framework startup procedure see for example equinox.eclipseprojects.io/articles/Adaptor_Hooks.html

This sounds unnecessarily complicated. The launcher seems to be fine.

We also have help.eclipse.org/latest/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fextension-points%2Forg_eclipse_ui_startup.html

IStartup is called too late and e.g. the P2 updater would have already been initialized with the old (default) SSLContext.

sratz avatar Oct 24 '25 10:10 sratz

This sounds unnecessarily complicated. The launcher seems to be fine.

But then only works when using the launcher that says

This class exists only for the purpose of launching Eclipse from the command line. To launch Eclipse programmatically, use org.eclipse.core.runtime.adaptor.EclipseStarter.

So don't expect it to kick in everywhere but might works "good enough" for the intended use.

laeubi avatar Oct 24 '25 10:10 laeubi

@sratz

So maybe it's better to put the logic in EclipseStarter where it's more universally available?

Image

That still allows you do insert the logic before much of anything else happens and it's then available everywhere.

merks avatar Oct 27 '25 11:10 merks

So maybe it's better to put the logic in EclipseStarter where it's more universally available?

Yes, I think that makes sense.

Just need to think about how to re-structure the tests, which currently only work like that because org.eclipse.equinox.launcher.tests is a fragment to org.eclipse.equinox.launcher.

org.eclipse.osgi.test is stand-alone. But org.eclipse.osgi already has some tests defined inside the main bundle, so maybe we can make use of tycho-surefire:plugin-test here as well.

sratz avatar Oct 27 '25 12:10 sratz

Hmm, org.eclipse.osgi is still on BREE 1.8 :(

sratz avatar Oct 27 '25 14:10 sratz

I asked @tjwatson to share his insights, advice, and concerns. Let's hold off doing more work until he has some time.

merks avatar Oct 27 '25 15:10 merks

I asked @tjwatson to share his insights, advice, and concerns

👍

Let's hold off doing more work until he has some time.

Turned out not to be that complicated after all, org.eclipse.osgi already had everything set up incl. Mockito so I just had to refactor the code back to JUnit 4 / Java 1.8 compatibility:

https://github.com/eclipse-equinox/equinox/pull/1176/commits/b1eeb1b57751f8deb1760cd6befd2a9ad33ea292

Both places work equally well.

sratz avatar Oct 27 '25 15:10 sratz

I asked @tjwatson to share his insights, advice, and concerns. Let's hold off doing more work until he has some time.

I'm sure you saw my response. But just in case, it is here: https://github.com/eclipse-equinox/equinox/pull/1176#discussion_r2466120936

tjwatson avatar Oct 27 '25 15:10 tjwatson

I asked @tjwatson to share his insights, advice, and concerns. Let's hold off doing more work until he has some time.

I'm sure you saw my response. But just in case, it is here: eclipse-equinox/equinox#1176 (comment)

Yes, saw it. I am wondering: Instead of adding another system bundle fragment, maybe just moving the initialization to

org.eclipse.core.runtime / org.eclipse.core.internal.runtime.InternalPlatform.start(BundleContext)

and lowering that bundle's startLevel in https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/blob/0b027b3b8606a314ae3f124ca06bb2c2c67150d1/eclipse.platform.releng.tychoeclipsebuilder/eclipse.platform.repository/platform.product#L194 would be good enough?

sratz avatar Nov 03 '25 12:11 sratz

@sratz the platform product is really only more used in testing I think, and other products launches might override that. But we already have in PDE recommended start levels and we can adjust the EPPs.

You can even have a p2.inf that sets the startlevel on install!

Beside that I wonder what other bundles are really starting and creating request before the platform starts... in any case I think if we have this code then it becomes only a configuration issue.

laeubi avatar Nov 03 '25 12:11 laeubi

@sratz the platform product is really only more used in testing I think, and other products launches might override that. But we already have in PDE recommended start levels and we can adjust the EPPs.

Yes, this was just an example.

You can even have a p2.inf that sets the startlevel on install!

Beside that I wonder what other bundles are really starting and creating request before the platform starts... in any case I think if we have this code then it becomes only a configuration issue.

Yes, so the consensus is the following, correct?

  • We move the code from equinox to platform org.eclipse.core.internal.runtime.InternalPlatform.start(BundleContext)
  • Most likely this will already cover all cases, as org.eclipse.core.runtime should be a dependency of practically any other bundle that would need an SSLContext.
  • If we find corner cases, we can fine-tune later.

sratz avatar Nov 03 '25 14:11 sratz

Yes, this sounds generally good to me.

merks avatar Nov 03 '25 14:11 merks

For me it looks like a good way to get started. If we later decide to move it somewhere else or that we missing a particular case its easier to build up on this.

laeubi avatar Nov 03 '25 14:11 laeubi

This sounds like a good direction to me also.

tjwatson avatar Nov 03 '25 14:11 tjwatson

#2241 is merged. The next steps would be to replace the system properties accordingly in the various product defintions:

  • remove windows-specififc <vmArgsWin>-Djavax.net.ssl.trustStoreType=Windows-ROOT -Djavax.net.ssl.trustStore=NONE</vmArgsWin>
  • add -Declipse.platform.mergeTrust=true to platform-independent <vmArgs>

Places:

  • platform SDK: https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/blob/c424fdacc335fd8ed27b63306ec9a5d52febfd23/eclipse.platform.releng.tychoeclipsebuilder/eclipse.platform.repository/sdk.product#L16
  • platform runtime binary: https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/blob/c424fdacc335fd8ed27b63306ec9a5d52febfd23/eclipse.platform.releng.tychoeclipsebuilder/eclipse.platform.repository/platform.product#L16
  • EPPs
  • Eclipse installer

sratz avatar Nov 11 '25 14:11 sratz

@sratz

Assuming there is a good I-build tomorrow, I'll be able to build Oomph and the installers based on that build. Then I'll be able to make these changes to both the installer and in the SDK installation it creates from that new I-build to test that this new stuff works in both places on my Windows 11 machine. If those tests are good, then I will update the SDK products accordingly.

OK?

merks avatar Nov 11 '25 14:11 merks