BCJSSE, JDK5 and SSLSocketFactory.getDefault()

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

BCJSSE, JDK5 and SSLSocketFactory.getDefault()

Veit Guna
Hi.

I've succesfully integrated BCJSSE programmatically using JDK5 and
HttpsUrlConnection being able to finally connect to TLSv1.2 webservices.

Basically by creating the SSLContext using the BCJSSE provider and
initializing it using a TrustManagerFactory and KeyManagerFactory.
Then getting the SSLSocketFactory via context.getSSLSocketFactory(); and
setting it on the HttpsUrlConnection.

So far so good. Now I'm wondering, how to achieve the same result,
without having to touch any code :).
Since HttpsUrlConnection and e.g. Apache Commons HttpClient are using
SSLSocketFactory.getDefault(), I currently don't see a way to do this in
JDK5.

The BCJSSE documentation I found here:

https://downloads.bouncycastle.org/fips-java/BC-FJA-(D)TLSUserGuide-1.0.0.pdf

states, that getDefault() will only return the BCJSSE SSLSocketFactory,
if the Provider is registered before the Sun JSSE one in java.security.
I tried it, but it didn't work out. It still returns the Sun JSSE one.

Taking a look at the JDK5 SSLSocketFactory.getDefault() implementation
shows, that it tries to load a povider, specified via
'ssl.SocketFactory.provider' system property.
But this requires a no-args ctor, which the BC package private
ProvSSLSocketFactory doesn't provide. So this would fail if one would
try to override the default one.

Not specifying it via system property, defaults to
'com.sun.net.ssl.internal.ssl.SSLSocketFactoryImpl' which is then
instantiated and used.
Now I'm wondering, how SSLSockerFactory.getDefault(); should ever return
the BC one :D.

Could somebody please shed some light on this, how the mechanism
described in the doc works for JDK5?

All I could imagine would be, to create my own SSLSocketFactory Provider
implementation, setting up the BC SSLContext within that and returning
the BC SSLSocketFactory
from it. Then registering it via mentioned system property. Any other
ways to do it?

Cheers,
Veit







Reply | Threaded
Open this post in threaded view
|

Re: BCJSSE, JDK5 and SSLSocketFactory.getDefault()

Veit Guna
For other people maybe trying to get this done using JDK5 as well,
here's a small snippet that worked for me.

It's no beauty (no autocloseables since JDK5 etc.), but one should get
the idea. It's working for me, automatically hooking into a standard
HttpsUrlConnection.
Keep in mind that setting
'ssl.SocketFactory.provider=fancy.pants.BcSslSocketFactory' in
java.security is required in order to make this work.
If it fails with IllegalKeySize or similar, remember to have JCE strong
encryption policy files in place.

Thanks again for this great library!

--cut here--

package fancy.pants;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;

import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;

public class BcSslSocketFactory extends SSLSocketFactory {

    private static final String DEFAULT_PASSWORD = "changeit";

    private SSLContext sslContext;

    public BcSslSocketFactory() throws IOException, KeyStoreException,
NoSuchProviderException, NoSuchAlgorithmException, CertificateException,
UnrecoverableKeyException, KeyManagementException{

        String provider =
System.getProperty("javax.net.ssl.keyStoreProvider");
        String keystoreType =
System.getProperty("javax.net.ssl.keyStoreType", KeyStore.getDefaultType());

        KeyStore keystore = null;
        if (provider != null) {
            keystore = KeyStore.getInstance(keystoreType, provider);
        } else {
            keystore = KeyStore.getInstance(keystoreType);
        }

        String keystorePath =
System.getProperty("javax.net.ssl.trustStore",
System.getProperty("java.home") + "/lib/security/cacerts".replace('/',
File.separatorChar));
        String keystorePassword =
System.getProperty("javax.net.ssl.trustStorePassword", DEFAULT_PASSWORD);

        InputStream keystoreInputStream = null;
        try {
            keystoreInputStream = new FileInputStream(keystorePath);
            keystore.load(keystoreInputStream,
keystorePassword.toCharArray());
        } finally {
            if (keystoreInputStream != null) {
                keystoreInputStream.close();
            }
        }

        KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(keystore, keystorePassword.toCharArray());

        sslContext = SSLContext.getInstance("TLS", new
BouncyCastleJsseProvider());

        TrustManagerFactory trustMgrFact =
TrustManagerFactory.getInstance("PKIX", "BCJSSE");
        trustMgrFact.init(keystore);
       
        sslContext.init(null, trustMgrFact.getTrustManagers(), null);
    }

    @Override
    public Socket createSocket(Socket arg0, String arg1, int arg2,
boolean arg3) throws IOException {
        return sslContext.getSocketFactory().createSocket(arg0, arg1,
arg2, arg3);
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return sslContext.getSocketFactory().getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return sslContext.getSocketFactory().getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(String arg0, int arg1) throws
IOException, UnknownHostException {
        return sslContext.getSocketFactory().createSocket(arg0, arg1);
    }

    @Override
    public Socket createSocket(InetAddress arg0, int arg1) throws
IOException {
        return sslContext.getSocketFactory().createSocket(arg0, arg1);
    }

    @Override
    public Socket createSocket(String arg0, int arg1, InetAddress arg2,
int arg3) throws IOException, UnknownHostException {
        return sslContext.getSocketFactory().createSocket(arg0, arg1,
arg2, arg3);
    }

    @Override
    public Socket createSocket(InetAddress arg0, int arg1, InetAddress
arg2, int arg3) throws IOException {
        return sslContext.getSocketFactory().createSocket(arg0, arg1,
arg2, arg3);
    }
}
--cut here--


Am 02.06.2018 um 16:40 schrieb Veit Guna:

> Hi.
>
> I've succesfully integrated BCJSSE programmatically using JDK5 and
> HttpsUrlConnection being able to finally connect to TLSv1.2 webservices.
>
> Basically by creating the SSLContext using the BCJSSE provider and
> initializing it using a TrustManagerFactory and KeyManagerFactory.
> Then getting the SSLSocketFactory via context.getSSLSocketFactory(); and
> setting it on the HttpsUrlConnection.
>
> So far so good. Now I'm wondering, how to achieve the same result,
> without having to touch any code :).
> Since HttpsUrlConnection and e.g. Apache Commons HttpClient are using
> SSLSocketFactory.getDefault(), I currently don't see a way to do this in
> JDK5.
>
> The BCJSSE documentation I found here:
>
> https://downloads.bouncycastle.org/fips-java/BC-FJA-(D)TLSUserGuide-1.0.0.pdf
>
> states, that getDefault() will only return the BCJSSE SSLSocketFactory,
> if the Provider is registered before the Sun JSSE one in java.security.
> I tried it, but it didn't work out. It still returns the Sun JSSE one.
>
> Taking a look at the JDK5 SSLSocketFactory.getDefault() implementation
> shows, that it tries to load a povider, specified via
> 'ssl.SocketFactory.provider' system property.
> But this requires a no-args ctor, which the BC package private
> ProvSSLSocketFactory doesn't provide. So this would fail if one would
> try to override the default one.
>
> Not specifying it via system property, defaults to
> 'com.sun.net.ssl.internal.ssl.SSLSocketFactoryImpl' which is then
> instantiated and used.
> Now I'm wondering, how SSLSockerFactory.getDefault(); should ever return
> the BC one :D.
>
> Could somebody please shed some light on this, how the mechanism
> described in the doc works for JDK5?
>
> All I could imagine would be, to create my own SSLSocketFactory Provider
> implementation, setting up the BC SSLContext within that and returning
> the BC SSLSocketFactory
> from it. Then registering it via mentioned system property. Any other
> ways to do it?
>
> Cheers,
> Veit
>
>
>
>
>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: BCJSSE, JDK5 and SSLSocketFactory.getDefault()

Veit Guna
One thing I forgot to implement was createSocket() - which wasn't
obvious and led to an exception during other usecases. Here's the
missing method:

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }


Am 04.06.2018 um 19:45 schrieb Veit Guna:

> For other people maybe trying to get this done using JDK5 as well,
> here's a small snippet that worked for me.
>
> It's no beauty (no autocloseables since JDK5 etc.), but one should get
> the idea. It's working for me, automatically hooking into a standard
> HttpsUrlConnection.
> Keep in mind that setting
> 'ssl.SocketFactory.provider=fancy.pants.BcSslSocketFactory' in
> java.security is required in order to make this work.
> If it fails with IllegalKeySize or similar, remember to have JCE strong
> encryption policy files in place.
>
> Thanks again for this great library!
>
> --cut here--
>
> package fancy.pants;
>
> import java.io.File;
> import java.io.FileInputStream;
> import java.io.IOException;
> import java.io.InputStream;
> import java.net.InetAddress;
> import java.net.Socket;
> import java.net.UnknownHostException;
> import java.security.KeyManagementException;
> import java.security.KeyStore;
> import java.security.KeyStoreException;
> import java.security.NoSuchAlgorithmException;
> import java.security.NoSuchProviderException;
> import java.security.UnrecoverableKeyException;
> import java.security.cert.CertificateException;
>
> import javax.net.ssl.KeyManagerFactory;
> import javax.net.ssl.SSLContext;
> import javax.net.ssl.SSLSocketFactory;
> import javax.net.ssl.TrustManagerFactory;
>
> import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
>
> public class BcSslSocketFactory extends SSLSocketFactory {
>
>     private static final String DEFAULT_PASSWORD = "changeit";
>
>     private SSLContext sslContext;
>
>     public BcSslSocketFactory() throws IOException, KeyStoreException,
> NoSuchProviderException, NoSuchAlgorithmException, CertificateException,
> UnrecoverableKeyException, KeyManagementException{
>
>         String provider =
> System.getProperty("javax.net.ssl.keyStoreProvider");
>         String keystoreType =
> System.getProperty("javax.net.ssl.keyStoreType", KeyStore.getDefaultType());
>
>         KeyStore keystore = null;
>         if (provider != null) {
>             keystore = KeyStore.getInstance(keystoreType, provider);
>         } else {
>             keystore = KeyStore.getInstance(keystoreType);
>         }
>
>         String keystorePath =
> System.getProperty("javax.net.ssl.trustStore",
> System.getProperty("java.home") + "/lib/security/cacerts".replace('/',
> File.separatorChar));
>         String keystorePassword =
> System.getProperty("javax.net.ssl.trustStorePassword", DEFAULT_PASSWORD);
>
>         InputStream keystoreInputStream = null;
>         try {
>             keystoreInputStream = new FileInputStream(keystorePath);
>             keystore.load(keystoreInputStream,
> keystorePassword.toCharArray());
>         } finally {
>             if (keystoreInputStream != null) {
>                 keystoreInputStream.close();
>             }
>         }
>
>         KeyManagerFactory kmf =
> KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
>         kmf.init(keystore, keystorePassword.toCharArray());
>
>         sslContext = SSLContext.getInstance("TLS", new
> BouncyCastleJsseProvider());
>
>         TrustManagerFactory trustMgrFact =
> TrustManagerFactory.getInstance("PKIX", "BCJSSE");
>         trustMgrFact.init(keystore);
>        
>         sslContext.init(null, trustMgrFact.getTrustManagers(), null);
>     }
>
>     @Override
>     public Socket createSocket(Socket arg0, String arg1, int arg2,
> boolean arg3) throws IOException {
>         return sslContext.getSocketFactory().createSocket(arg0, arg1,
> arg2, arg3);
>     }
>
>     @Override
>     public String[] getDefaultCipherSuites() {
>         return sslContext.getSocketFactory().getDefaultCipherSuites();
>     }
>
>     @Override
>     public String[] getSupportedCipherSuites() {
>         return sslContext.getSocketFactory().getSupportedCipherSuites();
>     }
>
>     @Override
>     public Socket createSocket(String arg0, int arg1) throws
> IOException, UnknownHostException {
>         return sslContext.getSocketFactory().createSocket(arg0, arg1);
>     }
>
>     @Override
>     public Socket createSocket(InetAddress arg0, int arg1) throws
> IOException {
>         return sslContext.getSocketFactory().createSocket(arg0, arg1);
>     }
>
>     @Override
>     public Socket createSocket(String arg0, int arg1, InetAddress arg2,
> int arg3) throws IOException, UnknownHostException {
>         return sslContext.getSocketFactory().createSocket(arg0, arg1,
> arg2, arg3);
>     }
>
>     @Override
>     public Socket createSocket(InetAddress arg0, int arg1, InetAddress
> arg2, int arg3) throws IOException {
>         return sslContext.getSocketFactory().createSocket(arg0, arg1,
> arg2, arg3);
>     }
> }
> --cut here--
>
>
> Am 02.06.2018 um 16:40 schrieb Veit Guna:
>> Hi.
>>
>> I've succesfully integrated BCJSSE programmatically using JDK5 and
>> HttpsUrlConnection being able to finally connect to TLSv1.2 webservices.
>>
>> Basically by creating the SSLContext using the BCJSSE provider and
>> initializing it using a TrustManagerFactory and KeyManagerFactory.
>> Then getting the SSLSocketFactory via context.getSSLSocketFactory(); and
>> setting it on the HttpsUrlConnection.
>>
>> So far so good. Now I'm wondering, how to achieve the same result,
>> without having to touch any code :).
>> Since HttpsUrlConnection and e.g. Apache Commons HttpClient are using
>> SSLSocketFactory.getDefault(), I currently don't see a way to do this in
>> JDK5.
>>
>> The BCJSSE documentation I found here:
>>
>> https://downloads.bouncycastle.org/fips-java/BC-FJA-(D)TLSUserGuide-1.0.0.pdf
>>
>> states, that getDefault() will only return the BCJSSE SSLSocketFactory,
>> if the Provider is registered before the Sun JSSE one in java.security.
>> I tried it, but it didn't work out. It still returns the Sun JSSE one.
>>
>> Taking a look at the JDK5 SSLSocketFactory.getDefault() implementation
>> shows, that it tries to load a povider, specified via
>> 'ssl.SocketFactory.provider' system property.
>> But this requires a no-args ctor, which the BC package private
>> ProvSSLSocketFactory doesn't provide. So this would fail if one would
>> try to override the default one.
>>
>> Not specifying it via system property, defaults to
>> 'com.sun.net.ssl.internal.ssl.SSLSocketFactoryImpl' which is then
>> instantiated and used.
>> Now I'm wondering, how SSLSockerFactory.getDefault(); should ever return
>> the BC one :D.
>>
>> Could somebody please shed some light on this, how the mechanism
>> described in the doc works for JDK5?
>>
>> All I could imagine would be, to create my own SSLSocketFactory Provider
>> implementation, setting up the BC SSLContext within that and returning
>> the BC SSLSocketFactory
>> from it. Then registering it via mentioned system property. Any other
>> ways to do it?
>>
>> Cheers,
>> Veit
>>
>>
>>
>>
>>
>>
>>