""" Přihlášení na portál ČPZP pomocí certifikátu. Flow: 1. GET /app/login/ → session cookie + csrfCert token + challenge (certificateLoginKey) 2. Podepiš challenge certifikátem (PKCS7 detached, RSA + SHA-256) 3. POST /app/ s form-data: csrfCert=&sign= 4. Ulož cookies do cpzp_cookies.json POUŽITÍ: pip install requests cryptography beautifulsoup4 python 01_prihlaseni.py """ import base64 import json import os import re import requests from bs4 import BeautifulSoup from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.serialization import pkcs7, pkcs12 def _parse_js_str(js_expr: str) -> str: """Převede JS string expression ('a' + String.fromCharCode(13,10) + 'b') na Python string.""" parts = re.findall(r"'([^']*)'|String\.fromCharCode\(([\d,\s]+)\)", js_expr) result = [] for str_part, chars_part in parts: if str_part is not None and (str_part or not chars_part): result.append(str_part) elif chars_part: result.append("".join(chr(int(c.strip())) for c in chars_part.split(","))) return "".join(result) PFX_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../Certificates/MBQualifiedCert.pfx")) PFX_PASSWORD = b"Vlado7309208104++" COOKIES_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "cpzp_cookies.json")) BASE_URL = "https://portal.cpzp.cz" LOGIN_URL = f"{BASE_URL}/app/login/" POST_URL = f"{BASE_URL}/app/" def main() -> None: session = requests.Session() session.headers.update({ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Origin": BASE_URL, "Referer": LOGIN_URL, }) # 1. Načti login stránku → session cookie + HTML s tokenem a challengem r = session.get(LOGIN_URL) r.raise_for_status() soup = BeautifulSoup(r.content, "html.parser", from_encoding="utf-8") # Extrahuj csrfCert z formuláře frmPrihlasCert csrf_input = soup.find("form", {"name": "frmPrihlasCert"}) if not csrf_input: raise RuntimeError("Formulář frmPrihlasCert nenalezen") csrf_cert = csrf_input.find("input", {"name": "csrfCert"})["value"] # Extrahuj challenge z JS — formát: 'část'+ String.fromCharCode(13, 10) +'část' html = r.content.decode("utf-8") # vynutit UTF-8 match = re.search(r"certificateLoginKey\s*:\s*(.+?)(?=,\s*\n|\n\s*maxHeight)", html, re.DOTALL) if not match: raise RuntimeError("certificateLoginKey nenalezen v HTML") challenge = _parse_js_str(match.group(1)) print(f"Challenge: {challenge[:80]}...") # 2. Podepis certifikátem (PKCS7 detached, RSA + SHA-256, bez CA řetězu) with open(PFX_PATH, "rb") as f: private_key, cert, _ = pkcs12.load_key_and_certificates(f.read(), PFX_PASSWORD) pem_podpis = ( pkcs7.PKCS7SignatureBuilder() .set_data(challenge.encode("utf-8")) .add_signer(cert, private_key, hashes.SHA256()) .sign(serialization.Encoding.PEM, [pkcs7.PKCS7Options.DetachedSignature]) ) podpis_pem = pem_podpis.decode("ascii").strip() # 3. Přihlas se (POST form-data) r = session.post(POST_URL, data={ "csrfCert": csrf_cert, "sign": podpis_pem, }, headers={ "Content-Type": "application/x-www-form-urlencoded", "Referer": LOGIN_URL, }) r.raise_for_status() # Ověř přihlášení — po úspěchu je v URL /app/ a stránka neobsahuje login formu if "frmPrihlasCert" in r.text: print("Přihlášení selhalo — server vrátil login stránku znovu") return print("Přihlášení úspěšné!") # 4. Ulož cookies cookies = [ { "name": c.name, "value": c.value, "domain": c.domain if c.domain.startswith(".") else "." + c.domain, "path": c.path or "/", "expires": int(c.expires) if c.expires else -1, "secure": bool(c.secure), "httpOnly": False, "sameSite": "Lax", } for c in session.cookies ] with open(COOKIES_FILE, "w", encoding="utf-8") as f: json.dump(cookies, f, indent=2, ensure_ascii=False) print(f"Ulozeno {len(cookies)} cookies -> {COOKIES_FILE}") if __name__ == "__main__": main()