Mac automatically installs certificates and checks trust

This commit is contained in:
wanghongenpin
2025-09-19 19:54:48 +08:00
parent f861beaa88
commit c5f3a7e036
3 changed files with 32 additions and 7 deletions

View File

@@ -7,6 +7,20 @@ import 'package:x509_cert_store/x509_cert_store.dart';
class CertInstaller {
static Future<bool> 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<bool> isCertInstalled(X509CertificateData caCert) async {
static Future<bool> 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<String> 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 = [

View File

@@ -17,8 +17,9 @@ class PCCert extends StatefulWidget {
}
class _PCCertState extends State<PCCert> 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<PCCert> 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

View File

@@ -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