Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Create an SslSocketFactory using PEM encrypted certificate files
/**
* Utility class to read encrypted PEM files and generate a
* SSL Socket Factory based on the provided certificates.
* The original code is by Sharon Asher (link below). I have modified
* it to use a newer version of the BouncyCastle Library (v1.52)
*
* Reference - https://gist.github.com/sharonbn/4104301"
*/
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileReader;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
public class SslUtil {
public static SSLSocketFactory getSocketFactory(final String caCrtFile, final String crtFile, final String keyFile,
final String password) {
try {
/**
* Add BouncyCastle as a Security Provider
*/
Security.addProvider(new BouncyCastleProvider());
JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter().setProvider("BC");
/**
* Load Certificate Authority (CA) certificate
*/
PEMParser reader = new PEMParser(new FileReader(caCrtFile));
X509CertificateHolder caCertHolder = (X509CertificateHolder) reader.readObject();
reader.close();
X509Certificate caCert = certificateConverter.getCertificate(caCertHolder);
/**
* Load client certificate
*/
reader = new PEMParser(new FileReader(crtFile));
X509CertificateHolder certHolder = (X509CertificateHolder) reader.readObject();
reader.close();
X509Certificate cert = certificateConverter.getCertificate(certHolder);
/**
* Load client private key
*/
reader = new PEMParser(new FileReader(keyFile));
Object keyObject = reader.readObject();
reader.close();
PEMDecryptorProvider provider = new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
JcaPEMKeyConverter keyConverter = new JcaPEMKeyConverter().setProvider("BC");
KeyPair key;
if (keyObject instanceof PEMEncryptedKeyPair) {
key = keyConverter.getKeyPair(((PEMEncryptedKeyPair) keyObject).decryptKeyPair(provider));
} else {
key = keyConverter.getKeyPair((PEMKeyPair) keyObject);
}
/**
* CA certificate is used to authenticate server
*/
KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
caKeyStore.load(null, null);
caKeyStore.setCertificateEntry("ca-certificate", caCert);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(caKeyStore);
/**
* Client key and certificates are sent to server so it can authenticate the client
*/
KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
clientKeyStore.load(null, null);
clientKeyStore.setCertificateEntry("certificate", cert);
clientKeyStore.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(),
new Certificate[]{cert});
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientKeyStore, password.toCharArray());
/**
* Create SSL socket factory
*/
SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
/**
* Return the newly created socket factory object
*/
return context.getSocketFactory();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
@nyyrikki

This comment has been minimized.

Copy link

nyyrikki commented Feb 9, 2016

thanks for the code examples, but in my case after clientHello and serverHello (turned on -Djavax.net.debug=ssl) my client can't find the a valid certificate even I tried different ways of adding it into the trustmanagerfactory (adding it at runtime from a file, loading the keystore from cacerts, had added it there as well), have you any idea what I am doing wrong?


%% Invalidated: [Session-2, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA]
MQTT Con: CLIENT1, SEND TLSv1 ALERT: fatal, description = certificate_unknown
MQTT Con: CLIENT1, WRITE: TLSv1 Alert, length = 2
MQTT Con: CLIENT1, called closeSocket()
MQTT Con: CLIENT1, handling exception: 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
MQTT Con: CLIENT1, called close()
MQTT Con: CLIENT1, called closeInternal(true)

@weswitt

This comment has been minimized.

Copy link

weswitt commented Mar 6, 2016

I am getting an error when compiling my Android app. I'm using bcpkix-jdk15on-154.jar and bcprov-ext-jdk15on-154.jar, but Gradle says:
Error:Execution failed for task ':app:transformResourcesWithMergeJavaResForDebug'.

com.android.build.api.transform.TransformException: com.android.builder.packaging.DuplicateFileException:
Duplicate files copied in APK META-INF/BCKEY.SF
File1: D:\dev\src\homeauto\hestia\app\libs\bcpkix-jdk15on-154.jar
File2: D:\dev\src\homeauto\hestia\app\libs\bcprov-ext-jdk15on-154.jar

Should I be including a different set of jars?

@iorobertob

This comment has been minimized.

Copy link

iorobertob commented Apr 17, 2016

Hi @weswitt I was having the same error. For me it seems like I had redundant or repeated libraries, meaning jar files. The fact is that after finding out that bouncycastle was not complete for Android, ended up using spongycastle and then at some poing I had libraries from both castles in the project, that caused a mess, but once I was rid of all extra libraries I was not using it all compiled properly.

@rohanag12

This comment has been minimized.

Copy link
Owner Author

rohanag12 commented Apr 22, 2016

@weswitt that is because 'bcprov-ext-jdk15on-154.jar' includes the other jar, so there is no need to include both. Just include the extensions jar.

@rohanag12

This comment has been minimized.

Copy link
Owner Author

rohanag12 commented Apr 22, 2016

@nyyrikki have you generated all the certificates properly ?

@franky1205

This comment has been minimized.

Copy link

franky1205 commented Oct 3, 2016

Hi @rohanag12 I would like to connect to MQTT broker with TLSv1.2 authenticated by Pre-Shared Key standard (RFC 4279). How do I change the code example to apply it ?

@peacecoder

This comment has been minimized.

Copy link

peacecoder commented Feb 23, 2017

@weswitt for android, maybe you need to try to use spongy castle.

@alexxsanchezm

This comment has been minimized.

Copy link

alexxsanchezm commented Apr 26, 2017

I'm not sure why I'm getting this error, maybe the client certificate is not generated properly.

org.eclipse.paho.android.service.MqttTokenAndroid@2a7465ed 04-26 12:50:29.703 18206-18206/.service.TraccarService E/PahoConnector: Throwable: MqttException (0) - javax.net.ssl.SSLException: Error setting private key: ssl=0xb7ad3d60: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch (external/openssl/crypto/x509/x509_cmp.c:330 0xae758005:0x00000000)

@mtippett

This comment has been minimized.

Copy link

mtippett commented Jun 14, 2017

What is the license or governance of this? sharonbn's original gist doesn't have any licensing information either.

@SanyasiraoM

This comment has been minimized.

Copy link

SanyasiraoM commented Jul 25, 2017

@weswitt did you resolved that error?

@sssvrock

This comment has been minimized.

Copy link

sssvrock commented Jul 24, 2019

Hello Guyz, I am connecting to server using server CA, Client CA & Private key, where all three are .pem files. But for private key there is no password, so am using random password. After connecting to server at handshake I am getting error like : fatal error Handshake failure.

WRITE: TLSv1.2 Handshake, length = 40
SwingWorker-pool-1-thread-1, READ: TLSv1.2 Alert, length = 2
SwingWorker-pool-1-thread-1, RECV TLSv1.2 ALERT: fatal, handshake_failure
%% Invalidated: [Session-1, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384]
SwingWorker-pool-1-thread-1, called closeSocket()
SwingWorker-pool-1-thread-1, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

Can any one guide please?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.