bc-java icon indicating copy to clipboard operation
bc-java copied to clipboard

Java 5 TLS 1.2 for jdbc connection

Open abhissha opened this issue 1 year ago • 7 comments

I am trying to use Bouncy castle in Java 5 to support TLS 1.2 for JDBC connection. Based on the documentation, I can't figure out how to achieve this, since examples seems to be for Java 8 and above. Are there any working examples or documentation that I can follow to achieve my goal?

abhissha avatar Nov 07 '24 13:11 abhissha

You can use the "BCJSSE" provider (org.bouncycastle.jsse.provider.BouncyCastleJsseProvider) in Java 5. Most JSSE features that were added in later APIs can also be accessed via BCJSSE-specific extensions.

Refer to the BCJSSE tests for examples of usage; I think there is very little dependency on Java version in those tests.

peterdettman avatar Nov 08 '24 04:11 peterdettman

Based on the test cases, I tried below code and below is not working. Any help to direct in right direction would be appreciated.

I am trying to get this working on SQL JDBC driver and Java 5 based application.

The error I am getting is below: ov 19, 2024 1:23:02 PM org.bouncycastle.jsse.provider.PropertyUtils getStringSecurityProperty WARNING: String security property [jdk.tls.disabledAlgorithms] defaulted to: SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, MD5withRSA, DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, ECDH Nov 19, 2024 1:23:02 PM org.bouncycastle.jsse.provider.PropertyUtils getStringSecurityProperty WARNING: String security property [jdk.certpath.disabledAlgorithms] defaulted to: MD2, MD5, SHA1 jdkCA & usage TLSServer, RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224, SHA1 usage SignedJAR & denyAfter 2019-01-01 Nov 19, 2024 1:23:02 PM org.bouncycastle.jsse.provider.DisabledAlgorithmConstraints create WARNING: Ignoring unsupported entry in 'jdk.certpath.disabledAlgorithms': SHA1 jdkCA & usage TLSServer Nov 19, 2024 1:23:02 PM org.bouncycastle.jsse.provider.DisabledAlgorithmConstraints create WARNING: Ignoring unsupported entry in 'jdk.certpath.disabledAlgorithms': SHA1 usage SignedJAR & denyAfter 2019-01-01 Nov 19, 2024 1:23:02 PM org.bouncycastle.jsse.provider.PropertyUtils getStringSystemProperty INFO: Found string system property [java.home]: C:\data\myinstalls\java\jdk-1.5.0_22\jre Nov 19, 2024 1:23:02 PM org.bouncycastle.jsse.provider.PropertyUtils getStringSystemProperty INFO: Found string system property [java.home]: C:\data\myinstalls\java\jdk-1.5.0_22\jre

com.microsoft.sqlserver.jdbc.SQLServerException: The driver could not establish a secure connection to SQL Server by using Secure Sockets Layer (SSL) encryption. Error: "unable to create JcaTlsCrypto: DEFAULT SecureRandom not available".

I tried couple of different ways: dbConnectionString is just a connection string to database.

  1. Without making any code changes, added BouncyCastleJsseProvider as a default Provider ``` @Test public void testDBServerWithTLSv12AsOnlyOptionUsingBouncyCastleJsseProviderDirectly() throws Exception { BouncyCastleJsseProvider provider = new BouncyCastleJsseProvider(); Security.insertProviderAt(provider,1); // Connect to the database executeSQL(dbConnectionString); }
2. Initializing TLSv1.2 as SSL Context. Based on https://docs.oracle.com/en/java/javase/15/security/transport-layer-security-tls-protocol-overview.html#GUID-D04EF7C1-B1D4-4611-9896-A7B5573CBEED, oracle talks about TLS V1.2 works without any cert/keyexchange, so set the keymanager/trustmanager to null. I also tried below with secureRandom as null with same issue.

@Test public void testDBServerWithTLSv12AsOnlyOptionUsingBouncyCastleJsseProviderAndSSLContextWithDefaultSecureRandom() throws Exception { BouncyCastleJsseProvider provider = new BouncyCastleJsseProvider(); Security.insertProviderAt(provider,1); SSLContext context = SSLContext.getInstance("TLSv1.2", provider.getName()); SecureRandom secureRandom = new SecureRandom(); CryptoServicesRegistrar.setSecureRandom(secureRandom); context.init(null,null, secureRandom); // Connect to the database executeSQL(dbConnectionString); }


executeSQL is pretty straight forward method, that just calls db with a select statement

private static void executeSQL(String connectionUrl) throws Exception { try { Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); Connection connection = DriverManager.getConnection(connectionUrl); Statement statement = connection.createStatement();

        // Execute a SELECT SQL statement
        String selectSql = "SELECT Id=1";
        ResultSet resultSet = statement.executeQuery(selectSql);

        // Print results from the SELECT statement
        while (resultSet.next()) {
            System.out.println(resultSet.getString("Id"));
        }
    }
    catch(Exception e) {
        throw e;
    }
}

abhissha avatar Nov 19 '24 18:11 abhissha

I guess there is some issue trying to create a SecureRandom of type "DEFAULT" (the exception comes from here). I guess you are not using the BouncyCastle crypto provider ("BC") i.e. BouncyCastleProvider, which should have "DEFAULT".

You could create your own subclass of JcaTlsCryptoProvider so that you can override the create(SecureRandom) method (e.g. replace SecureRandom.getInstance("DEFAULT", ...) with new SecureRandom().

Then use the BouncyCastleJsseProvider(boolean, JcaTlsCryptoProvider) constructor to create the provider instance, passing your custom JcaTlsCryptoProvider subclass.

peterdettman avatar Nov 20 '24 12:11 peterdettman

Thanks for the suggestion. I tried using BouncyCastleProvider directly and as well as BouncyCastleJsseProvider. Based on your suggestion, added a new JcaTlsCryptoProvider and ran the code. Now I am running into a different issue

When I use BouncyCastleProvider-> Error is com.microsoft.sqlserver.jdbc.SQLServerException: Reason: Login failed due to client TLS version being less than minimal TLS version allowed by the server.

at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:196)
at com.microsoft.sqlserver.jdbc.TDSTokenHandler.onEOF(tdsparser.java:246)
at com.microsoft.sqlserver.jdbc.TDSParser.parse(tdsparser.java:83)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.sendLogon(SQLServerConnection.java:2532)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.logon(SQLServerConnection.java:1929)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.access$000(SQLServerConnection.java:41)
at com.microsoft.sqlserver.jdbc.SQLServerConnection$LogonCommand.doExecute(SQLServerConnection.java:1917)
at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:4026)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:1416)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectHelper(SQLServerConnection.java:1061)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.login(SQLServerConnection.java:833)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connect(SQLServerConnection.java:716)
at com.microsoft.sqlserver.jdbc.SQLServerDriver.connect(SQLServerDriver.java:841)
at java.sql.DriverManager.getConnection(DriverManager.java:525)
at java.sql.DriverManager.getConnection(DriverManager.java:193)
at BouncyCastleProviderTest.executeSQL(BouncyCastleProviderTest.java:103)
at BouncyCastleProviderTest.testDBServerWithTLSv12AsOnlyOptionUsingBouncyCastleProviderDirectly(BouncyCastleProviderTest.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:592)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

When I use BouncyCastleJsseProvider, error I get is below Nov 22, 2024 10:10:20 AM org.bouncycastle.jsse.provider.PropertyUtils getStringSecurityProperty WARNING: String security property [jdk.tls.disabledAlgorithms] defaulted to: SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, MD5withRSA, DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, ECDH Nov 22, 2024 10:10:20 AM org.bouncycastle.jsse.provider.PropertyUtils getStringSecurityProperty WARNING: String security property [jdk.certpath.disabledAlgorithms] defaulted to: MD2, MD5, SHA1 jdkCA & usage TLSServer, RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224, SHA1 usage SignedJAR & denyAfter 2019-01-01 Nov 22, 2024 10:10:20 AM org.bouncycastle.jsse.provider.DisabledAlgorithmConstraints create WARNING: Ignoring unsupported entry in 'jdk.certpath.disabledAlgorithms': SHA1 jdkCA & usage TLSServer Nov 22, 2024 10:10:20 AM org.bouncycastle.jsse.provider.DisabledAlgorithmConstraints create WARNING: Ignoring unsupported entry in 'jdk.certpath.disabledAlgorithms': SHA1 usage SignedJAR & denyAfter 2019-01-01

Nov 22, 2024 10:10:20 AM org.bouncycastle.jsse.provider.ProvTlsClient notifyConnectionClosed INFO: [client #1 @18bbc5a] disconnected from [dbhostname]:1433

com.microsoft.sqlserver.jdbc.SQLServerException: The driver could not establish a secure connection to SQL Server by using Secure Sockets Layer (SSL) encryption. Error: "No usable protocols enabled".

at com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:1368)
at com.microsoft.sqlserver.jdbc.TDSChannel.enableSSL(IOBuffer.java:1412)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectHelper(SQLServerConnection.java:1058)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.login(SQLServerConnection.java:833)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.connect(SQLServerConnection.java:716)
at com.microsoft.sqlserver.jdbc.SQLServerDriver.connect(SQLServerDriver.java:841)
at java.sql.DriverManager.getConnection(DriverManager.java:525)
at java.sql.DriverManager.getConnection(DriverManager.java:193)
at BouncyCastleJsseProviderTest.executeSQL(BouncyCastleJsseProviderTest.java:106)
at BouncyCastleJsseProviderTest.testDBServerWithTLSv12AsOnlyOptionUsingBouncyCastleJsseProviderDirectly(BouncyCastleJsseProviderTest.java:66)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:592)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Caused by: java.lang.IllegalStateException: No usable protocols enabled at org.bouncycastle.jsse.provider.ProvSSLContextSpi.getActiveProtocolVersions(Unknown Source) at org.bouncycastle.jsse.provider.ProvTlsClient.getSupportedVersions(Unknown Source) at org.bouncycastle.tls.AbstractTlsClient.init(Unknown Source) at org.bouncycastle.tls.TlsClientProtocol.connect(Unknown Source) at org.bouncycastle.jsse.provider.ProvSSLSocketWrap.startHandshake(Unknown Source) at org.bouncycastle.jsse.provider.ProvSSLSocketWrap.startHandshake(Unknown Source) at com.microsoft.sqlserver.jdbc.TDSChannel.enableSSL(IOBuffer.java:1379) ... 31 more

I tried to run this code against Java 8 and had no issue connecting to DB using TLSv1.2. The issue seems to be specific to Java 5.

Bouncy Castle Provider Tests I am running private BouncyCastleProvider getAndSetProvider() { BouncyCastleProvider provider; provider = new BouncyCastleProvider(); Security.insertProviderAt(provider,1);

    return provider;
}

/*
    This test is by inserting BouncyCastle JSEE provider by default without any changes
 */
@Test
public void testDBServerWithTLSv12AsOnlyOptionUsingBouncyCastleProviderDirectly() throws Exception {
    getAndSetProvider();
    // Connect to the database
    executeSQL(dbConnectionString);
}

Bouncy Castle Jsse Provider Tests I am running private BouncyCastleJsseProvider getAndSetProvider() { BouncyCastleJsseProvider provider; provider = new BouncyCastleJsseProvider(false, new CustomJcaTlsCryptoProvider()); Security.insertProviderAt(provider,1); return provider; }

/*
    This test is by inserting BouncyCastle JSEE provider by default without any changes
 */
@Test
public void testDBServerWithTLSv12AsOnlyOptionUsingBouncyCastleJsseProviderDirectly() throws Exception {
    getAndSetProvider();
    // Connect to the database
    executeSQL(dbConnectionString);
}

abhissha avatar Nov 22 '24 15:11 abhissha

In Java 5, on debugging further, what I found is MS SQL JDBC driver that's getting used is making below call -> SSLContext var21 = SSLContext.getInstance("SSLv3");, which is causing the BouncyCastle Provider to not pick up TLSv1.2. Since the MS SQL JDBC driver is old v2.0, is there any way in which BouncyCastle Provider can help to use TLSv1.2?

abhissha avatar Nov 26 '24 01:11 abhissha

@abissha Normally "jdk.tls.client.protocols" could be used to set the client protocols to support, but SSLContext.getInstance("SSLv3") will ignore that property; for historical reasons it will effectively try to use only TLSv1.

Your java.security settings apparently don't have a jdk.tls.disabledAlgorithms setting (I guess Java 1.5 hadn't introduced it yet), but the log shows we therefore default to "SSLv3, TLSv1, TLSv1.1, DTLSv1.0, (etc.)". So you can see that SSLv3, TLSv1 and TLSv1.1 will be disabled. You could try editing that property to allow TLSv1 in particular (given the getInstance call above).

I guess the server error about "minimal TLS version" is what happens when you successfully connect with TLSv1 (I assume using SunJSSE), so then TLSv1 would not be enough.

Perhaps there is a way to create the SSLContext yourself (or sometimes a way to override the SSLSocketFactory creation) and tell the JDBC driver to use yours instead of creating its own? Or is there some SSL configuration for the JDBC driver?

peterdettman avatar Dec 16 '24 09:12 peterdettman

@peterdettman I have added TLSV1.2 in my own SSLContext, and i get Exceptions.

  1. When I initialize SSLContext using this line --> sslContext.init(null,null, new SecureRandom()); I get below exception, and i cannot create Object of SSLSocketFactory until I initialize SSLContext.

Caused by: java.lang.SecurityException: Cannot set up certs for trusted CAs at javax.crypto.SunJCE_b.(DashoA12275) ... 34 more Caused by: java.lang.SecurityException: Jurisdiction policy files are not signed by trusted signers!

I have the java 5 specific local_policy and US_export_policy in jre/lib/security directory.

ghost avatar Dec 18 '24 12:12 ghost