0

I'm running H2 tcp server.
Java (Servlet 3.1 webapp in Tomcat 8.5):

org.h2.tools.Server tcp = Server.createTcpServer("-ifExists", "-tcpSSL", "-tcpPort", "8089", "-tcpAllowOthers").start();

I'm trying to access it on a remote machine via the H2 Shell on command line:

$ java -cp h2-2.1.210.jar org.h2.tools.Shell

After opening port 8089 on router, tcp connections work:

jdbc:h2:tcp://mySite.net:8089/file:/Users/xyz/tomcat/webapps/ROOT/WEB-INF/database/myh2db;IFEXISTS=TRUE;MODE=MySQL;CASE_INSENSITIVE_IDENTIFIERS=TRUE
jdbc:h2:tcp://mySite.net:8089//Users/xyz/tomcat/webapps/ROOT/WEB-INF/database/myh2db;IFEXISTS=TRUE;MODE=MySQL;CASE_INSENSITIVE_IDENTIFIERS=TRUE

But any attempt at using ssl fails with:

SQL Exception: Connection is broken: "javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

At first I thought it was a Tomcat issue with port 8089, but it doesn't look to be. The router routes incoming requests at its port 80 to Tomcat at port 8080. Tomcat redirects any HTTPS requests Tomcat receives to the router's port 443, and the router routes incoming requests it receives at its port 443 to Tomcat's port 8443.

For H2 tcp SSL (running in Tomcat, bound to port 8089), I have set up the router to route incoming requests at port 8089 to port 8089. So now tcp works, but ssl fails with the javax.net.ssl.SSLHandshakeException.

Tomcat server.xml:

<Connector port="8080"
           protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="443">
   <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
</Connector>
.
.
.
<Connector port="8443" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
               sslImplementationName="org.apache.tomcat.util.net.jsse.JSSEImplementation"
               scheme="https" secure="true"
               maxThreads="150" SSLEnabled="true" >
     <SSLHostConfig certificateVerification="none" sslProtocol="TLS">
         <Certificate certificateKeystoreFile="conf/tomcat.keystore"
                      type="RSA"
                      certificateKeystorePassword="<password>" />
     </SSLHostConfig>
</Connector>

Any idea what the problem is? How to fix it?
Thanks.

UPDATE 2022-03-28
Thanks to Evgenij's pointer, I did some more research.
I used openssl (on macOS) to create new self-signed certificate. I have the following files from this process:

  • server.key (private key)
  • server.crt (the certificate)
  • server.pem (concatenation of above 2; used to make keystore.pkcs12; otherwise looks useless)
  • keystore.pkcs12

I then set six java system properties (keyStore and trustStore for the TcpServer (for anonymous TLS connection from client side)):

  • javax.net.ssl.keyStore=/path/to/keystore.pkcs12
  • javax.net.ssl.keyStorePassword=<password>
  • javax.net.ssl.keyStoreType=pkcs12
  • javax.net.ssl.trustStore=/path/to/keystore.pkcs12
  • javax.net.ssl.trustStorePassword=<password>
  • javax.net.ssl.trustStoreType=pkcs12

Now when I start up Tomcat, I get:

Error starting H2 server
org.h2.jdbc.JdbcSQLNonTransientException: IO Exception: "java.net.SocketException: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: sun.security.ssl.SSLContextImpl$DefaultSSLContext)"; "port: 8089 ssl: true" [90031-210]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:573)
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:496)
    at org.h2.message.DbException.get(DbException.java:216)
    at org.h2.message.DbException.convertIOException(DbException.java:461)
    at org.h2.util.NetUtils.createServerSocketTry(NetUtils.java:214)
    at org.h2.util.NetUtils.createServerSocket(NetUtils.java:177)
    at org.h2.server.TcpServer.start(TcpServer.java:236)
    at org.h2.tools.Server.start(Server.java:521)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4763)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5232)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:753)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:727)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:695)
    at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1016)
    at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1903)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.net.SocketException: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: sun.security.ssl.SSLContextImpl$DefaultSSLContext)
    at java.base/javax.net.ssl.DefaultSSLServerSocketFactory.throwException(SSLServerSocketFactory.java:175)
    at java.base/javax.net.ssl.DefaultSSLServerSocketFactory.createServerSocket(SSLServerSocketFactory.java:188)
    at org.h2.security.CipherFactory.createServerSocket(CipherFactory.java:152)
    at org.h2.util.NetUtils.createServerSocketTry(NetUtils.java:204)
    ... 17 common frames omitted
Caused by: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: sun.security.ssl.SSLContextImpl$DefaultSSLContext)
    at java.base/java.security.Provider$Service.newInstance(Provider.java:1901)
    at java.base/sun.security.jca.GetInstance.getInstance(GetInstance.java:236)
    at java.base/sun.security.jca.GetInstance.getInstance(GetInstance.java:164)
    at java.base/javax.net.ssl.SSLContext.getInstance(SSLContext.java:168)
    at java.base/javax.net.ssl.SSLContext.getDefault(SSLContext.java:99)
    at java.base/javax.net.ssl.SSLServerSocketFactory.getDefault(SSLServerSocketFactory.java:114)
    at org.h2.security.CipherFactory.createServerSocket(CipherFactory.java:149)
    ... 18 common frames omitted
Caused by: java.security.KeyStoreException: problem accessing trust store
    at java.base/sun.security.ssl.TrustManagerFactoryImpl.engineInit(TrustManagerFactoryImpl.java:73)
    at java.base/javax.net.ssl.TrustManagerFactory.init(TrustManagerFactory.java:278)
    at java.base/sun.security.ssl.SSLContextImpl$DefaultManagersHolder.getTrustManagers(SSLContextImpl.java:1053)
    at java.base/sun.security.ssl.SSLContextImpl$DefaultManagersHolder.<clinit>(SSLContextImpl.java:1023)
    at java.base/sun.security.ssl.SSLContextImpl$DefaultSSLContext.<init>(SSLContextImpl.java:1198)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
    at java.base/java.security.Provider.newInstanceUtil(Provider.java:154)
    at java.base/java.security.Provider$Service.newInstance(Provider.java:1894)
    at java.base/sun.security.jca.GetInstance.getInstance(GetInstance.java:236)
    at java.base/sun.security.jca.GetInstance.getInstance(GetInstance.java:164)
    at java.base/javax.net.ssl.SSLContext.getInstance(SSLContext.java:168)
    at java.base/javax.net.ssl.SSLContext.getDefault(SSLContext.java:99)
    at java.base/javax.net.ssl.SSLServerSocketFactory.getDefault(SSLServerSocketFactory.java:114)
    at org.h2.security.CipherFactory.createServerSocket(CipherFactory.java:149)
    at org.h2.util.NetUtils.createServerSocketTry(NetUtils.java:204)
    at org.h2.util.NetUtils.createServerSocket(NetUtils.java:174)
    ... 16 common frames omitted
Caused by: java.io.IOException: toDerInputStream rejects tag type 45
    at java.base/sun.security.util.DerValue.toDerInputStream(DerValue.java:858)
    at java.base/sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:1974)
    at java.base/sun.security.util.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:222)
    at java.base/java.security.KeyStore.load(KeyStore.java:1479)
    at java.base/sun.security.ssl.TrustStoreManager$TrustAnchorManager.loadKeyStore(TrustStoreManager.java:365)
    at java.base/sun.security.ssl.TrustStoreManager$TrustAnchorManager.getTrustedCerts(TrustStoreManager.java:313)
    at java.base/sun.security.ssl.TrustStoreManager.getTrustedCerts(TrustStoreManager.java:55)
    at java.base/sun.security.ssl.TrustManagerFactoryImpl.engineInit(TrustManagerFactoryImpl.java:49)
    ... 34 common frames omitted

UPDATE 2022-03-29
@dave_thompson_085, to your comment re toDerInputStream rejects tag type 45, here are the first few lines of the od -tx1 output on the keystore.pkcs12:

$ od -tx1 keystore.pkcs12 
0000000    30  82  09  e9  02  01  03  30  82  09  af  06  09  2a  86  48
0000020    86  f7  0d  01  07  01  a0  82  09  a0  04  82  09  9c  30  82
0000040    09  98  30  82  04  4f  06  09  2a  86  48  86  f7  0d  01  07
0000060    06  a0  82  04  40  30  82  04  3c  02  01  00  30  82  04  35
0000100    06  09  2a  86  48  86  f7  0d  01  07  01  30  1c  06  0a  2a
0000120    86  48  86  f7  0d  01  0c  01  06  30  0e  04  08  7c  2f  ef
0000140    9d  c7  1d  83  a8  02  02  08  00  80  82  04  08  5a  ea  2b
0000160    57  b0  b6  ba  58  4c  b5  45  7c  48  28  e0  5c  94  50  aa
0000200    b0  9b  5d  33  b1  94  0b  eb  5e  85  0b  58  83  3c  f0  86
0000220    24  d7  b9  53  12  fa  ab  8b  b1  fe  b4  2b  5c  b7  2c  0a
...

Do I look for 2d (the hex value for the hyphen character)? I don't see any there. The first time any 2d appears is at offset 320:

$ od -tx1 keystore.pkcs12 | grep 2d
0000320    76  6c  82  cb  b4  2f  b2  78  a3  d3  8d  2d  53  36  5e  65
...

Also: I thought I'd try importing the cert (pem file) into $JAVA_HOME/lib/security/cacerts instead of setting those six Java system properties. So I downloaded the pem file via the browser (clicking on the padlock icon in the URL locator box), and I imported it:

$ keytool -importcert -file cert.pem -alias testcert -cacerts

I then listed the cacerts file and confirmed cert.pem was imported into it. But I also noticed that the Keystore type: JKS:

$ keytool -list -cacerts
Enter keystore password:  
Keystore type: JKS
Keystore provider: SUN

Your keystore contains 214 entries
...

Is that JKS type in conflict with the openssl origin of the cert.pem file, which is pkcs12? Should the pem file have been converted first into JKS format before importing into cacerts?
I tested the H2 TcpServer over ssl, and it still throws the same exception as before:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

UPDATE 2022-03-29, #2
I did over again the truststore and keystore per the instructions given at Oracle using Java keytool: Creating a Keystore to Use with JSSE. And I reset all six JVM system properties. I started up Tomcat running the TcpServer, then I tried again connecting to the server remotely via tcp over ssl. Getting the same exceptions.

UPDATE 2022-03-30
I give up getting anonymous TLS connection to work. I am able to connect non-anonymously from the client to the server when I have the client also have the six JVM system properties point to the client's own set of truststore and keystore (client's own credential) when starting the H2 Shell:

$ java -Djavax.net.ssl.keyStore=/path/to/keystore -Djavax.net.ssl.keyStorePassword=<password> -Djavax.net.ssl.keyStoreType=PKCS12 -Djavax.net.ssl.trustStore=/path/to/truststore -Djavax.net.ssl.trustStorePassword=<password> -Djavax.net.ssl.trustStoreType=PKCS12 -cp h2-2.1.210.jar org.h2.tools.Shell

What I did learn was that the truststore must have its own file; it cannot share the same file as the keystore even though the files are equivalent to each other. Sharing the same file leads to:

SEVERE [main] org.apache.catalina.core.StandardService.
initInternal Failed to initialize connector [Connector[org.apache.coyote.http11.
Http11Nio2Protocol-8443]]
...
Caused by: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty

The latest set of truststore and keystore files were all generated via Java keytool. Since I am using Java 11, all files are PKCS12. I have had no success with the OpenSSL files (also PKCS12) mentioned at the very beginning.

I have not been able to replicate the toDerInputStream rejects tag type 45 exception.

niemand
  • 43
  • 6
  • Did you read the documentation? https://h2database.com/html/advanced.html#tls_connections Configuration of Tomcat isn't related to configuration of H2 in any way. (Anonymous TLS doesn't work in new JVMs, so you need to configure the keystore explicitly.) – Evgenij Ryazanov Mar 26 '22 at 04:52
  • When did anonymous TLS stop working in new JVMs? How new? I can't find any official documentation. Are you referring to this: https://github.com/h2database/h2database/issues/1303 – niemand Mar 28 '22 at 22:49
  • Anonymous TLS *never* worked in any JVM I have ever used in 20+ years, unless you specifically enable anonymous cipher suites at both ends. But it doesn't have anything to do with this error. – user207421 Mar 28 '22 at 23:37
  • There is a trick to enable it through reflection and it is actually used by H2, but this trick does't work in Java 11 and later versions and I think it may not work in **some new** Java 8 VMs. Type of keystore and keys in it need to be supported by JVM and JDK has own `keytool` utility, it can be used to generate a self-signed certificate and store it in compatible way. – Evgenij Ryazanov Mar 29 '22 at 00:42
  • @user207421+ what is newish is that Java 11 (2018) and 8u261 (2020) up support TLS1.3, and _if you use_ 1.3 anonymous is no longer possible at all -- except in manual/nonresumption PSK, which Java does not (and did not) support. niemand: the last cause 'DerValue rejects tag 45' indicates your supposed pkcs12 contains a hyphen where it shouldn't, _possibly_ at the beginning, which would occur if you got the pkcs12 mixed up with or overwritten by one of the PEM files (which begin with 5 hyphens); please check `od -tx1` or similar on the beginning of your supposed pkcs12. – dave_thompson_085 Mar 29 '22 at 07:50
  • @dave_thompson_085 I updated with the result of the `od -tx1`. `2d` (hyphen) first appears at offset 320. – niemand Mar 29 '22 at 16:04
  • I just noticed you said you set `javax.net.ssl.keyStore*` for the server but `javax.net.ssl.trustStore*` for the client. What _is_ set for `trustStore*` _on the server_? That's where the problem is. If that's something different than the pkcs12 you just created, you need to look at that different something. – dave_thompson_085 Mar 30 '22 at 06:21
  • @dave_thompson_085 I need to correct that statement. They were actually all set for the server. I had intended for the client to connect anonymously over TCP SSL (i.e., no credentials coming from client side), but that is apparently a lost cause. I can connect over TCP SSL when I include the properties using `-D` (and the keystore and truststore files) when starting the H2 Shell on command line on client side. See my update with full details. – niemand Mar 30 '22 at 10:00
  • I concur a PKCS12 from OpenSSL won't actually work as Java truststore (i.e. trustAnchors empty). You _can_ create a PKCS12 with keytool usable as both keystore and truststore, _if_ you create both privateKey and trustedCert entries; OpenSSL doesn't support that mixture _or_ the special bag attribute Java needs for trustedCert. But Java _should_ be able _read_ OpenSSL PKCS12. OTOH if you can no longer replicate it I don't think there's anything to do -- and even if you can, this now looks arcane enough we (I) would probably need a dummy or test key you can expose publicly. Cheers. – dave_thompson_085 Mar 30 '22 at 17:42

0 Answers0