Download Let's Encrypt CA-Certificates
This commit is contained in:
parent
8c13a92fa3
commit
23651e0e2e
2 changed files with 117 additions and 0 deletions
109
bin/fetch_letsencrypt_ca_certs.py
Executable file
109
bin/fetch_letsencrypt_ca_certs.py
Executable 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()
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue