feat: enhance certificate management with uninstall functionality

This commit is contained in:
Abolfazl
2026-04-28 21:23:58 +03:30
parent e8a4557edf
commit 84424e899d
5 changed files with 55 additions and 34 deletions
+1 -1
View File
@@ -232,7 +232,7 @@ Firefox uses its own certificate store, so even after OS-level install you need
> **Auto-install on startup:** When running in `apps_script` mode the proxy will automatically detect if the CA is not yet trusted and attempt to install it for you. If it succeeds you'll see a confirmation in the log; if it fails (e.g. needs administrator rights) it will print instructions. You can also run `python main.py --install-cert` at any time to (re-)install the certificate. > **Auto-install on startup:** When running in `apps_script` mode the proxy will automatically detect if the CA is not yet trusted and attempt to install it for you. If it succeeds you'll see a confirmation in the log; if it fails (e.g. needs administrator rights) it will print instructions. You can also run `python main.py --install-cert` at any time to (re-)install the certificate.
> **Uninstalling:** To remove the certificate from your system's trust stores, run `python main.py --uninstall-cert` or use `python start.bat --uninstall-cert` on Windows. This removes the certificate from all system trust stores and Firefox profiles. > **Uninstalling:** To remove the certificate from your system's trust stores, run `python main.py --uninstall-cert` or use `start.bat --uninstall-cert` on Windows. This removes the certificate from all system trust stores and Firefox profiles.
> ⚠️ **Security note:** This certificate only works locally on your machine. Don't share the `ca/` folder with anyone. If you want to start fresh, delete the `ca/` folder and the tool will generate a new one. > ⚠️ **Security note:** This certificate only works locally on your machine. Don't share the `ca/` folder with anyone. If you want to start fresh, delete the `ca/` folder and the tool will generate a new one.
+1 -1
View File
@@ -291,7 +291,7 @@ python3 main.py --disable-socks5
python3 main.py --log-level DEBUG python3 main.py --log-level DEBUG
python3 main.py -c /path/to/config.json python3 main.py -c /path/to/config.json
python3 main.py --install-cert # نصب گواهی CA و خروج python3 main.py --install-cert # نصب گواهی CA و خروج
python3 main.py --uninstall-cert # حذف گراهی CA و خروج python3 main.py --uninstall-cert # حذف گواهی CA و خروج
python3 main.py --no-cert-check # رد شدن از بررسی خودکار گواهی python3 main.py --no-cert-check # رد شدن از بررسی خودکار گواهی
python3 main.py --scan # اسکن IP های Google و یافتن سریع‌ترین python3 main.py --scan # اسکن IP های Google و یافتن سریع‌ترین
``` ```
+22 -18
View File
@@ -108,6 +108,28 @@ def parse_args():
def main(): def main():
args = parse_args() args = parse_args()
# Handle cert-only commands before loading config so they can run standalone.
if args.install_cert or args.uninstall_cert:
setup_logging("INFO")
_log = logging.getLogger("Main")
if args.install_cert:
_log.info("Installing CA certificate…")
if not os.path.exists(CA_CERT_FILE):
from mitm import MITMCertManager
MITMCertManager() # side-effect: creates ca/ca.crt + ca/ca.key
ok = install_ca(CA_CERT_FILE)
sys.exit(0 if ok else 1)
_log.info("Removing CA certificate…")
ok = uninstall_ca(CA_CERT_FILE)
if ok:
_log.info("CA certificate removed successfully.")
else:
_log.warning("CA certificate removal may have failed. Check logs above.")
sys.exit(0 if ok else 1)
config_path = args.config config_path = args.config
try: try:
@@ -195,24 +217,6 @@ def main():
print("Deploy the Apps Script from Code.gs and paste the Deployment ID.") print("Deploy the Apps Script from Code.gs and paste the Deployment ID.")
sys.exit(1) sys.exit(1)
# ── Certificate installation ──────────────────────────────────────────
if args.install_cert:
setup_logging("INFO")
_log = logging.getLogger("Main")
_log.info("Installing CA certificate…")
ok = install_ca(CA_CERT_FILE)
sys.exit(0 if ok else 1)
# ── Certificate uninstallation ───────────────────────────────────────────
if args.uninstall_cert:
setup_logging("INFO")
_log = logging.getLogger("Main")
_log.info("Removing CA certificate…")
ok = uninstall_ca(CA_CERT_FILE)
if ok:
_log.info("CA certificate removed successfully.")
else:
_log.warning("CA certificate removal may have failed. Check logs above.")
# ── Google IP Scanner ────────────────────────────────────────────────── # ── Google IP Scanner ──────────────────────────────────────────────────
if args.scan: if args.scan:
setup_logging("INFO") setup_logging("INFO")
+29 -13
View File
@@ -383,8 +383,11 @@ def _uninstall_firefox(cert_name: str):
for profile in profile_dirs: for profile in profile_dirs:
db = f"sql:{profile}" if os.path.exists(os.path.join(profile, "cert9.db")) else f"dbm:{profile}" db = f"sql:{profile}" if os.path.exists(os.path.join(profile, "cert9.db")) else f"dbm:{profile}"
try: try:
_run(["certutil", "-D", "-n", cert_name, "-d", db], check=False) result = _run(["certutil", "-D", "-n", cert_name, "-d", db], check=False)
log.info("Removed from Firefox profile: %s", os.path.basename(profile)) if result.returncode == 0:
log.info("Removed from Firefox profile: %s", os.path.basename(profile))
else:
log.debug("Firefox profile %s: certificate not present", os.path.basename(profile))
except (subprocess.CalledProcessError, FileNotFoundError) as exc: except (subprocess.CalledProcessError, FileNotFoundError) as exc:
log.debug("Firefox profile %s: %s", os.path.basename(profile), exc) log.debug("Firefox profile %s: %s", os.path.basename(profile), exc)
@@ -393,11 +396,14 @@ def _uninstall_firefox(cert_name: str):
# Uninstall functions # Uninstall functions
# ───────────────────────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────────────────────
def _uninstall_windows(cert_name: str) -> bool: def _uninstall_windows(cert_path: str, cert_name: str) -> bool:
"""Remove certificate from the Windows Trusted Root store.""" """Remove certificate from the Windows Trusted Root store."""
thumbprint = _cert_thumbprint(cert_path)
# Try per-user store first (no admin required) # Try per-user store first (no admin required)
try: try:
_run(["certutil", "-delstore", "-user", "Root", cert_name]) target = thumbprint if thumbprint else cert_name
_run(["certutil", "-delstore", "-user", "Root", target])
log.info("Certificate removed from Windows user Trusted Root store.") log.info("Certificate removed from Windows user Trusted Root store.")
return True return True
except (subprocess.CalledProcessError, FileNotFoundError) as exc: except (subprocess.CalledProcessError, FileNotFoundError) as exc:
@@ -405,7 +411,8 @@ def _uninstall_windows(cert_name: str) -> bool:
# Try system store (requires admin) # Try system store (requires admin)
try: try:
_run(["certutil", "-delstore", "Root", cert_name]) target = thumbprint if thumbprint else cert_name
_run(["certutil", "-delstore", "Root", target])
log.info("Certificate removed from Windows system Trusted Root store.") log.info("Certificate removed from Windows system Trusted Root store.")
return True return True
except (subprocess.CalledProcessError, FileNotFoundError) as exc: except (subprocess.CalledProcessError, FileNotFoundError) as exc:
@@ -413,11 +420,20 @@ def _uninstall_windows(cert_name: str) -> bool:
# Fallback: use PowerShell # Fallback: use PowerShell
try: try:
ps_cmd = ( if thumbprint:
f"Remove-Item -Path Cert:\\CurrentUser\\Root\\{cert_name} -Force -ErrorAction SilentlyContinue" ps_cmd = (
) "Get-ChildItem Cert:\\CurrentUser\\Root | "
_run(["powershell", "-NoProfile", "-Command", ps_cmd], check=False) f"Where-Object {{ $_.Thumbprint -eq '{thumbprint}' }} | "
log.info("Attempted certificate removal via PowerShell.") "Remove-Item -Force -ErrorAction SilentlyContinue"
)
else:
ps_cmd = (
"Get-ChildItem Cert:\\CurrentUser\\Root | "
f"Where-Object {{ $_.Subject -like '*CN={cert_name}*' -or $_.FriendlyName -eq '{cert_name}' }} | "
"Remove-Item -Force -ErrorAction SilentlyContinue"
)
_run(["powershell", "-NoProfile", "-Command", ps_cmd])
log.info("Certificate removal via PowerShell completed.")
return True return True
except (subprocess.CalledProcessError, FileNotFoundError) as exc: except (subprocess.CalledProcessError, FileNotFoundError) as exc:
log.error("PowerShell removal failed: %s", exc) log.error("PowerShell removal failed: %s", exc)
@@ -436,7 +452,7 @@ def _uninstall_macos(cert_name: str) -> bool:
"security", "delete-certificate", "security", "delete-certificate",
"-c", cert_name, "-c", cert_name,
login_keychain, login_keychain,
], check=False) ])
log.info("Certificate removed from macOS login keychain.") log.info("Certificate removed from macOS login keychain.")
return True return True
except (subprocess.CalledProcessError, FileNotFoundError) as exc: except (subprocess.CalledProcessError, FileNotFoundError) as exc:
@@ -448,7 +464,7 @@ def _uninstall_macos(cert_name: str) -> bool:
"sudo", "security", "delete-certificate", "sudo", "security", "delete-certificate",
"-c", cert_name, "-c", cert_name,
"/Library/Keychains/System.keychain", "/Library/Keychains/System.keychain",
], check=False) ])
log.info("Certificate removed from macOS system keychain.") log.info("Certificate removed from macOS system keychain.")
return True return True
except (subprocess.CalledProcessError, FileNotFoundError) as exc: except (subprocess.CalledProcessError, FileNotFoundError) as exc:
@@ -582,7 +598,7 @@ def uninstall_ca(cert_path: str, cert_name: str = CERT_NAME) -> bool:
log.info("Removing CA certificate from %s", system) log.info("Removing CA certificate from %s", system)
if system == "Windows": if system == "Windows":
ok = _uninstall_windows(cert_name) ok = _uninstall_windows(cert_path, cert_name)
elif system == "Darwin": elif system == "Darwin":
ok = _uninstall_macos(cert_name) ok = _uninstall_macos(cert_name)
elif system == "Linux": elif system == "Linux":
+2 -1
View File
@@ -65,7 +65,8 @@ if not exist "config.json" (
) )
REM -------- Check for uninstall flag -------- REM -------- Check for uninstall flag --------
if "%~1"=="--uninstall-cert" ( echo %* | findstr /C:"--uninstall-cert" >nul
if not errorlevel 1 (
echo [*] Uninstalling CA certificate ... echo [*] Uninstalling CA certificate ...
"%VPY%" main.py --uninstall-cert "%VPY%" main.py --uninstall-cert
exit /b %errorlevel% exit /b %errorlevel%