package dk.silverbullet.telemed.questionnaire.node; import android.annotation.SuppressLint; import android.app.ProgressDialog; import android.content.Context; import android.net.http.SslCertificate; import android.net.http.SslError; import android.os.Build; import android.util.Base64; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.*; import android.widget.TextView; import dk.silverbullet.telemed.questionnaire.Questionnaire; import dk.silverbullet.telemed.questionnaire.R; import dk.silverbullet.telemed.utils.Util; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.HashMap; public class WebViewNode extends IONode { private static final String TAG = Util.getTag(WebViewNode.class); private final String url; private final String title; public WebViewNode(Questionnaire questionnaire, String nodeName, String url, String title) { super(questionnaire, nodeName); this.url = url; this.title = title; } @Override public void enter() { clearElements(); inflateLayout(); showDialog(); ViewGroup rootLayout = questionnaire.getRootLayout(); linkTopPanel(rootLayout); super.enter(); } private ProgressDialog dialog; private WebView webView; @SuppressLint("SetJavaScriptEnabled") private void inflateLayout() { Context context = questionnaire.getContext(); ViewGroup rootLayout = questionnaire.getRootLayout(); LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View webviewLayout = inflater.inflate(R.layout.webview_node, null); webView = (WebView) webviewLayout.findViewById(R.id.webView); WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true); settings.setUseWideViewPort(true); settings.setSupportZoom(true); settings.setBuiltInZoomControls(true); settings.setLoadWithOverviewMode(true); settings.setSaveFormData(false); settings.setSavePassword(false); CookieSyncManager.createInstance(context); CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptCookie(false); cookieManager.removeAllCookie(); TextView headlineTextView = (TextView) webviewLayout.findViewById(R.id.headlineText); headlineTextView.setText(title); webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url, getRequestMap()); showDialog(); return true; } @Override public void onPageFinished(WebView view, String url) { dialog.dismiss(); } @Override public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) { handler.proceed(username(), password()); } @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { if(Build.VERSION.SDK_INT == 17 /* Android 4.2, which does not trust our production certificates */) { if (isCertificateTrusted(error)) { handler.proceed(); } else { handler.cancel(); } } else { super.onReceivedSslError(view, handler, error); } } }); webView.loadUrl(url, getRequestMap()); rootLayout.removeAllViews(); rootLayout.addView(webviewLayout); } private HashMap<String, String> getRequestMap() { HashMap<String, String> usernamePassword = new HashMap<String, String>(1); try { usernamePassword .put("Authorization", "Basic " + Base64.encodeToString((username() + ":" + password()).getBytes("UTF-8"), Base64.NO_WRAP)); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } return usernamePassword; } private void showDialog() { dialog = ProgressDialog.show(questionnaire.getContext(), Util.getString(R.string.web_view_fetching, questionnaire), Util.getString(R.string.default_please_wait, questionnaire), true); } private String username() { return Util.getStringVariableValue(questionnaire, Util.VARIABLE_USERNAME); } private String password() { return Util.getStringVariableValue(questionnaire, Util.VARIABLE_PASSWORD); } public boolean canGoBack() { return webView.canGoBack(); } public void goBack() { webView.goBack(); } private boolean isCertificateTrusted(SslError error) { try { X509Certificate x509Certificate = getCertificateFromError(error); KeyStore keyStore = createKeyStore(); X509TrustManager x590TrustManager = getTrustManager(keyStore); X509Certificate[] chain = new X509Certificate[]{x509Certificate}; x590TrustManager.checkServerTrusted(chain, TrustManagerFactory.getDefaultAlgorithm()); //checkServerTrusted will throw a CertificateException so if we get this far the certificate is trusted return true; } catch (Exception ex) { Log.e(TAG, "Cert validation failed", ex); return false; } } private X509TrustManager getTrustManager(KeyStore keyStore) throws NoSuchAlgorithmException, KeyStoreException { TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); return (X509TrustManager) trustManagers[0]; } private KeyStore createKeyStore() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); InputStream certificateInputStream = questionnaire.getContext().getResources().openRawResource(R.raw.certs); Certificate certificate; try { certificate = certificateFactory.generateCertificate(certificateInputStream); } finally { certificateInputStream.close(); } keyStore.setCertificateEntry("ca", certificate); return keyStore; } private X509Certificate getCertificateFromError(SslError error) throws NoSuchFieldException, IllegalAccessException { SslCertificate sslCertificate = error.getCertificate(); Field mX509CertificateField = sslCertificate.getClass().getDeclaredField("mX509Certificate"); mX509CertificateField.setAccessible(true); return (X509Certificate) mX509CertificateField.get(sslCertificate); } }