-
-
Save saumilsdk/1e17e30e33d0a18f44ce4e2b5841b281 to your computer and use it in GitHub Desktop.
| /** | |
| * Utility class to read encrypted PEM files and generate a SSL Socket Factory based on the provided certificates. | |
| * This utility also support an option to enable and disable server hostname verification. | |
| * | |
| * Note: Java use jks (Java KeyStore) format, but openssl usual use pem format. We can convert it by keytool (jdk build-in tool), or use | |
| * BouncyCastle library to handle pem. | |
| * | |
| * The original code is by Sharon Asher (link below). I have modified it to use a newer version of the BouncyCastle Library (v1.68) | |
| * Add below dependencies to work with this util. | |
| * org.bouncycastle:bcpkix-jdk15on:1.68 | |
| * | |
| * Reference - https://gist.github.com/sharonbn/4104301 and https://gist.github.com/10gic/b204dd9016f0b348797d94861d7962b9 | |
| * @author - Saumil, Kapadia (https://github.com/saumilsdk) | |
| * @version - 3.0 | |
| */ | |
| import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; | |
| 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 java.io.FileReader; | |
| import java.io.IOException; | |
| import java.net.Socket; | |
| import java.security.KeyManagementException; | |
| import java.security.KeyPair; | |
| import java.security.KeyStore; | |
| import java.security.KeyStoreException; | |
| import java.security.NoSuchAlgorithmException; | |
| import java.security.PrivateKey; | |
| import java.security.Security; | |
| import java.security.UnrecoverableKeyException; | |
| import java.security.cert.Certificate; | |
| import java.security.cert.CertificateException; | |
| import java.security.cert.X509Certificate; | |
| import javax.net.ssl.KeyManagerFactory; | |
| import javax.net.ssl.SSLContext; | |
| import javax.net.ssl.SSLEngine; | |
| import javax.net.ssl.SSLSocketFactory; | |
| import javax.net.ssl.TrustManager; | |
| import javax.net.ssl.TrustManagerFactory; | |
| import javax.net.ssl.X509ExtendedTrustManager; | |
| import javax.net.ssl.X509TrustManager; | |
| public class SslUtil { | |
| private SslUtil() { | |
| } | |
| /** | |
| * Create an SslSocketFactory using PEM encrypted certificate files. Mutual SSL | |
| * Authentication is NOT supported. | |
| * | |
| * @param caCrtFile CA certificate of remote server. | |
| * @param serverHostnameVerification Enable/disable verification of server | |
| * certificate DNS and hostname. | |
| * @return | |
| * @throws CertificateException | |
| * @throws IOException | |
| * @throws KeyStoreException | |
| * @throws NoSuchAlgorithmException | |
| * @throws KeyManagementException | |
| */ | |
| public static SSLSocketFactory getSSLSocketFactory(final String caCrtFile, boolean serverHostnameVerification) | |
| throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, | |
| KeyManagementException { | |
| /** | |
| * Add BouncyCastle as a Security Provider | |
| */ | |
| Security.addProvider(new BouncyCastleProvider()); | |
| JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter().setProvider("BC"); | |
| /** | |
| * Load Certificate Authority (CA) certificate | |
| */ | |
| X509CertificateHolder caCertHolder = (X509CertificateHolder) readPEMFile(caCrtFile); | |
| X509Certificate caCert = certificateConverter.getCertificate(caCertHolder); | |
| /** | |
| * CA certificate is used to authenticate server | |
| */ | |
| KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); | |
| caKeyStore.load(null, null); | |
| caKeyStore.setCertificateEntry("ca-certificate", caCert); | |
| /** | |
| * Create SSL socket factory | |
| */ | |
| SSLContext context = SSLContext.getInstance("TLSv1.2"); | |
| context.init(null, | |
| serverHostnameVerification ? getTrustManagers(caKeyStore) : getUnsafeTrustManagers(caKeyStore), null); | |
| /** | |
| * Return the newly created socket factory object | |
| */ | |
| return context.getSocketFactory(); | |
| } | |
| /** | |
| * Create an SslSocketFactory using PEM encrypted certificate files. Mutual SSL | |
| * Authentication is supported. | |
| * | |
| * @param caCrtFile CA certificate of remote server. | |
| * @param crtFile certificate file of client. | |
| * @param keyFile key file of client. | |
| * @param password password of key file. | |
| * @param serverHostnameVerification Enable/disable verification of server | |
| * certificate DNS and hostname. | |
| * @return | |
| * @throws CertificateException | |
| * @throws IOException | |
| * @throws KeyStoreException | |
| * @throws NoSuchAlgorithmException | |
| * @throws KeyManagementException | |
| * @throws UnrecoverableKeyException | |
| */ | |
| public static SSLSocketFactory getSSLSocketFactory(final String caCrtFile, final String crtFile, | |
| final String keyFile, final String password, boolean serverHostnameVerification) | |
| throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, | |
| KeyManagementException, UnrecoverableKeyException { | |
| /** | |
| * Add BouncyCastle as a Security Provider | |
| */ | |
| Security.addProvider(new BouncyCastleProvider()); | |
| JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter().setProvider("BC"); | |
| /** | |
| * Load Certificate Authority (CA) certificate | |
| */ | |
| X509CertificateHolder caCertHolder = (X509CertificateHolder) readPEMFile(caCrtFile); | |
| X509Certificate caCert = certificateConverter.getCertificate(caCertHolder); | |
| /** | |
| * Load client certificate | |
| */ | |
| X509CertificateHolder certHolder = (X509CertificateHolder) readPEMFile(crtFile); | |
| X509Certificate cert = certificateConverter.getCertificate(certHolder); | |
| /** | |
| * Load client private key | |
| */ | |
| Object keyObject = readPEMFile(keyFile); | |
| JcaPEMKeyConverter keyConverter = new JcaPEMKeyConverter().setProvider("BC"); | |
| PrivateKey privateKey = null; | |
| if (keyObject instanceof PEMEncryptedKeyPair) { | |
| PEMDecryptorProvider provider = new JcePEMDecryptorProviderBuilder().build(password.toCharArray()); | |
| KeyPair keyPair = keyConverter.getKeyPair(((PEMEncryptedKeyPair) keyObject).decryptKeyPair(provider)); | |
| privateKey = keyPair.getPrivate(); | |
| } else if (keyObject instanceof PEMKeyPair) { | |
| KeyPair keyPair = keyConverter.getKeyPair((PEMKeyPair) keyObject); | |
| privateKey = keyPair.getPrivate(); | |
| } else if (keyObject instanceof PrivateKeyInfo) { | |
| privateKey = keyConverter.getPrivateKey((PrivateKeyInfo) keyObject); | |
| } else { | |
| throw new IOException(String.format("Unsported type of keyFile %s", keyFile)); | |
| } | |
| /** | |
| * CA certificate is used to authenticate server | |
| */ | |
| KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); | |
| caKeyStore.load(null, null); | |
| caKeyStore.setCertificateEntry("ca-certificate", caCert); | |
| /** | |
| * Client key and certificates are sent to server so it can authenticate the | |
| * client. (server send CertificateRequest message in TLS handshake step). | |
| */ | |
| KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); | |
| clientKeyStore.load(null, null); | |
| clientKeyStore.setCertificateEntry("certificate", cert); | |
| clientKeyStore.setKeyEntry("private-key", privateKey, 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(), | |
| serverHostnameVerification ? getTrustManagers(caKeyStore) : getUnsafeTrustManagers(caKeyStore), null); | |
| /** | |
| * Return the newly created socket factory object | |
| */ | |
| return context.getSocketFactory(); | |
| } | |
| private static Object readPEMFile(String filePath) throws IOException { | |
| try (PEMParser reader = new PEMParser(new FileReader(filePath))) { | |
| return reader.readObject(); | |
| } | |
| } | |
| private static TrustManager[] getTrustManagers(KeyStore caKeyStore) | |
| throws NoSuchAlgorithmException, KeyStoreException { | |
| TrustManagerFactory trustManagerFactory = TrustManagerFactory | |
| .getInstance(TrustManagerFactory.getDefaultAlgorithm()); | |
| trustManagerFactory.init(caKeyStore); | |
| return trustManagerFactory.getTrustManagers(); | |
| } | |
| /** | |
| * This method checks server and client certificates but overrides server hostname verification. | |
| * @param caKeyStore | |
| * @return | |
| * @throws NoSuchAlgorithmException | |
| * @throws KeyStoreException | |
| ' */ | |
| private static TrustManager[] getUnsafeTrustManagers(KeyStore caKeyStore) | |
| throws NoSuchAlgorithmException, KeyStoreException { | |
| X509TrustManager standardTrustManager = (X509TrustManager) getTrustManagers(caKeyStore)[0]; | |
| return new TrustManager[] { new X509ExtendedTrustManager() { | |
| @Override | |
| public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { | |
| standardTrustManager.checkClientTrusted(chain, authType); | |
| } | |
| @Override | |
| public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { | |
| standardTrustManager.checkServerTrusted(chain, authType); | |
| } | |
| @Override | |
| public X509Certificate[] getAcceptedIssuers() { | |
| return standardTrustManager.getAcceptedIssuers(); | |
| } | |
| @Override | |
| public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) | |
| throws CertificateException { | |
| standardTrustManager.checkClientTrusted(chain, authType); | |
| } | |
| @Override | |
| public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) | |
| throws CertificateException { | |
| standardTrustManager.checkServerTrusted(chain, authType); | |
| } | |
| @Override | |
| public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) | |
| throws CertificateException { | |
| standardTrustManager.checkClientTrusted(chain, authType); | |
| } | |
| @Override | |
| public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) | |
| throws CertificateException { | |
| standardTrustManager.checkServerTrusted(chain, authType); | |
| } | |
| } }; | |
| } | |
| } |
Hi,
What location are using for placing the certificate and key files in the android project?
The following path shows the file not found exception.
I placed the files in the assets folder and specified the path as caCertFile = "file:///android_asset/" + "AmazonRootCA1.pem";private static Object readPEMFile(String filePath) throws IOException { try (PEMParser reader = new PEMParser(new FileReader(filePath))) { return reader.readObject(); } }Please specify absolute path without
file:///Thanks, I found another method instead of specifying the path.
I am using the assetManager
Great. Can you put that snippet so we can also use that?
I get this error when giving the absolute path : String caCrtFile = "Users/test/AndroidStudioProjects/MyApplication/app/src/main/res/raw/ca.crt";
java.io.FileNotFoundException: Users/Steve/AndroidStudioProjects/MyApplication/app/src/main/res/raw/ca.crt: open failed: ENOENT (No such file or directory)
2023-09-15 17:04:01.212 24260-24260 System.err com.example.myapplication W at libcore.io.IoBridge.open(IoBridge.java:574)
@Deazyhome were you able to figure this out? Basically you need to keep the ca.crt at some path and use that path in the code.
@saumilsdk No I still haven't solved my problem, I read in an article that it's not possible to use an absolute path in Android Studio. Where should I place my Ca, key and crt certificates. Can you please show me an example, because I'm really lost, I've been trying for 2 weeks but I can't read the certificates.
@ssmr1982 you can share with us the method that allows you to feed the method getSSLSocketFactory
Thanks, I found another method instead of specifying the path.
I am using the assetManager