From ceabdaaa642d08d97faafeba5a4a06b2e0e9bbe3 Mon Sep 17 00:00:00 2001 From: Aigio Liu Date: Fri, 25 Mar 2022 00:17:41 +0800 Subject: [PATCH] =?UTF-8?q?fix=20Android=20Guide=20Pages=20Test=20API=2031?= =?UTF-8?q?=20=F0=9F=86=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HttpClientExtensions.AddTrustedCerts.cs | 37 +++++-- src/ST.Client.Android/GoToPlatformPages.cs | 82 ++++++++++------ .../Properties/ManifestConstants.cs | 7 ++ .../AndroidPlatformServiceImpl.Certificate.cs | 46 +++++++++ .../Services/Native/ProxyForegroundService.cs | 11 ++- .../UI/Activities/GuideCACertActivity.cs | 97 +++++++++++++++---- .../UI/Activities/GuideVPNActivity.cs | 15 ++- .../UI/Fragments/CommunityFixFragment.cs | 18 +++- .../ActivityResultTask.cs | 7 +- .../fragment_guide_export_ca_cert_content.xml | 18 ++-- ...ment_guide_how_install_ca_cert_content.xml | 23 +++-- .../ui/src/main/res/values-en/strings.xml | 3 +- .../ui/src/main/res/values-es/strings.xml | 3 +- .../ui/src/main/res/values-it/strings.xml | 3 +- .../ui/src/main/res/values-ja/strings.xml | 3 +- .../ui/src/main/res/values-ko/strings.xml | 3 +- .../ui/src/main/res/values-ru/strings.xml | 3 +- .../ui/src/main/res/values-zh-rHK/strings.xml | 3 +- .../ui/src/main/res/values/strings.xml | 3 +- .../Services/IPlatformService.Certificate.cs | 14 +++ .../Implementation/HttpProxyServiceImpl.cs | 24 +++-- 21 files changed, 318 insertions(+), 105 deletions(-) create mode 100644 src/ST.Client.Android/Services/Implementation/AndroidPlatformServiceImpl.Certificate.cs create mode 100644 src/ST.Client/Services/IPlatformService.Certificate.cs diff --git a/src/Common.ClientLib.Droid/Extensions/HttpClientExtensions.AddTrustedCerts.cs b/src/Common.ClientLib.Droid/Extensions/HttpClientExtensions.AddTrustedCerts.cs index bc880e3ef..edf3d1bc6 100644 --- a/src/Common.ClientLib.Droid/Extensions/HttpClientExtensions.AddTrustedCerts.cs +++ b/src/Common.ClientLib.Droid/Extensions/HttpClientExtensions.AddTrustedCerts.cs @@ -2,33 +2,54 @@ using Android.Runtime; using Java.Security.Cert; using System.IO; using Xamarin.Android.Net; +using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2; // ReSharper disable once CheckNamespace namespace System { public static partial class HttpClientExtensions { + const string certificateFileType = "X.509"; + + static readonly Lazy certificateFactory = new(() => + { + var certificateFactory = CertificateFactory.GetInstance(certificateFileType); + return certificateFactory; + }); + + internal static CertificateFactory CertificateFactory + { + get + { + var certificateFactory = HttpClientExtensions.certificateFactory.Value; + if (certificateFactory == null) + throw new NullReferenceException( + $"CertificateFactory.GetInstance Fail, Type: {certificateFileType}"); + return certificateFactory; + } + } + /// /// 添加可信任证书,可用于调试时HTTPS抓包 /// /// /// 证书文件数据(文件流,内嵌资源流) - /// 证书文件类型,默认使用X.509 public static void AddTrustedCert( this AndroidClientHandler handler, - Stream certificateFileData, - string certificateFileType = "X.509") + Stream certificateFileData) { - var certificateFactory = CertificateFactory.GetInstance(certificateFileType); - if (certificateFactory == null) - throw new NullReferenceException( - $"CertificateFactory.GetInstance Fail, Type: {certificateFileType}"); - var certificate = certificateFactory.GenerateCertificate(certificateFileData); + var certificate = CertificateFactory.GenerateCertificate(certificateFileData); if (certificate == null) throw new NullReferenceException("GenerateCertificate Fail"); if (handler.TrustedCerts == null) handler.TrustedCerts = new JavaList(); handler.TrustedCerts.Add(certificate); } + + public static X509Certificate Convert(this X509Certificate2 certificate2) + { + using var stream = new MemoryStream(certificate2.RawData); + return CertificateFactory.GenerateCertificate(stream)!.JavaCast(); + } } } \ No newline at end of file diff --git a/src/ST.Client.Android/GoToPlatformPages.cs b/src/ST.Client.Android/GoToPlatformPages.cs index c96c9bbf6..6226cd629 100644 --- a/src/ST.Client.Android/GoToPlatformPages.cs +++ b/src/ST.Client.Android/GoToPlatformPages.cs @@ -84,38 +84,38 @@ namespace System.Application return default; } -// [Obsolete] -// public static void StartActivity(Activity activity, Type activityType, object viewModel, Type? viewModelType = null) -// { -// viewModelType ??= viewModel.GetType(); -// var viewModel_ = Serializable.SMP(viewModelType, viewModel); -//#if DEBUG -// var t = Serializable.DMP(viewModelType, viewModel_); -//#endif -// StartActivity(activity, activityType, viewModel_); -// } + // [Obsolete] + // public static void StartActivity(Activity activity, Type activityType, object viewModel, Type? viewModelType = null) + // { + // viewModelType ??= viewModel.GetType(); + // var viewModel_ = Serializable.SMP(viewModelType, viewModel); + //#if DEBUG + // var t = Serializable.DMP(viewModelType, viewModel_); + //#endif + // StartActivity(activity, activityType, viewModel_); + // } -// [Obsolete] -// public static void StartActivity(Fragment fragment, Type activityType, object viewModel, Type? viewModelType = null) -// { -// var activity = fragment.Activity; -// if (activity == null) return; -// StartActivity(activity, activityType, viewModel, viewModelType); -// } + // [Obsolete] + // public static void StartActivity(Fragment fragment, Type activityType, object viewModel, Type? viewModelType = null) + // { + // var activity = fragment.Activity; + // if (activity == null) return; + // StartActivity(activity, activityType, viewModel, viewModelType); + // } -// [Obsolete] -// public static void StartActivity(Activity activity, object viewModel, Type? viewModelType = null) -// { -// StartActivity(activity, typeof(TActivity), viewModel, viewModelType); -// } + // [Obsolete] + // public static void StartActivity(Activity activity, object viewModel, Type? viewModelType = null) + // { + // StartActivity(activity, typeof(TActivity), viewModel, viewModelType); + // } -// [Obsolete] -// public static void StartActivity(Fragment fragment, object viewModel, Type? viewModelType = null) -// { -// var activity = fragment.Activity; -// if (activity == null) return; -// StartActivity(activity, viewModel, viewModelType); -// } + // [Obsolete] + // public static void StartActivity(Fragment fragment, object viewModel, Type? viewModelType = null) + // { + // var activity = fragment.Activity; + // if (activity == null) return; + // StartActivity(activity, viewModel, viewModelType); + // } public static void StartActivity(Activity activity, Type activityType, TViewModel viewModel) { @@ -184,7 +184,7 @@ namespace System.Application return InstallApk(context, apkFile); } - public static bool InstallCertificate(Context context, string filePath, string name) + public static Intent? GetInstallCertificateIntent(string filePath, string name) { try { @@ -193,6 +193,20 @@ namespace System.Application intent.PutExtra("name", name); intent.PutExtra("CERT", File.ReadAllBytes(filePath)); intent.AddFlags(ActivityFlags.NewTask); + return intent; + } + catch + { + return null; + } + } + + public static bool InstallCertificate(Context context, string filePath, string name) + { + try + { + var intent = GetInstallCertificateIntent(filePath, name); + if (intent == null) return false; context.StartActivity(intent); return true; } @@ -202,10 +216,16 @@ namespace System.Application } } - static void StartNewTaskActivity(Context context, string action) + public static Intent GetNewTaskIntent(string action) { var intent = new Intent(action); intent.AddFlags(ActivityFlags.NewTask); + return intent; + } + + static void StartNewTaskActivity(Context context, string action) + { + var intent = GetNewTaskIntent(action); context.StartActivity(intent); } diff --git a/src/ST.Client.Android/Properties/ManifestConstants.cs b/src/ST.Client.Android/Properties/ManifestConstants.cs index 02245028c..c8fc35db3 100644 --- a/src/ST.Client.Android/Properties/ManifestConstants.cs +++ b/src/ST.Client.Android/Properties/ManifestConstants.cs @@ -15,6 +15,13 @@ namespace System.Application ConfigChanges.LayoutDirection | ConfigChanges.SmallestScreenSize; + public const ConfigChanges ConfigurationChangesWithOutOrientationLocale = + ConfigChanges.ScreenSize | + ConfigChanges.Keyboard | + ConfigChanges.KeyboardHidden | + ConfigChanges.ScreenLayout | + ConfigChanges.SmallestScreenSize; + public const string MainTheme2 = "@style/MainTheme2"; public const string MainTheme2_NoActionBar = "@style/MainTheme2.NoActionBar"; public const string MainTheme2_Splash = "@style/MainTheme2.Splash"; diff --git a/src/ST.Client.Android/Services/Implementation/AndroidPlatformServiceImpl.Certificate.cs b/src/ST.Client.Android/Services/Implementation/AndroidPlatformServiceImpl.Certificate.cs new file mode 100644 index 000000000..e5d801d62 --- /dev/null +++ b/src/ST.Client.Android/Services/Implementation/AndroidPlatformServiceImpl.Certificate.cs @@ -0,0 +1,46 @@ +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using Java.Security; +using Javax.Net.Ssl; + +namespace System.Application.Services.Implementation +{ + partial class AndroidPlatformServiceImpl + { + static IX509TrustManager? X509TrustManager + { + get + { + var instance = KeyStore.GetInstance("AndroidCAStore"); + if (instance == null) return null; + instance.Load(null); + var instance2 = TrustManagerFactory.GetInstance(TrustManagerFactory.DefaultAlgorithm); + if (instance2 == null) return null; + instance2.Init(instance); + var trustManager = instance2.GetTrustManagers()? + .Select(x => x is IX509TrustManager x509TrustManager ? x509TrustManager : null) + .FirstOrDefault(x => x != null); + return trustManager; + } + } + + internal static bool IsCertificateInstalled(X509Certificate2 certificate2) + { + var x509TrustManager = X509TrustManager; + if (x509TrustManager == null) return false; + using var certificate = certificate2.Convert(); + var authType = certificate.SigAlgName; + try + { + x509TrustManager.CheckServerTrusted(new[] { certificate }, authType); + return true; + } + catch (Exception e) + { + return false; + } + } + + bool IPlatformService.IsCertificateInstalled(X509Certificate2 certificate2) => IsCertificateInstalled(certificate2); + } +} diff --git a/src/ST.Client.Android/Services/Native/ProxyForegroundService.cs b/src/ST.Client.Android/Services/Native/ProxyForegroundService.cs index c11dd17f8..bc3aea2aa 100644 --- a/src/ST.Client.Android/Services/Native/ProxyForegroundService.cs +++ b/src/ST.Client.Android/Services/Native/ProxyForegroundService.cs @@ -33,8 +33,15 @@ namespace System.Application.Services.Native { void OnResult(Intent intent) => activity.StartOrStopForegroundService(true); - await IntermediateActivity.StartAsync(intent, - requestCodeVpnService, onResult: OnResult); + try + { + await IntermediateActivity.StartAsync(intent, + requestCodeVpnService, onResult: OnResult); + } + catch (OperationCanceledException) + { + + } return; } } diff --git a/src/ST.Client.Android/UI/Activities/GuideCACertActivity.cs b/src/ST.Client.Android/UI/Activities/GuideCACertActivity.cs index ce1be9fe4..e4110067a 100644 --- a/src/ST.Client.Android/UI/Activities/GuideCACertActivity.cs +++ b/src/ST.Client.Android/UI/Activities/GuideCACertActivity.cs @@ -18,6 +18,8 @@ using Android.Views; using System.Application.Services; using ASettings = Android.Provider.Settings; using static AndroidX.Activity.Result.ActivityResultTask; +using System.Threading.Tasks; +using XEPlatform = Xamarin.Essentials.Platform; namespace System.Application.UI.Activities { @@ -27,7 +29,7 @@ namespace System.Application.UI.Activities [Register(JavaPackageConstants.Activities + nameof(GuideCACertActivity))] [Activity(Theme = ManifestConstants.MainTheme2_NoActionBar, LaunchMode = LaunchMode.SingleTask, - ConfigurationChanges = ManifestConstants.ConfigurationChanges)] + ConfigurationChanges = ManifestConstants.ConfigurationChangesWithOutOrientationLocale)] internal sealed class GuideCACertActivity : BaseActivity { protected override int? LayoutResource => Resource.Layout.activity_guide_ca_cert; @@ -38,9 +40,13 @@ namespace System.Application.UI.Activities this.SetSupportActionBarWithNavigationClick(binding!.toolbar, true); + Title = string.Empty; + var appBarConfiguration = new AppBarConfiguration.Builder(Resource.Id.navigation_guide_export_ca_cert).Build(); navController = ((NavHostFragment)SupportFragmentManager.FindFragmentById(Resource.Id.nav_host_fragment)).NavController; NavigationUI.SetupActionBarWithNavController(this, navController, appBarConfiguration); + + SupportActionBar.SetDisplayHomeAsUpEnabled(true); } [Register(JavaPackageConstants.Fragments + nameof(GuideExportCACertFragment))] @@ -53,10 +59,24 @@ namespace System.Application.UI.Activities return IViewModelManager.Instance.GetMainPageViewModel(); } + bool canInstalledCACert; public override void OnCreateView(View view) { base.OnCreateView(view); + var sdkInt = (int)Build.VERSION.SdkInt; + canInstalledCACert = !(sdkInt > 29 || (sdkInt == 29 && Build.VERSION.PreviewSdkInt > 0)); + // com.adguard.kit.compatibility.h() + // com.adguard.android.ui.fragments.https_ca_installation.b() + // return com.adguard.kit.compatibility.a.h() ? super.b() : R.l.https_ca_installation_guide_install_ca; + // Android 10+ 只能通过系统设置从存储区导入 CA 证书 + + if (canInstalledCACert) + { + binding!.tvDesc3.Visibility = ViewStates.Gone; + binding!.btnDone.Text = GetString(Resource.String.guide_export_ca_cert_btn_install); + } + SetOnClickListener(binding!.btnDone); } @@ -64,7 +84,14 @@ namespace System.Application.UI.Activities { if (view.Id == Resource.Id.btnDone) { - ExportCertificateFile(); + if (canInstalledCACert) + { + InstallCertificate(); + } + else + { + ExportCertificateFile(); + } return true; } return base.OnClick(view); @@ -75,15 +102,48 @@ namespace System.Application.UI.Activities var isOK = await ViewModel!.ExportCertificateFileAsync(); if (isOK) { - GoToGuideHowInstallCACertPage(); + await GoToGuideHowInstallCACertPageAsync(); } } - void GoToGuideHowInstallCACertPage() + async Task GoToGuideHowInstallCACertPageAsync() { + int count = 0; + while (XEPlatform.CurrentActivity != RequireActivity() && count < 10) + { + await Task.Delay(200); + count++; + } var navController = this.GetNavController(); navController?.Navigate(Resource.Id.action_navigation_guide_export_ca_cert_to_navigation_guide_how_install_ca_cert); } + + async void InstallCertificate() + { + var isOK = await ViewModel!.ExportCertificateFileAsync(InstallCertificateAsync); + if (isOK) + { + var a = RequireActivity(); + var intent = IntermediateActivity.SetResult(new(), Result.Ok); + a.SetResult(Result.Ok, intent); + a.Finish(); + } + } + + async Task InstallCertificateAsync(string cefFilePath) + { + var intent = GoToPlatformPages.GetInstallCertificateIntent(cefFilePath, IHttpProxyService.RootCertificateName); + if (intent == null) return false; + try + { + await IntermediateActivity.StartAsync(intent, NextRequestCode()); + } + catch (OperationCanceledException) + { + + } + return IHttpProxyService.Instance.IsCurrentCertificateInstalled; + } } [Register(JavaPackageConstants.Fragments + nameof(GuideHowInstallCACertFragment))] @@ -98,29 +158,28 @@ namespace System.Application.UI.Activities SetOnClickListener(binding!.btnDone); } + public override void OnResume() + { + base.OnResume(); + + if (IHttpProxyService.Instance.IsCurrentCertificateInstalled) + { + var intent = IntermediateActivity.SetResult(new(), Result.Ok); + var a = RequireActivity(); + a.SetResult(Result.Ok, intent); + a.Finish(); + } + } + protected override bool OnClick(View view) { if (view.Id == Resource.Id.btnDone) { - GoToSystemSettingsSecurity(); + GoToPlatformPages.SystemSettingsSecurity(RequireActivity()); return true; } return base.OnClick(view); } - - async void GoToSystemSettingsSecurity() - { - var intent = new Intent(ASettings.ActionSecuritySettings); - intent.AddFlags(ActivityFlags.NewTask); - intent = await IntermediateActivity.StartAsync(intent, NextRequestCode()); - var a = RequireActivity(); - if (IHttpProxyService.Instance.IsCurrentCertificateInstalled) - { - intent = IntermediateActivity.SetResult(intent, Result.Ok); - a.SetResult(Result.Ok, intent); - } - a.Finish(); - } } } } diff --git a/src/ST.Client.Android/UI/Activities/GuideVPNActivity.cs b/src/ST.Client.Android/UI/Activities/GuideVPNActivity.cs index 5d399b191..ab9080cf4 100644 --- a/src/ST.Client.Android/UI/Activities/GuideVPNActivity.cs +++ b/src/ST.Client.Android/UI/Activities/GuideVPNActivity.cs @@ -26,7 +26,7 @@ namespace System.Application.UI.Activities [Register(JavaPackageConstants.Activities + nameof(GuideVPNActivity))] [Activity(Theme = ManifestConstants.MainTheme2_NoActionBar, LaunchMode = LaunchMode.SingleTask, - ConfigurationChanges = ManifestConstants.ConfigurationChanges)] + ConfigurationChanges = ManifestConstants.ConfigurationChangesWithOutOrientationLocale)] internal sealed class GuideVPNActivity : BaseActivity { protected override int? LayoutResource => Resource.Layout.activity_guide_vpn; @@ -37,6 +37,8 @@ namespace System.Application.UI.Activities this.SetSupportActionBarWithNavigationClick(binding!.toolbar, true); + Title = string.Empty; + SetOnClickListener(binding.btnDone); } @@ -51,8 +53,15 @@ namespace System.Application.UI.Activities SetResult(Result.Ok, intent); Finish(); } - await IntermediateActivity.StartAsync(intent, - NextRequestCode(), onResult: OnResult); + try + { + await IntermediateActivity.StartAsync(intent, + NextRequestCode(), onResult: OnResult); + } + catch (OperationCanceledException) + { + + } } else { diff --git a/src/ST.Client.Android/UI/Fragments/CommunityFixFragment.cs b/src/ST.Client.Android/UI/Fragments/CommunityFixFragment.cs index 524decb8c..810fddfd7 100644 --- a/src/ST.Client.Android/UI/Fragments/CommunityFixFragment.cs +++ b/src/ST.Client.Android/UI/Fragments/CommunityFixFragment.cs @@ -163,7 +163,14 @@ namespace System.Application.UI.Fragments { // 需要授予 VPN 权限 intent = new Intent(a, typeof(GuideVPNActivity)); - intent = await IntermediateActivity.StartAsync(intent, NextRequestCode()); + try + { + intent = await IntermediateActivity.StartAsync(intent, NextRequestCode()); + } + catch (OperationCanceledException) + { + return; + } } } @@ -171,7 +178,14 @@ namespace System.Application.UI.Fragments { // 当未安装证书时 intent = new Intent(a, typeof(GuideCACertActivity)); - intent = await IntermediateActivity.StartAsync(intent, NextRequestCode(), onResult: OnResult); + try + { + intent = await IntermediateActivity.StartAsync(intent, NextRequestCode()); + } + catch (OperationCanceledException) + { + return; + } } if (intent != null) diff --git a/src/ST.Client.Android/_/AndroidX.Activity.Result/ActivityResultTask.cs b/src/ST.Client.Android/_/AndroidX.Activity.Result/ActivityResultTask.cs index 2d7d271c9..7c0195e75 100644 --- a/src/ST.Client.Android/_/AndroidX.Activity.Result/ActivityResultTask.cs +++ b/src/ST.Client.Android/_/AndroidX.Activity.Result/ActivityResultTask.cs @@ -39,6 +39,7 @@ namespace AndroidX.Activity.Result const string actualIntentExtra = "actual_intent"; const string guidExtra = "guid"; const string requestCodeExtra = "request_code"; + const string resultExtra = "droid_app_result"; static readonly ConcurrentDictionary pendingTasks = new(); @@ -173,17 +174,15 @@ namespace AndroidX.Activity.Result public Action? OnResult { get; } } - const string AndroidAppResult = "AndroidAppResult"; - public static Intent SetResult(Intent? data, AndroidAppResult resultCode) { if (data == null) data = new(); - data.PutExtra(AndroidAppResult, (int)resultCode); + data.PutExtra(resultExtra, (int)resultCode); return data; } public static AndroidAppResult GetResult(Intent data, int defaultValue = int.MinValue) - => (AndroidAppResult)data.GetIntExtra(AndroidAppResult, defaultValue); + => (AndroidAppResult)data.GetIntExtra(resultExtra, defaultValue); } } } diff --git a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/layout/fragment_guide_export_ca_cert_content.xml b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/layout/fragment_guide_export_ca_cert_content.xml index 70bdac78a..1f1bd4895 100644 --- a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/layout/fragment_guide_export_ca_cert_content.xml +++ b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/layout/fragment_guide_export_ca_cert_content.xml @@ -33,7 +33,6 @@ android:paddingStart="@dimen/activity_horizontal_margin" android:paddingEnd="@dimen/activity_horizontal_margin" android:tint="?colorPrimary" - app:layout_constraintBottom_toTopOf="@+id/tvTitle" app:layout_constraintEnd_toStartOf="@+id/guideline_right" app:layout_constraintStart_toStartOf="@+id/guideline_left" app:layout_constraintTop_toTopOf="parent" @@ -44,57 +43,56 @@ android:id="@+id/tvTitle" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/activity_vertical_margin" android:paddingTop="@dimen/activity_vertical_margin" android:text="@string/guide_export_ca_cert_title" android:textColor="@color/text_primary" android:textSize="24sp" android:textStyle="bold" - app:layout_constraintBottom_toTopOf="@+id/tvDesc1" app:layout_constraintEnd_toStartOf="@+id/guideline_right" app:layout_constraintStart_toStartOf="@+id/guideline_left" + app:layout_constraintTop_toBottomOf="@+id/ivLogo" xamarin:managedType="AndroidX.AppCompat.Widget.AppCompatTextView" /> This certificate needs to be installed in order for Network Acceleration to work properly. The certificate is unique, generated by your device, and stored locally on the device. Please save the certificate. The next step describes how to install it manually. - Save it now + Save it now + Install CA certificate How do I install the certificate? 1. Open [Security] settings diff --git a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-es/strings.xml b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-es/strings.xml index 48681a0c0..e589c85e3 100644 --- a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-es/strings.xml +++ b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-es/strings.xml @@ -8,7 +8,8 @@ Este certificado es necesario para que la Aceleración de Red funcione correctamente. El certificado es único, generado por su dispositivo y almacenado localmente en el mismo. Por favor, guarde el certificado. El siguiente paso describe cómo instalarlo manualmente. - Guárdalo ahora + Guárdalo ahora + Instalación de certificados CA ¿Cómo se instala un certificado? 1. Abrir la configuración de [Seguridad] diff --git a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-it/strings.xml b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-it/strings.xml index afd3589b6..07273a3e1 100644 --- a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-it/strings.xml +++ b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-it/strings.xml @@ -8,7 +8,8 @@ Questo certificato è necessario per far funzionare correttamente Network Acceleration. Il certificato è unico, generato dal tuo dispositivo e memorizzato localmente sul dispositivo. Si prega di salvare il certificato. Il prossimo passo descrive come installarlo manualmente. - Salvare ora + Salvare ora + Installazione di certificati CA Come si installa un certificato? 1. Aprire le impostazioni [Sicurezza]. diff --git a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-ja/strings.xml b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-ja/strings.xml index 81b8aac55..19ac419e4 100644 --- a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-ja/strings.xml +++ b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-ja/strings.xml @@ -8,7 +8,8 @@ この証明書は、ネットワークアクセラレーションが正常に動作するために必要です。 証明書はユニークで、デバイスによって生成され、デバイスにローカルに保存されます。 証明書を保存してください。 次のステップでは、手動でインストールする方法を説明します。 - 今すぐ保存する + 今すぐ保存する + CA 証明書のインストール 証明書をインストールするにはどうすればよいですか? 1. [セキュリティ]設定を開く diff --git a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-ko/strings.xml b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-ko/strings.xml index 6fff91b1b..b742de164 100644 --- a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-ko/strings.xml +++ b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-ko/strings.xml @@ -8,7 +8,8 @@ 네트워크가 정상적으로 작동하도록 하기 위해서 이 인증서를 설치해야 합니다. 인증서는 고유하며 디바이스에서 생성되어 로컬 디바이스에 저장됩니다. 인증서를 저장하십시오.다음 단계에서는 수동으로 설치하는 방법을 소개합니다. - 지금 저장 + 지금 저장 + CA 인증서 설치 인증서는 어떻게 설치합니까? 1. [안전] 설정 열기 diff --git a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-ru/strings.xml b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-ru/strings.xml index d667134b3..62c88e33b 100644 --- a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-ru/strings.xml +++ b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-ru/strings.xml @@ -8,7 +8,8 @@ Этот сертификат необходим для правильной работы Network Acceleration. Сертификат является уникальным, генерируется вашим устройством и хранится локально на устройстве. Пожалуйста, сохраните сертификат. Следующий шаг описывает, как установить его вручную. - Сохранить сейчас + Сохранить сейчас + Установка сертификатов ЦС Как установить сертификат? 1. Откройте настройки [Безопасность] diff --git a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-zh-rHK/strings.xml b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-zh-rHK/strings.xml index 5da58a79b..00c7ceebd 100644 --- a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-zh-rHK/strings.xml +++ b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values-zh-rHK/strings.xml @@ -8,7 +8,8 @@ 為了使 網絡加速 正常工作,需要安裝此證書。 該證書是唯一的,由您的設備生成,並存儲在本地設備上。 請保存證書。 下一步介紹如何手動安裝它。 - 現在保存它 + 現在保存它 + 安装 CA 證書 如何安裝證書? 1. 打開【安全】設置 diff --git a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values/strings.xml b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values/strings.xml index ba2d6b8be..93da2c723 100644 --- a/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values/strings.xml +++ b/src/ST.Client.Mobile.Droid.Design/ui/src/main/res/values/strings.xml @@ -17,7 +17,8 @@ 为了使 网络加速 正常工作,需要安装此证书。 该证书是唯一的,由您的设备生成,并存储在本地设备上。 请保存证书。下一步介绍如何手动安装它。 - 现在保存它 + 现在保存它 + 安装 CA 证书 如何安装证书? 1. 打开【安全】设置 diff --git a/src/ST.Client/Services/IPlatformService.Certificate.cs b/src/ST.Client/Services/IPlatformService.Certificate.cs new file mode 100644 index 000000000..115610902 --- /dev/null +++ b/src/ST.Client/Services/IPlatformService.Certificate.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Text; +using System.Security.Cryptography.X509Certificates; + +namespace System.Application.Services +{ + partial interface IPlatformService + { + bool IsCertificateInstalled(X509Certificate2 certificate2) + { + return true; + } + } +} diff --git a/src/ST.Client/Services/Implementation/HttpProxyServiceImpl.cs b/src/ST.Client/Services/Implementation/HttpProxyServiceImpl.cs index 3959e0698..9bc76052a 100644 --- a/src/ST.Client/Services/Implementation/HttpProxyServiceImpl.cs +++ b/src/ST.Client/Services/Implementation/HttpProxyServiceImpl.cs @@ -807,24 +807,36 @@ namespace System.Application.Services.Implementation { if (proxyServer.CertificateManager.RootCertificate == null) if (GetCerFilePathGeneratedWhenNoFileExists() == null) return false; - return IsCertificateInstalled(proxyServer.CertificateManager.RootCertificate); + return IsCertificateInstalled(proxyServer.CertificateManager.RootCertificate, usePlatformCheck: true); } } - public bool IsCertificateInstalled(X509Certificate2? certificate2) + public bool IsCertificateInstalled(X509Certificate2? certificate2) => IsCertificateInstalled(certificate2, false); + + public bool IsCertificateInstalled(X509Certificate2? certificate2, bool usePlatformCheck) { if (certificate2 == null) return false; if (certificate2.NotAfter <= DateTime.Now) return false; - if (OperatingSystem2.IsLinux) + if (!OperatingSystem2.IsAndroid && OperatingSystem2.IsLinux) { return true; } - using var store = new X509Store(OperatingSystem2.IsMacOS ? StoreName.My : StoreName.Root, StoreLocation.CurrentUser); - store.Open(OpenFlags.ReadOnly); - return store.Certificates.Contains(certificate2); + + bool result; + if (usePlatformCheck && OperatingSystem2.IsAndroid) + { + result = platformService.IsCertificateInstalled(certificate2); + } + else + { + using var store = new X509Store(OperatingSystem2.IsMacOS ? StoreName.My : StoreName.Root, StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadOnly); + result = store.Certificates.Contains(certificate2); + } + return result; } void Dispose(bool disposing)