Download Let's Encrypt CA-Certificates

This commit is contained in:
Frank Agerholm 2026-01-18 19:11:46 +01:00
commit 23651e0e2e
No known key found for this signature in database
2 changed files with 117 additions and 0 deletions

109
bin/fetch_letsencrypt_ca_certs.py Executable file
View file

@ -0,0 +1,109 @@
#!/usr/bin/env python3
import os
import sys
import requests
from html.parser import HTMLParser
from urllib.parse import urljoin
LETSENCRYPT_CERT_PAGE = "https://letsencrypt.org/certificates/"
DEFAULT_TARGET_DIR = "./letsencrypt-ca"
class PemLinkParser(HTMLParser):
def __init__(self):
super().__init__()
self.pem_links = set()
def handle_starttag(self, tag, attrs):
if tag.lower() != "a":
return
for attr, value in attrs:
if attr == "href" and value.lower().endswith(".pem"):
self.pem_links.add(value)
def fetch_certificate_page():
resp = requests.get(LETSENCRYPT_CERT_PAGE, timeout=15)
resp.raise_for_status()
return resp.text
def extract_pem_links(html):
parser = PemLinkParser()
parser.feed(html)
return sorted(
urljoin(LETSENCRYPT_CERT_PAGE, link)
for link in parser.pem_links
)
def download_and_store(url, target_dir):
base_name = os.path.basename(url)
# Zielname = .crt statt .pem
target_name = os.path.splitext(base_name)[0] + ".crt"
target_path = os.path.join(target_dir, target_name)
if os.path.exists(target_path):
print(f"[=] Bereits vorhanden: {target_name}")
return target_name
print(f"[+] Lade herunter: {base_name}")
resp = requests.get(url, timeout=15)
resp.raise_for_status()
if b"BEGIN CERTIFICATE" not in resp.content:
print(f"[!] Ungültiges Zertifikat: {base_name}")
return None
# Direkt als .crt speichern (PEM-Inhalt)
with open(target_path, "wb") as f:
f.write(resp.content)
return target_name
def list_local_crt_files(target_dir):
return {
f for f in os.listdir(target_dir)
if f.lower().endswith(".crt")
}
def main():
target_dir = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_TARGET_DIR
os.makedirs(target_dir, exist_ok=True)
print(f"[*] Zielverzeichnis: {os.path.abspath(target_dir)}")
print("[*] Format: PEM-Inhalt mit .crt-Endung")
html = fetch_certificate_page()
pem_urls = extract_pem_links(html)
# Remote-Dateinamen im .crt-Format
remote_files = {
os.path.splitext(os.path.basename(url))[0] + ".crt"
for url in pem_urls
}
for url in pem_urls:
download_and_store(url, target_dir)
local_files = list_local_crt_files(target_dir)
orphaned = sorted(local_files - remote_files)
print("\n" + "=" * 60)
if orphaned:
print("[!] Lokal vorhanden, aber nicht mehr gelistet:")
for f in orphaned:
print(f" - {f}")
else:
print("[✓] Keine veralteten Dateien gefunden")
print("=" * 60)
print("[✓] Fertig.")
if __name__ == "__main__":
main()