From f861beaa88de7163bc6717f34f4dd15eb682ca6a Mon Sep 17 00:00:00 2001 From: wanghongenpin Date: Thu, 18 Sep 2025 23:55:23 +0800 Subject: [PATCH] Windows Automatic Install Certificate --- lib/l10n/app_en.arb | 5 +- lib/l10n/app_localizations.dart | 12 + lib/l10n/app_localizations_en.dart | 6 + lib/l10n/app_localizations_zh.dart | 6 + lib/l10n/app_zh.arb | 5 +- lib/network/util/cert/cert_data.dart | 1 - lib/network/util/crts.dart | 31 +- lib/ui/desktop/ssl/cert_installer.dart | 62 ++++ lib/ui/desktop/ssl/pc_cert.dart | 274 ++++++++++++++++++ lib/ui/desktop/ssl/ssl.dart | 67 +---- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 14 files changed, 399 insertions(+), 77 deletions(-) create mode 100644 lib/ui/desktop/ssl/cert_installer.dart create mode 100644 lib/ui/desktop/ssl/pc_cert.dart diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 337bc8f..0e25768 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -336,5 +336,8 @@ "appUpdateIgnoreBtnTxt": "Ignore", "requestMap": "Request Map", - "requestMapDescribe": "Do not request remote services, use local configuration or script for response" + "requestMapDescribe": "Do not request remote services, use local configuration or script for response", + + "automatic": "Automatic", + "manual": "Manual" } \ No newline at end of file diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 339cf07..c6e3472 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1979,6 +1979,18 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Do not request remote services, use local configuration or script for response'** String get requestMapDescribe; + + /// No description provided for @automatic. + /// + /// In en, this message translates to: + /// **'Automatic'** + String get automatic; + + /// No description provided for @manual. + /// + /// In en, this message translates to: + /// **'Manual'** + String get manual; } class _AppLocalizationsDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index e531eef..a4e08c0 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -978,4 +978,10 @@ class AppLocalizationsEn extends AppLocalizations { @override String get requestMapDescribe => 'Do not request remote services, use local configuration or script for response'; + + @override + String get automatic => 'Automatic'; + + @override + String get manual => 'Manual'; } diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 3d54b4b..2a4eb18 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -966,6 +966,12 @@ class AppLocalizationsZh extends AppLocalizations { @override String get requestMapDescribe => '不请求远程服务,使用本地配置或脚本进行响应'; + + @override + String get automatic => '自动安装'; + + @override + String get manual => '手动安装'; } /// The translations for Chinese, using the Han script (`zh_Hant`). diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index e839b45..6e6c1a2 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -335,5 +335,8 @@ "appUpdateIgnoreBtnTxt": "忽略", "requestMap": "请求映射", - "requestMapDescribe": "不请求远程服务,使用本地配置或脚本进行响应" + "requestMapDescribe": "不请求远程服务,使用本地配置或脚本进行响应", + + "automatic": "自动安装", + "manual": "手动安装" } \ No newline at end of file diff --git a/lib/network/util/cert/cert_data.dart b/lib/network/util/cert/cert_data.dart index 655805f..f93c5b0 100644 --- a/lib/network/util/cert/cert_data.dart +++ b/lib/network/util/cert/cert_data.dart @@ -21,7 +21,6 @@ class X509CertificateData { Map issuer; /// The validity of the certificate - @Deprecated('Use tbsCertificate.validity instead') X509CertificateValidity validity; /// The sha1 thumbprint for the certificate diff --git a/lib/network/util/crts.dart b/lib/network/util/crts.dart index 29eb26f..5c24e86 100644 --- a/lib/network/util/crts.dart +++ b/lib/network/util/crts.dart @@ -19,11 +19,8 @@ import 'dart:core'; import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; - import 'package:path_provider/path_provider.dart'; -import 'package:pointycastle/api.dart'; -import 'package:pointycastle/asymmetric/api.dart'; -import 'package:proxypin/network/util/cert/basic_constraints.dart'; +import 'package:pointycastle/export.dart'; import 'package:proxypin/network/util/cert/pkcs12.dart'; import 'package:proxypin/network/util/cert/x509.dart'; import 'package:proxypin/network/util/logger.dart'; @@ -31,6 +28,7 @@ import 'package:proxypin/network/util/random.dart'; import 'package:proxypin/utils/lang.dart'; import 'cache.dart'; +import 'cert/basic_constraints.dart'; import 'cert/cert_data.dart'; import 'cert/extension.dart'; import 'cert/key_usage.dart'; @@ -39,7 +37,6 @@ import 'file_read.dart'; Future main() async { await CertificateManager.getCertificateContext('www.jianshu.com'); - CertificateManager.caCert.tbsCertificateSeqAsString; } enum StartState { uninitialized, initializing, initialized } @@ -53,7 +50,7 @@ class CertificateManager { static AsymmetricKeyPair _serverKeyPair = CryptoUtils.generateRSAKeyPair(); /// ca证书 - static late X509CertificateData _caCert; + static X509CertificateData? _caCert; /// ca私钥 static late RSAPrivateKey _caPriKey; @@ -66,7 +63,7 @@ class CertificateManager { return _certificateMap[host]; } - static X509CertificateData get caCert => _caCert; + static X509CertificateData? get caCert => _caCert; /// 清除缓存 static void cleanCache() { @@ -84,7 +81,7 @@ class CertificateManager { await initCAConfig(); } - String cer = generate(_caCert, _serverKeyPair.publicKey as RSAPublicKey, _caPriKey, host); + String cer = generate(_caCert!, _serverKeyPair.publicKey as RSAPublicKey, _caPriKey, host); var rsaPrivateKey = _serverKeyPair.privateKey as RSAPrivateKey; @@ -122,7 +119,7 @@ class CertificateManager { await initCAConfig(); } - var subject = caCert.subject; + var subject = caCert!.subject; return '${X509Utils.getSubjectHashName(subject)}.0'; } @@ -147,7 +144,7 @@ class CertificateManager { x509Subject['CN'] = 'ProxyPin CA (${DateTime.now().dateFormat()},${RandomUtil.randomString(6).toUpperCase()})'; var csrPem = X509Utils.generateSelfSignedCertificate( - _caCert, + _caCert!, serverPubKey, serverPriKey, 825, @@ -230,6 +227,12 @@ class CertificateManager { return caFile; } + ///证书pem格式内容 + static Future certificatePem() async { + var caFile = await certificateFile(); + return caFile.readAsString(); + } + /// 私钥文件 static Future privateKeyFile() async { final String appPath = await getApplicationSupportDirectory().then((value) => value.path); @@ -265,4 +268,12 @@ class CertificateManager { cleanCache(); _state = StartState.uninitialized; } + + /// 获取证书详细信息 + static Future getCertificateDetails() async { + if (_state != StartState.initialized) { + await initCAConfig(); + } + return caCert!; + } } diff --git a/lib/ui/desktop/ssl/cert_installer.dart b/lib/ui/desktop/ssl/cert_installer.dart new file mode 100644 index 0000000..34d4223 --- /dev/null +++ b/lib/ui/desktop/ssl/cert_installer.dart @@ -0,0 +1,62 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:proxypin/network/util/cert/cert_data.dart'; +import 'package:proxypin/network/util/logger.dart'; +import 'package:x509_cert_store/x509_cert_store.dart'; + +class CertInstaller { + static Future installCertificate(File certFile) async { + try { + // Read the certificate file and encode it to Base64 + final certBytes = await certFile.readAsBytes(); + final certificateBase64 = base64.encode(certBytes); + + // Initialize the X509CertStore plugin + final x509CertStorePlugin = X509CertStore(); + + // Add the certificate to the trusted root store + final result = await x509CertStorePlugin.addCertificate( + storeName: X509StoreName.root, // Add to the trusted root store + certificateBase64: certificateBase64, // Base64-encoded certificate + addType: X509AddType.addNewer, // Replace if it already exists + setTrusted: Platform.isMacOS, // Mark the certificate as trusted + ); + + logger.d('Certificate successfully installed to the trusted root store. Result: ${result.code} $result'); + return result.isOk || result.code == X509ErrorCode.alreadyExist.getString(); + } catch (e) { + logger.e('Failed to install certificate: $e'); + return false; + } + } + + /// 检查证书是否已安装 + static Future isCertInstalled(X509CertificateData caCert) async { + String commonName = caCert.subject['2.5.4.3'] ?? 'ProxyPin CA'; + String? sha1 = caCert.sha1Thumbprint; + try { + if (Platform.isWindows) { + List args = ['-user', '-store', 'root']; + if (sha1 != null) { + args.add(sha1); + } + var res = await Process.run('certutil', args); + return res.stdout.toString().toLowerCase().contains(commonName.toLowerCase()); + } else if (Platform.isMacOS) { + var res = await Process.run('security', ['find-certificate', '-c', commonName, '-a']); + return (res.stdout as String).isNotEmpty; + } else if (Platform.isLinux) { + // check common locations + var paths = [ + '/usr/local/share/ca-certificates/$commonName.crt', + '/etc/ssl/certs/$commonName.crt', + ]; + for (var p in paths) if (await File(p).exists()) return true; + // fallback: search /etc/ssl/certs for subject text + var res = await Process.run('grep', ['-i', commonName, '-R', '/etc/ssl/certs']); + return (res.stdout as String).isNotEmpty; + } + } catch (_) {} + return false; + } +} diff --git a/lib/ui/desktop/ssl/pc_cert.dart b/lib/ui/desktop/ssl/pc_cert.dart new file mode 100644 index 0000000..bf33363 --- /dev/null +++ b/lib/ui/desktop/ssl/pc_cert.dart @@ -0,0 +1,274 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:proxypin/l10n/app_localizations.dart'; +import 'package:proxypin/network/util/cert/cert_data.dart'; +import 'package:proxypin/network/util/crts.dart'; +import 'package:proxypin/ui/component/app_dialog.dart'; +import 'package:proxypin/ui/desktop/ssl/cert_installer.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class PCCert extends StatefulWidget { + const PCCert({super.key}); + + @override + State createState() => _PCCertState(); +} + +class _PCCertState extends State with TickerProviderStateMixin { + late TabController _tabController; + final RxnBool isCertInstalled = RxnBool(true); + X509CertificateData? certDetails; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 2, vsync: this); + certDetails = CertificateManager.caCert; + _checkCertStatus(); + + if (certDetails == null) { + CertificateManager.getCertificateDetails().then((value) => setState(() { + certDetails = value; + })); + } + } + + void _checkCertStatus() async { + final details = certDetails ?? await CertificateManager.getCertificateDetails(); + isCertInstalled.value = await CertInstaller.isCertInstalled(details); + } + + @override + Widget build(BuildContext context) { + final localizations = AppLocalizations.of(context)!; + final isCN = Localizations.localeOf(context) == const Locale.fromSubtags(languageCode: 'zh'); + + return SimpleDialog( + titlePadding: const EdgeInsets.symmetric(), + contentPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 15), + title: Row(children: [ + const Expanded(child: SizedBox()), + Text(isCN ? "安装证书" : "Install Certificate", style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500)), + const Expanded(child: SizedBox()), + Align(alignment: Alignment.topRight, child: CloseButton()) + ]), + children: [ + TabBar( + controller: _tabController, + tabs: [ + Tab(text: localizations.automatic), + Tab(text: localizations.manual), + ], + ), + SizedBox( + width: 700, + height: 470, + child: TabBarView( + controller: _tabController, + children: [ + _buildAutomaticTab(context), + _buildManualTab(context), + ], + ), + ), + ], + ); + } + + Widget _buildAutomaticTab(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 16.0), + child: Obx(() => Column(children: buildAutomaticChildren())), + ); + } + + List buildAutomaticChildren() { + final localizations = AppLocalizations.of(context)!; + final isCN = Localizations.localeOf(context) == const Locale.fromSubtags(languageCode: 'zh'); + + final subtitleStyle = Theme.of(context).textTheme.bodyMedium; + final infoLabelStyle = Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.grey[600]); + final infoValueStyle = Theme.of(context).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w500); + List children = [ + const SizedBox(height: 8), + Text(isCN ? "通过安装并信任 ProxyPin CA" : "Install and Trust ProxyPin CA Certificate", + style: subtitleStyle, textAlign: TextAlign.center), + const SizedBox(height: 3), + Text( + isCN + ? "ProxyPin 可以动态解密 HTTPS 流量以展示原始请求/响应。" + : "ProxyPin can decrypt encrypted traffic on the fly and enable to see raw HTTPS requests and responses.", + style: subtitleStyle, + textAlign: TextAlign.center), + const SizedBox(height: 45), + ]; + + if (isCertInstalled.value == false) { + children.add(const SizedBox(height: 20)); + children.add(Icon(Icons.error_outline, color: Colors.red, size: 56)); + children.add(const SizedBox(height: 12)); + children.add(Text(isCN ? '证书未安装' : 'Certificate Not Installed', + textAlign: TextAlign.center, style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600))); + children.add(const SizedBox(height: 20)); + children.add( + FilledButton( + onPressed: _installCert, + style: FilledButton.styleFrom( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + padding: const EdgeInsets.symmetric(horizontal: 60, vertical: 19)), + child: Text(localizations.install)), + ); + } else if (isCertInstalled.value == true) { + children.add(Card( + elevation: 2, + color: Theme.brightnessOf(context) == Brightness.light ? Colors.grey[50] : Colors.grey[800], + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 18.0), + child: Column(children: [ + Icon(Icons.verified_rounded, color: Colors.green, size: 56), + const SizedBox(height: 12), + Text(isCN ? "证书已安装" : "Certificate Installed", style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600)), + const SizedBox(height: 8), + if (certDetails != null) ...[ + const Divider(), + const SizedBox(height: 8), + // certificate details + Row(children: [ + Text('Name', style: infoLabelStyle), + Expanded( + child: SelectableText(certDetails!.subject['2.5.4.3'] ?? 'ProxyPin CA', + style: infoValueStyle, textAlign: TextAlign.right)), + ]), + const SizedBox(height: 6), + Row(children: [ + Text('Expires', style: infoLabelStyle), + Expanded( + child: SelectableText(certDetails!.validity.notAfter.toLocal().toString().split(' ').first, + style: infoValueStyle, textAlign: TextAlign.right)), + ]), + const SizedBox(height: 6), + Row(children: [ + Text('Fingerprint', style: infoLabelStyle), + Expanded( + child: SelectableText(certDetails!.sha1Thumbprint ?? '-', + style: infoValueStyle, textAlign: TextAlign.right), + ), + ]) + ] + ]), + ), + )); + } + + return children; + } + + Widget _buildManualTab(BuildContext context) { + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.only(top: 12.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: _buildChildren(context), + )), + ); + } + + List _buildChildren(BuildContext context) { + if (Platform.isMacOS || Platform.isWindows) { + return _buildWindowsAndMacContent(context); + } + return _buildLinuxContent(context); + } + + List _buildWindowsAndMacContent(BuildContext context) { + final localizations = AppLocalizations.of(context)!; + final isCN = Localizations.localeOf(context) == const Locale.fromSubtags(languageCode: 'zh'); + + return [ + isCN + ? Text(" 安装证书到本系统,${Platform.isMacOS ? "安装完双击选择“始终信任此证书”。 如安装打开失败,请导出证书拖拽到系统证书里" : "选择“受信任的根证书颁发机构”"}") + : Text( + " Install certificate to this system,${Platform.isMacOS ? "After installation, double-click to select “Always Trust”。\n If installation and opening fail,Please export the certificate and drag it to the system certificate" : "choice“Trusted Root Certificate Authority”"}"), + const SizedBox(height: 10), + SizedBox( + width: double.maxFinite, + child: FilledButton( + onPressed: () => _manualInstallCert(), + style: FilledButton.styleFrom(shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))), + child: Text(localizations.installRootCa))), + const SizedBox(height: 10), + Platform.isMacOS + ? Image.network("https://foruda.gitee.com/images/1689323260158189316/c2d881a4_1073801.png", + width: 800, height: 500) + : Row(children: [ + Image.network("https://foruda.gitee.com/images/1689335589122168223/c904a543_1073801.png", + width: 370, height: 380), + const SizedBox(width: 10), + Image.network("https://foruda.gitee.com/images/1689335334688878324/f6aa3a3a_1073801.png", + width: 370, height: 380) + ]) + ]; + } + + List _buildLinuxContent(BuildContext context) { + final isCN = Localizations.localeOf(context) == const Locale.fromSubtags(languageCode: 'zh'); + + return [ + Text(isCN + ? "安装证书到本系统,以Ubuntu为例 下载证书:\n" + "先把证书复制到 /usr/local/share/ca-certificates/,然后执行 update-ca-certificates 即可。\n" + "其他系统请网上搜索安装根证书" + : "Install the certificate to this system), take Ubuntu as an example to download the certificate:\n" + "First copy the certificate to /usr/local/share/ca-certificates/, and then execute update-ca-certificates.\n" + "For other systems, please search online for installing root certificates."), + const SizedBox(height: 5), + Text( + isCN + ? "提示:FireFox有自己的信任证书库,所以要手动在设置中导入需要导入的证书。" + : "Note: FireFox has its own trusted certificate library, so you need to manually import the required certificates in the settings.", + style: TextStyle(fontSize: 12)), + const SizedBox(height: 10), + const SelectableText.rich( + textAlign: TextAlign.justify, + TextSpan(style: TextStyle(color: Color(0xff6a8759)), children: [ + TextSpan(text: " sudo cp ProxyPinCA.crt /usr/local/share/ca-certificates/ \n"), + TextSpan(text: " sudo update-ca-certificates") + ])), + const SizedBox(height: 10) + ]; + } + + void _installCert() async { + final isCN = Localizations.localeOf(context) == const Locale.fromSubtags(languageCode: 'zh'); + var caFile = await CertificateManager.certificateFile(); + bool success = await CertInstaller.installCertificate(caFile); + CertificateManager.cleanCache(); + + if (!mounted) { + return; + } + + if (success) { + isCertInstalled.value = true; + CustomToast.success(isCN ? "证书安装成功" : "Certificate installed successfully").show(context); + } else { + isCertInstalled.value = false; + final isCN = Localizations.localeOf(context) == const Locale.fromSubtags(languageCode: 'zh'); + CustomToast.error(isCN ? "证书安装失败,请尝试手动安装" : "Certificate installation failed, please try manual installation") + .show(context); + } + } + + void _manualInstallCert() async { + var caFile = await CertificateManager.certificateFile(); + launchUrl(Uri.file(caFile.path)).then((_) { + CertificateManager.cleanCache(); + isCertInstalled.value = null; + }); + } +} diff --git a/lib/ui/desktop/ssl/ssl.dart b/lib/ui/desktop/ssl/ssl.dart index 73ab80e..5c21a6b 100644 --- a/lib/ui/desktop/ssl/ssl.dart +++ b/lib/ui/desktop/ssl/ssl.dart @@ -8,6 +8,7 @@ import 'package:proxypin/network/bin/server.dart'; import 'package:proxypin/network/util/crts.dart'; import 'package:proxypin/network/util/logger.dart'; import 'package:proxypin/ui/component/utils.dart'; +import 'package:proxypin/ui/desktop/ssl/pc_cert.dart'; import 'package:proxypin/utils/ip.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -42,7 +43,8 @@ class _SslState extends State { }, menuChildren: [ _Switch(proxyServer: widget.proxyServer, onEnableChange: (val) => setState(() {})), - item(localizations.installCaLocal, onPressed: pcCer), + item(localizations.installCaLocal, + onPressed: () => showDialog(context: context, builder: (context) => PCCert())), item("${localizations.installRootCa} iOS", onPressed: () async => iosCer(await localIp())), item("${localizations.installRootCa} Android", onPressed: () async => androidCer(await localIp())), const Divider(thickness: 0.3, height: 3), @@ -202,64 +204,6 @@ class _SslState extends State { child: Text(text, style: const TextStyle(fontSize: 14)))); } - void pcCer() async { - bool isCN = Localizations.localeOf(context) == const Locale.fromSubtags(languageCode: 'zh'); - - List list = []; - if (Platform.isMacOS || Platform.isWindows) { - list = [ - isCN - ? Text(" 安装证书到本系统,${Platform.isMacOS ? "安装完双击选择“始终信任此证书”。 如安装打开失败,请导出证书拖拽到系统证书里" : "选择“受信任的根证书颁发机构”"}") - : Text(" Install certificate to this system,${Platform.isMacOS ? "After installation, double-click to select “Always Trust”。\n" - " If installation and opening fail,Please export the certificate and drag it to the system certificate" : "choice“Trusted Root Certificate Authority”"}"), - const SizedBox(height: 10), - FilledButton(onPressed: _installCert, child: Text(localizations.installRootCa)), - const SizedBox(height: 10), - Platform.isMacOS - ? Image.network("https://foruda.gitee.com/images/1689323260158189316/c2d881a4_1073801.png", - width: 800, height: 500) - : Row(children: [ - Image.network("https://foruda.gitee.com/images/1689335589122168223/c904a543_1073801.png", - width: 400, height: 400), - const SizedBox(width: 10), - Image.network("https://foruda.gitee.com/images/1689335334688878324/f6aa3a3a_1073801.png", - width: 400, height: 400) - ]) - ]; - } else { - list.add(const Text("安装证书到本系统,以Ubuntu为例 下载证书:\n" - "先把证书复制到 /usr/local/share/ca-certificates/,然后执行 update-ca-certificates 即可。\n" - "其他系统请网上搜索安装根证书")); - list.add(const SizedBox(height: 5)); - list.add(const Text("提示:FireFox有自己的信任证书库,所以要手动在设置中导入需要导入的证书。", style: TextStyle(fontSize: 12))); - list.add(const SizedBox(height: 10)); - list.add(const SelectableText.rich( - textAlign: TextAlign.justify, - TextSpan(style: TextStyle(color: Color(0xff6a8759)), children: [ - TextSpan(text: " sudo cp ProxyPinCA.crt /usr/local/share/ca-certificates/ \n"), - TextSpan(text: " sudo update-ca-certificates") - ]))); - list.add(const SizedBox(height: 10)); - } - - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return SimpleDialog( - contentPadding: const EdgeInsets.symmetric(vertical: 5, horizontal: 15), - title: Row(children: [ - const Expanded(child: SizedBox()), - Text(isCN ? "电脑HTTPS抓包配置" : "Computer HTTPS Packet Capture Configuration", - style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500)), - const Expanded(child: SizedBox()), - Align(alignment: Alignment.topRight, child: CloseButton()) - ]), - alignment: Alignment.center, - children: list); - }); - } - void iosCer(String host) { showDialog( context: context, @@ -402,11 +346,6 @@ class _SslState extends State { )))); }); } - - void _installCert() async { - var caFile = await CertificateManager.certificateFile(); - launchUrl(Uri.file(caFile.path)).then((value) => CertificateManager.cleanCache()); - } } class _Switch extends StatefulWidget { diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 0d1ddd6..b0d8780 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -18,6 +18,7 @@ import share_plus import shared_preferences_foundation import url_launcher_macos import window_manager +import x509_cert_store import zstandard_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { @@ -34,5 +35,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) + X509CertStorePlugin.register(with: registry.registrar(forPlugin: "X509CertStorePlugin")) ZstandardMacosPlugin.register(with: registry.registrar(forPlugin: "ZstandardMacosPlugin")) } diff --git a/pubspec.yaml b/pubspec.yaml index fb61145..a2ceeb9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,6 +47,7 @@ dependencies: macos_window_utils: ^1.9.0 win32audio: ^1.3.1 vclibs: ^0.1.3 + x509_cert_store: ^1.2.1 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 76364a6..0ec1f52 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { @@ -42,6 +43,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("Win32audioPluginCApi")); WindowManagerPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("WindowManagerPlugin")); + X509CertStorePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("X509CertStorePluginCApi")); ZstandardWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("ZstandardWindowsPluginCApi")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 655bc53..7a8b039 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -14,6 +14,7 @@ list(APPEND FLUTTER_PLUGIN_LIST vclibs win32audio window_manager + x509_cert_store zstandard_windows )