z230
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
"""
|
||||
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=<token>&sign=<podpis_base64_der>
|
||||
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()
|
||||
Reference in New Issue
Block a user