From e5a656ce577ded6e59bd24bd4b0f0a2825a13a46 Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Tue, 31 Mar 2020 22:09:42 +0200 Subject: [PATCH] Many improvements and bug fixes. --- .../mian/gitnex/clients/IssuesService.java | 2 +- .../gitnex/clients/PullRequestsService.java | 2 +- .../mian/gitnex/clients/RetrofitClient.java | 2 +- .../gitnex/helpers/MemorizingActivity.java | 6 +- .../helpers/MemorizingTrustManager.java | 194 +++++++----------- app/src/main/res/values/strings.xml | 2 +- 6 files changed, 79 insertions(+), 129 deletions(-) diff --git a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java index 999b9ad7..b132aff0 100644 --- a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java +++ b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java @@ -36,7 +36,7 @@ public class IssuesService { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); - try { + try { // try-catch can be problematic here SSLContext sslContext = SSLContext.getInstance("TLS"); MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); diff --git a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java index bf2beb96..23364211 100644 --- a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java +++ b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java @@ -36,7 +36,7 @@ public class PullRequestsService { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); - try { + try { // try-catch can be problematic here SSLContext sslContext = SSLContext.getInstance("TLS"); MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); diff --git a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java index 9e677ff9..85517923 100644 --- a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java +++ b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java @@ -36,7 +36,7 @@ public class RetrofitClient { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); - try { + try { // try-catch can be problematic here SSLContext sslContext = SSLContext.getInstance("TLS"); MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); diff --git a/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java b/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java index a787a007..ee20979e 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java +++ b/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java @@ -18,9 +18,9 @@ public class MemorizingActivity extends Activity { super.onCreate(savedInstanceState); Intent intent = getIntent(); - int decisionId = intent.getIntExtra(MemorizingTrustManager.DECISION_INTENT_ID, MTMDecision.DECISION_INVALID); - int titleId = intent.getIntExtra(MemorizingTrustManager.DECISION_TITLE_ID, R.string.mtm_accept_cert); - String cert = intent.getStringExtra(MemorizingTrustManager.DECISION_INTENT_CERT); + int decisionId = intent.getIntExtra("DECISION_INTENT_ID", MTMDecision.DECISION_INVALID); + int titleId = intent.getIntExtra("DECISION_TITLE_ID", R.string.mtm_accept_cert); + String cert = intent.getStringExtra("DECISION_INTENT_CERT"); AlertDialog.Builder builder = new AlertDialog.Builder(MemorizingActivity.this); builder.setTitle(titleId); diff --git a/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java b/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java index bef13f21..ece64672 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java +++ b/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java @@ -46,20 +46,12 @@ import javax.net.ssl.X509TrustManager; */ public class MemorizingTrustManager implements X509TrustManager { - - private final static String DECISION_INTENT = "de.duenndns.ssl.DECISION"; - final static String DECISION_INTENT_ID = DECISION_INTENT + ".decisionId"; - final static String DECISION_INTENT_CERT = DECISION_INTENT + ".cert"; - - private final static Logger LOGGER = Logger.getLogger(MemorizingTrustManager.class.getName()); - final static String DECISION_TITLE_ID = DECISION_INTENT + ".titleId"; private final static int NOTIFICATION_ID = 100509; private final static String KEYSTORE_NAME = "keystore"; private final static String KEYSTORE_KEY = "keystore"; private Context context; - private Activity foregroundAct; private NotificationManager notificationManager; private static int decisionId = 0; private static final SparseArray openDecisions = new SparseArray<>(); @@ -143,38 +135,6 @@ public class MemorizingTrustManager implements X509TrustManager { return new X509TrustManager[]{new MemorizingTrustManager(c)}; } - /** - * Binds an Activity to the MTM for displaying the query dialog. - *

- * This is useful if your connection is run from a service that is - * triggered by user interaction -- in such cases the activity is - * visible and the user tends to ignore the service notification. - *

- * You should never have a hidden activity bound to MTM! Use this - * function in onResume() and @see unbindDisplayActivity in onPause(). - * - * @param act Activity to be bound - */ - private void bindDisplayActivity(Activity act) { - - foregroundAct = act; - } - - /** - * Removes an Activity from the MTM display stack. - *

- * Always call this function when the Activity added with - * {@link #bindDisplayActivity(Activity)} is hidden. - * - * @param act Activity to be unbound - */ - public void unbindDisplayActivity(Activity act) { - // do not remove if it was overridden by a different activity - if(foregroundAct == act) { - foregroundAct = null; - } - } - /** * Get a list of all certificate aliases stored in MTM. * @@ -365,6 +325,7 @@ public class MemorizingTrustManager implements X509TrustManager { } e = e.getCause(); } while(e != null); + return false; } @@ -374,17 +335,16 @@ public class MemorizingTrustManager implements X509TrustManager { if(e instanceof CertPathValidatorException) { return true; } + e = e.getCause(); } while(e != null); + return false; } private void checkCertTrusted(X509Certificate[] chain, String authType, boolean isServer) throws CertificateException { - - LOGGER.log(Level.FINE, "checkCertTrusted(" + Arrays.toString(chain) + ", " + authType + ", " + isServer + ")"); - try { - LOGGER.log(Level.FINE, "checkCertTrusted: trying appTrustManager"); + if(isServer) { appTrustManager.checkServerTrusted(chain, authType); } @@ -393,22 +353,15 @@ public class MemorizingTrustManager implements X509TrustManager { } } catch(CertificateException ae) { - LOGGER.log(Level.FINER, "checkCertTrusted: appTrustManager did not verify certificate. Will fall back to secondary verification mechanisms (if any).", ae); // if the cert is stored in our appTrustManager, we ignore expiredness - if(isExpiredException(ae)) { - LOGGER.log(Level.INFO, "checkCertTrusted: accepting expired certificate from keystore"); - return; - } - if(isCertKnown(chain[0])) { - LOGGER.log(Level.INFO, "checkCertTrusted: accepting cert already stored in keystore"); + if(isExpiredException(ae) || isCertKnown(chain[0])) { return; } + try { if(defaultTrustManager == null) { - LOGGER.fine("No defaultTrustManager set. Verification failed, throwing " + ae); throw ae; } - LOGGER.log(Level.FINE, "checkCertTrusted: trying defaultTrustManager"); if(isServer) { defaultTrustManager.checkServerTrusted(chain, authType); } @@ -417,7 +370,6 @@ public class MemorizingTrustManager implements X509TrustManager { } } catch(CertificateException e) { - LOGGER.log(Level.FINER, "checkCertTrusted: defaultTrustManager failed", e); interactCert(chain, authType, e); } } @@ -452,12 +404,14 @@ public class MemorizingTrustManager implements X509TrustManager { private static String hexString(byte[] data) { StringBuilder si = new StringBuilder(); + for(int i = 0; i < data.length; i++) { si.append(String.format("%02x", data[i])); if(i < data.length - 1) { si.append(":"); } } + return si.toString(); } @@ -473,91 +427,97 @@ public class MemorizingTrustManager implements X509TrustManager { } } - private static void certDetails(StringBuilder si, X509Certificate c) { + private static void certDetails(StringBuilder stringBuilder, X509Certificate c) { SimpleDateFormat validityDateFormater = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); - si.append("\n"); - si.append(c.getSubjectDN().toString()); - si.append("\n"); - si.append(validityDateFormater.format(c.getNotBefore())); - si.append(" - "); - si.append(validityDateFormater.format(c.getNotAfter())); - si.append("\nSHA-256: "); - si.append(certHash(c, "SHA-256")); - si.append("\nSHA-1: "); - si.append(certHash(c, "SHA-1")); - si.append("\nSigned by: "); - si.append(c.getIssuerDN().toString()); - si.append("\n"); + + stringBuilder.append("\n") + .append(c.getSubjectDN().toString()) + .append("\n") + .append(validityDateFormater.format(c.getNotBefore())) + .append(" - ") + .append(validityDateFormater.format(c.getNotAfter())) + .append("\nSHA-256: ") + .append(certHash(c, "SHA-256")) + .append("\nSHA-1: ") + .append(certHash(c, "SHA-1")) + .append("\nSigned by: ") + .append(c.getIssuerDN().toString()) + .append("\n"); } private String certChainMessage(final X509Certificate[] chain, CertificateException cause) { Throwable e = cause; - StringBuilder si = new StringBuilder(); + StringBuilder stringBuilder = new StringBuilder(); if(isPathException(e)) { - si.append(context.getString(R.string.mtm_trust_anchor)); + stringBuilder.append(context.getString(R.string.mtm_trust_anchor)); } else if(isExpiredException(e)) { - si.append(context.getString(R.string.mtm_cert_expired)); + stringBuilder.append(context.getString(R.string.mtm_cert_expired)); } else { // get to the cause - while(e.getCause() != null) + while(e.getCause() != null) { e = e.getCause(); - si.append(e.getLocalizedMessage()); + } + + stringBuilder.append(e.getLocalizedMessage()); } - si.append("\n\n"); - si.append(context.getString(R.string.mtm_connect_anyway)); - si.append("\n\n"); - si.append(context.getString(R.string.mtm_cert_details)); + stringBuilder.append("\n\n"); + stringBuilder.append(context.getString(R.string.mtm_connect_anyway)); + stringBuilder.append("\n\n"); + stringBuilder.append(context.getString(R.string.mtm_cert_details)); + for(X509Certificate c : chain) { - certDetails(si, c); + certDetails(stringBuilder, c); } - return si.toString(); + + return stringBuilder.toString(); } private String hostNameMessage(X509Certificate cert, String hostname) { - StringBuilder si = new StringBuilder(); + StringBuilder stringBuilder = new StringBuilder(); - si.append(context.getString(R.string.mtm_hostname_mismatch, hostname)); - si.append("\n\n"); + stringBuilder.append(context.getString(R.string.mtm_hostname_mismatch, hostname)); + stringBuilder.append("\n\n"); try { Collection> sans = cert.getSubjectAlternativeNames(); + if(sans == null) { - si.append(cert.getSubjectDN()); - si.append("\n"); + stringBuilder.append(cert.getSubjectDN()); + stringBuilder.append("\n"); } else { for(List altName : sans) { Object name = altName.get(1); if(name instanceof String) { - si.append("["); - si.append(altName.get(0)); - si.append("] "); - si.append(name); - si.append("\n"); + stringBuilder.append("["); + stringBuilder.append(altName.get(0)); + stringBuilder.append("] "); + stringBuilder.append(name); + stringBuilder.append("\n"); } } } } catch(CertificateParsingException e) { e.printStackTrace(); - si.append("\n"); + stringBuilder.append("\n"); } - si.append("\n"); - si.append(context.getString(R.string.mtm_connect_anyway)); - si.append("\n\n"); - si.append(context.getString(R.string.mtm_cert_details)); - certDetails(si, cert); - return si.toString(); + stringBuilder.append("\n"); + stringBuilder.append(context.getString(R.string.mtm_connect_anyway)); + stringBuilder.append("\n\n"); + stringBuilder.append(context.getString(R.string.mtm_cert_details)); + certDetails(stringBuilder, cert); + return stringBuilder.toString(); } /** @@ -584,29 +544,24 @@ public class MemorizingTrustManager implements X509TrustManager { } } - @TargetApi(Build.VERSION_CODES.HONEYCOMB) private void startActivityNotification(Intent intent, int decisionId, String certName) { final PendingIntent call = PendingIntent.getActivity(context, 0, intent, 0); final String mtmNotification = context.getString(R.string.mtm_notification); - NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "ssl").setSmallIcon(android.R.drawable.ic_lock_lock).setContentTitle(mtmNotification).setContentText(certName).setTicker(certName).setContentIntent(call).setAutoCancel(true).setPriority(NotificationCompat.PRIORITY_HIGH); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "ssl") + .setSmallIcon(android.R.drawable.ic_lock_lock) + .setContentTitle(mtmNotification) + .setContentText(certName) + .setTicker(certName) + .setContentIntent(call) + .setAutoCancel(true) + .setPriority(NotificationCompat.PRIORITY_HIGH); notificationManager.notify(NOTIFICATION_ID + decisionId, builder.build()); } - /** - * Returns the top-most entry of the activity stack. - * - * @return the Context of the currently bound UI or the master context if none is bound - */ - Context getUI() { - - return (foregroundAct != null) ? foregroundAct : context; - } - private int interact(final String message, final int titleId) { - /* prepare the MTMDecision blocker object */ MTMDecision choice = new MTMDecision(); final int myId = createDecisionId(choice); @@ -615,19 +570,14 @@ public class MemorizingTrustManager implements X509TrustManager { public void run() { Intent intent = new Intent(context, MemorizingActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.setData(Uri.parse(MemorizingTrustManager.class.getName() + "/" + myId)); - intent.putExtra(DECISION_INTENT_ID, myId); - intent.putExtra(DECISION_INTENT_CERT, message); - intent.putExtra(DECISION_TITLE_ID, titleId); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + intent.putExtra("DECISION_INTENT_ID", myId); + intent.putExtra("DECISION_INTENT_CERT", message); + intent.putExtra("DECISION_TITLE_ID", titleId); - // we try to directly start the activity and fall back to - // making a notification. If no foreground activity is set - // (foregroundAct==null) or if the app developer set an - // invalid / expired activity, the catch-all fallback is - // deployed. try { - foregroundAct.startActivity(intent); + context.startActivity(intent); } catch(Exception e) { startActivityNotification(intent, myId, message); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ed2ddd7..e95e0430 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -92,7 +92,7 @@ 1- Choose the correct protocol(https or http). \n2- Enter Gitea url e.g: try.gitea.io. \n3- If you have enabled 2FA for your account, enter the code in the OTP Code field. \n4- For HTTP basic auth use USERNAME@DOMAIN.COM in the URL field. Wrong username/password :// - Could'nt connect to host. Please check your URL or port for any errors. + Could\'nt connect to host. Please check your URL or port for any errors. It is not recommended to use HTTP protocol unless you are testing on local network. Malformed JSON was received. Server response was not successful. Instance URL is required