platform/android/Rhodes/src/com/rhomobile/rhodes/socket/SSLImpl.java in rhodes-7.1.17 vs platform/android/Rhodes/src/com/rhomobile/rhodes/socket/SSLImpl.java in rhodes-7.4.1

- old
+ new

@@ -38,14 +38,17 @@ import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.UnrecoverableKeyException; +import java.security.PrivateKey; +import java.security.KeyStore.PasswordProtection; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.security.UnrecoverableEntryException; import java.util.ArrayList; import java.util.List; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; @@ -54,19 +57,25 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import android.net.http.X509TrustManagerExtensions; +import android.os.Build; import android.util.Base64; import com.rhomobile.rhodes.Logger; import com.rhomobile.rhodes.RhoConf; import com.rhomobile.rhodes.file.RhoFileApi; import java.util.StringTokenizer; import java.security.SecureRandom; +import android.os.Looper; +import com.rhomobile.rhodes.RhodesService; +import org.conscrypt.BaseOpenSSLSocketAdapterFactory; +import org.conscrypt.Conscrypt; +import org.conscrypt.OpenSSLProvider; public class SSLImpl { private static final String TAG = "SSLImplJava"; @@ -80,11 +89,16 @@ //Used from jni @SuppressWarnings("unused") private int sockfd; private InputStream is; - private OutputStream os; + private OutputStream os; + + private static Certificate local_server_cert = null; + private static PrivateKey client_private_key = null; + private static Certificate[] client_cert_chain = null; + public native RhoSockAddr getRemoteSockAddr(int sockfd); private static class MyTrustManager implements X509TrustManager { @@ -296,15 +310,121 @@ Logger.I(TAG, "SSL certificates loaded: " + String.valueOf(certs.size()) ); return certs; } - + + private static Certificate loadLocalServerCert(byte[] data) + { + InputStream certStream = new ByteArrayInputStream(data); + Certificate cert = null; + + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + cert = cf.generateCertificate(certStream); + certStream.close(); + } + catch(Exception e) + { + Logger.E(TAG, e.getMessage()); + } + + return cert; + } + + public static PrivateKey getClientPrivateKey() + { + return client_private_key; + } + + public static Certificate[] getClientCertChain() + { + return client_cert_chain; + } + + private static void loadLocalCientP12Bundle(byte[] data, String pwd) + { + if (client_private_key == null && client_cert_chain == null) { + try { + KeyStore clientKeystore = KeyStore.getInstance("pkcs12"); + InputStream p12Stream = new ByteArrayInputStream(data); + clientKeystore.load(p12Stream, pwd.toCharArray()); + + KeyStore.ProtectionParameter protParam = new KeyStore.PasswordProtection(pwd.toCharArray()); + KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) clientKeystore.getEntry("taup12", protParam); + client_private_key = pkEntry.getPrivateKey(); + client_cert_chain = pkEntry.getCertificateChain(); + + p12Stream.close(); + } + catch (Exception e) + { + Logger.I(TAG, e.getMessage()); + client_private_key = null; + client_cert_chain = null; + } + + } + } + + public static SSLSocketFactory getSecureClientFactory() throws NoSuchAlgorithmException, KeyManagementException, CertificateException, KeyStoreException, IOException, UnrecoverableKeyException + { + SSLContext context = SSLContext.getInstance("TLS"); + + Logger.I(TAG, "Creating TrustManager for system certificates"); + TrustManagerFactory systemTmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + systemTmf.init((KeyStore)null); + X509TrustManager systemTrustManager = (X509TrustManager)systemTmf.getTrustManagers()[0]; + + KeyStore keystore = KeyStore.getInstance( KeyStore.getDefaultType() ); + keystore.load(null); + + if(local_server_cert != null) + { + keystore.setCertificateEntry("cert-alias-local", local_server_cert); + } + + Logger.I(TAG, "Creating TrustManager for custom certificates"); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(keystore); + X509TrustManager customTrustManager = (X509TrustManager)tmf.getTrustManagers()[0]; + + KeyManagerFactory kmf = null; + KeyStore clientKeystore = null; + + if(client_private_key != null && client_cert_chain != null) + { + if (clientKeystore == null) + { + clientKeystore = KeyStore.getInstance("pkcs12"); + clientKeystore.load(null); + } + + KeyStore.PrivateKeyEntry pkEntry = new KeyStore.PrivateKeyEntry(client_private_key, client_cert_chain); + clientKeystore.setEntry("taup12", pkEntry, null); + } + + if (clientKeystore != null) { + kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(clientKeystore, null); + } + + context.init( + (kmf==null)?null:kmf.getKeyManagers(), + new TrustManager[] { new MySecureTrustManager( systemTrustManager, customTrustManager ) }, + new SecureRandom() + ); + + Logger.I(TAG, "Secure SSL factory initialization completed"); + + return (SSLSocketFactory)context.getSocketFactory(); + } + private static SSLSocketFactory getSecureFactory() throws NoSuchAlgorithmException, KeyManagementException, CertificateException, KeyStoreException, IOException, UnrecoverableKeyException { Logger.I(TAG, "Creating secure SSL factory"); - SSLContext context = SSLContext.getInstance("TLS"); + SSLContext context = Build.VERSION.SDK_INT >= 30 ? SSLContext.getInstance("TLS", new OpenSSLProvider()) : SSLContext.getInstance("TLS"); // First, load all system installed certificates Logger.I(TAG, "Creating TrustManager for system certificates"); TrustManagerFactory systemTmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); systemTmf.init((KeyStore)null); @@ -321,38 +441,58 @@ for ( int i = 0; i < certs.size(); ++i ) { keystore.setCertificateEntry("cert-alias"+ String.valueOf(i),certs.get(i)); } } + if(local_server_cert != null) + { + keystore.setCertificateEntry("cert-alias-local", local_server_cert); + } Logger.I(TAG, "Creating TrustManager for custom certificates"); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(keystore); X509TrustManager customTrustManager = (X509TrustManager)tmf.getTrustManagers()[0]; KeyManagerFactory kmf = null; - + KeyStore clientKeystore = null; + if ( RhoConf.isExist("clientSSLCertificate")) { String clientCertPath = RhoConf.getString("clientSSLCertificate"); Logger.I(TAG, "clientSSLCertificate is " + clientCertPath ); - if ( clientCertPath.length() > 0 ) { Logger.I(TAG, "Creating KeyManager for client certificates"); kmf = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() ); String password = ""; if (RhoConf.isExist("clientSSLCertificatePassword")) { password = RhoConf.getString("clientSSLCertificatePassword"); } - - KeyStore clientKeystore = KeyStore.getInstance( "pkcs12" ); - clientKeystore.load( RhoFileApi.open(clientCertPath), password.toCharArray() ); - kmf.init(clientKeystore, password.toCharArray()); + + clientKeystore = KeyStore.getInstance("pkcs12"); + clientKeystore.load(RhoFileApi.open(clientCertPath), password.toCharArray() ); } } + + if(client_private_key != null && client_cert_chain != null) + { + if (clientKeystore == null) + { + clientKeystore = KeyStore.getInstance("pkcs12"); + clientKeystore.load(null); + } + + KeyStore.PrivateKeyEntry pkEntry = new KeyStore.PrivateKeyEntry(client_private_key, client_cert_chain); + clientKeystore.setEntry("taup12", pkEntry, null); + } + + if (clientKeystore != null) { + kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(clientKeystore, null); + } /* * this really works only with first provided TrustManager, * so we make our own wrapper which encapsulates both system installed and custom provided certificates */ @@ -366,20 +506,24 @@ return (SSLSocketFactory)context.getSocketFactory(); } - private static SSLSocketFactory getFactory(boolean verify) throws NoSuchAlgorithmException, KeyManagementException, CertificateException, KeyStoreException, IOException, UnrecoverableKeyException { - if (verify) { + public static SSLSocketFactory getFactory(boolean verify) throws NoSuchAlgorithmException, KeyManagementException, CertificateException, KeyStoreException, IOException, UnrecoverableKeyException { + + if (Build.VERSION.SDK_INT >= 30) + Conscrypt.setUseEngineSocketByDefault(false); + + if (verify) { //if ( secureFactory == null ) { secureFactory = getSecureFactory(); //} return secureFactory; } if (factory == null) { - SSLContext context = SSLContext.getInstance("TLS"); + SSLContext context = Build.VERSION.SDK_INT >= 30 ? SSLContext.getInstance("TLS", new OpenSSLProvider()) : SSLContext.getInstance("TLS"); TrustManager[] managers = {new MyTrustManager()}; context.init(null, managers, new SecureRandom()); factory = context.getSocketFactory(); } return factory; @@ -396,10 +540,95 @@ reportFail("connect", e); e.printStackTrace(); return false; } } + + public static void setRawCertificate(byte[] data) + { + local_server_cert = loadLocalServerCert(data); + } + + + public static void setP12(byte[] data, String p) + { + loadLocalCientP12Bundle(data, p); + } + + private class SSLThreadIn extends Thread + { + private int n = 0; + private InputStream is = null; + private byte[] data = null; + private int size = 0; + + public SSLThreadIn(InputStream i, byte[] d, int s) + { + this.is = i; + size = s; + data = d; + } + + public void run() { + if(is != null) + { + try { + n = is.read(data, 0, size); + } catch (IOException e) { + + } + } + } + + public int getReadenBytes() + { + return n; + } + + } + + private class SSLThreadShutdown extends Thread + { + private SSLSocket sock; + + public SSLThreadShutdown(SSLSocket s) + { + sock = s; + } + + public void run() { + try { + sock.close(); + } catch (IOException e) { + + } + } + } + + private class SSLThreadOut extends Thread + { + private OutputStream os = null; + private byte[] data = null; + + public SSLThreadOut(OutputStream o, byte[] d) + { + this.os = o; + data = d; + } + + public void run() { + if(os != null) + { + try { + os.write(data); + } catch (IOException e) { + + } + } + } + + } public boolean connect(int fd, boolean sslVerifyPeer, String hostname ) { try { Logger.I(TAG, "SSL connect to " + hostname); @@ -431,11 +660,18 @@ public void shutdown() { try { if (sock != null) { synchronized (this) { if (sock != null) { - sock.close(); + + if (RhodesService.isLocalHttpsServerEnable() && Looper.myLooper() == Looper.getMainLooper()) { + SSLThreadShutdown shutdownThread = new SSLThreadShutdown(sock); + shutdownThread.start(); + shutdownThread.join(); + } else + sock.close(); + sock = null; os = null; is = null; //TODO: check file descriptor is closed gracefully @@ -456,11 +692,18 @@ synchronized (this) { if (os != null) aOs = os; } if (aOs != null) { - aOs.write(data); + + if (RhodesService.isLocalHttpsServerEnable() && Looper.myLooper() == Looper.getMainLooper()) { + SSLThreadOut ssl_out = new SSLThreadOut(os, data); + ssl_out.start(); + ssl_out.join(); + } else + aOs.write(data); + return true; } } } catch (Exception e) { @@ -477,11 +720,20 @@ if (is != null) aIs = is; } if (aIs != null) { int size = data.length; - int n = is.read(data, 0, size); + int n = 0; + + if (RhodesService.isLocalHttpsServerEnable() && Looper.myLooper() == Looper.getMainLooper()) { + SSLThreadIn ssl_in = new SSLThreadIn(is, data, size); + ssl_in.start(); + ssl_in.join(); + n = ssl_in.getReadenBytes(); + } else + n = is.read(data, 0, size); + return n; } } } catch (Exception e) { @@ -490,6 +742,6 @@ return -1; } -} +} \ No newline at end of file