diff --git a/lib/ui/desktop/ssl/cert_installer.dart b/lib/ui/desktop/ssl/cert_installer.dart index 34d4223..142443d 100644 --- a/lib/ui/desktop/ssl/cert_installer.dart +++ b/lib/ui/desktop/ssl/cert_installer.dart @@ -7,6 +7,20 @@ import 'package:x509_cert_store/x509_cert_store.dart'; class CertInstaller { static Future installCertificate(File certFile) async { try { + if (Platform.isMacOS) { + // 使用 security add-trusted-cert 安装证书到登录钥匙串并设为信任根 + final result = await Process.run('security', [ + 'add-trusted-cert', + '-r', + 'trustRoot', + '-k', + '${Platform.environment['HOME']}/Library/Keychains/login.keychain-db', + certFile.path, + ]); + logger.d('security add-trusted-cert result: ${result.stdout} ${result.stderr}'); + return result.exitCode == 0; + } + // Read the certificate file and encode it to Base64 final certBytes = await certFile.readAsBytes(); final certificateBase64 = base64.encode(certBytes); @@ -22,7 +36,7 @@ class CertInstaller { setTrusted: Platform.isMacOS, // Mark the certificate as trusted ); - logger.d('Certificate successfully installed to the trusted root store. Result: ${result.code} $result'); + 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'); @@ -31,9 +45,10 @@ class CertInstaller { } /// 检查证书是否已安装 - static Future isCertInstalled(X509CertificateData caCert) async { + static Future isCertInstalled(File filePath, X509CertificateData caCert) async { String commonName = caCert.subject['2.5.4.3'] ?? 'ProxyPin CA'; String? sha1 = caCert.sha1Thumbprint; + logger.d('Checking if certificate is installed: CN=$commonName, SHA1=$sha1'); try { if (Platform.isWindows) { List args = ['-user', '-store', 'root']; @@ -43,8 +58,16 @@ class CertInstaller { 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; + var res = await Process.run('security', ['find-certificate', '-c', commonName]); + + if ((res.stdout as String).isNotEmpty) { + // check if trusted + var trustRes = await Process.run('security', ['verify-cert', '-c', filePath.path]); + + logger.d('security verify-cert $commonName result: ${trustRes.stdout} ${trustRes.stderr}'); + return (trustRes.stdout as String).contains('certificate verification successful'); + } + return false; } else if (Platform.isLinux) { // check common locations var paths = [ diff --git a/lib/ui/desktop/ssl/pc_cert.dart b/lib/ui/desktop/ssl/pc_cert.dart index bf33363..4c96553 100644 --- a/lib/ui/desktop/ssl/pc_cert.dart +++ b/lib/ui/desktop/ssl/pc_cert.dart @@ -17,8 +17,9 @@ class PCCert extends StatefulWidget { } class _PCCertState extends State with TickerProviderStateMixin { + static final RxnBool isCertInstalled = RxnBool(null); + late TabController _tabController; - final RxnBool isCertInstalled = RxnBool(true); X509CertificateData? certDetails; @override @@ -37,7 +38,8 @@ class _PCCertState extends State with TickerProviderStateMixin { void _checkCertStatus() async { final details = certDetails ?? await CertificateManager.getCertificateDetails(); - isCertInstalled.value = await CertInstaller.isCertInstalled(details); + final caFile = await CertificateManager.certificateFile(); + isCertInstalled.value = await CertInstaller.isCertInstalled(caFile, details); } @override diff --git a/linux/build.sh b/linux/build.sh index 427bc4d..2f27f1b 100644 --- a/linux/build.sh +++ b/linux/build.sh @@ -5,7 +5,7 @@ cd ../build/linux/x64/release rm -rf package mkdir -p package/DEBIAN echo "Package: ProxyPin" >> package/DEBIAN/control -echo "Version: 1.1.9" >> package/DEBIAN/control +echo "Version: 1.2.1" >> package/DEBIAN/control echo "Priority: optional" >> package/DEBIAN/control echo "Architecture: amd64" >> package/DEBIAN/control echo "Depends: ca-certificates" >> package/DEBIAN/control