Merge fix/kik-v2-dynamic-security-headers: KİK v2 dynamic request signing
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
# kik_mcp_module/client_v2.py
|
# kik_mcp_module/client_v2.py
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import base64
|
||||||
import httpx
|
import httpx
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
@@ -50,6 +51,12 @@ class KikV2ApiClient:
|
|||||||
174, 228, 219, 174, 208, 104, 174, 120, 32, 76, 250, 4, 143, 159, 211, 176
|
174, 228, 219, 174, 208, 104, 174, 120, 32, 76, 250, 4, 143, 159, 211, 176
|
||||||
])
|
])
|
||||||
|
|
||||||
|
# AES-192-CBC key (environment.r8fact) used by the Angular HTTP interceptor to sign every
|
||||||
|
# request. The server decrypts X-Custom-Request-Ts and rejects stale timestamps with
|
||||||
|
# HTTP 401 "İstek zaman aşımına uğradı.", so these headers MUST be generated per-request
|
||||||
|
# with the current timestamp (see _generate_security_headers).
|
||||||
|
REQUEST_SIGNING_KEY = b"Qm2LtXR0aByP69vZNKef4wMJ" # UTF-8 bytes, 24 chars -> AES-192
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def encrypt_document_id(numeric_id: str) -> str:
|
def encrypt_document_id(numeric_id: str) -> str:
|
||||||
"""
|
"""
|
||||||
@@ -128,21 +135,43 @@ class KikV2ApiClient:
|
|||||||
# Generate security headers (these might need to be updated based on API requirements)
|
# Generate security headers (these might need to be updated based on API requirements)
|
||||||
self.security_headers = self._generate_security_headers()
|
self.security_headers = self._generate_security_headers()
|
||||||
|
|
||||||
|
def _sign_request_value(self, plaintext: str, iv: bytes) -> str:
|
||||||
|
"""AES-192-CBC encrypt a value with the request signing key, return base64 ciphertext."""
|
||||||
|
cipher = Cipher(
|
||||||
|
algorithms.AES(self.REQUEST_SIGNING_KEY),
|
||||||
|
modes.CBC(iv),
|
||||||
|
backend=default_backend()
|
||||||
|
)
|
||||||
|
encryptor = cipher.encryptor()
|
||||||
|
data = plaintext.encode("utf-8")
|
||||||
|
block_size = 16
|
||||||
|
padding_len = block_size - (len(data) % block_size)
|
||||||
|
padded = data + bytes([padding_len] * padding_len)
|
||||||
|
ciphertext = encryptor.update(padded) + encryptor.finalize()
|
||||||
|
return base64.b64encode(ciphertext).decode("ascii")
|
||||||
|
|
||||||
def _generate_security_headers(self) -> dict:
|
def _generate_security_headers(self) -> dict:
|
||||||
"""
|
"""
|
||||||
Generate the custom security headers required by KIK v2 API.
|
Generate the custom security headers required by the KIK v2 API.
|
||||||
These headers appear to be for request validation/encryption.
|
|
||||||
|
Mirrors the Angular HTTP interceptor on ekapv2.kik.gov.tr: a random GUID and a
|
||||||
|
current-timestamp (epoch milliseconds) are AES-192-CBC encrypted with environment.r8fact
|
||||||
|
using a fresh random IV. The IV is sent as -Siv, the encrypted timestamp as -Ts, and the
|
||||||
|
encrypted GUID as -R8id. The server validates the decrypted timestamp's freshness, so these
|
||||||
|
MUST be regenerated on every request; stale values yield HTTP 401 "İstek zaman aşımına uğradı.".
|
||||||
"""
|
"""
|
||||||
# Generate a random GUID for each session
|
if not HAS_CRYPTOGRAPHY:
|
||||||
|
raise ImportError("cryptography library required for KIK v2 request signing")
|
||||||
|
|
||||||
request_guid = str(uuid.uuid4())
|
request_guid = str(uuid.uuid4())
|
||||||
|
iv = os.urandom(16)
|
||||||
|
timestamp_ms = str(int(datetime.now().timestamp() * 1000))
|
||||||
|
|
||||||
# These are example values - in a real implementation, these might need
|
|
||||||
# to be calculated based on the request content or session
|
|
||||||
return {
|
return {
|
||||||
"X-Custom-Request-Guid": request_guid,
|
"X-Custom-Request-Guid": request_guid,
|
||||||
"X-Custom-Request-R8id": "hwnOjsN8qdgtDw70x3sKkxab0rj2bQ8Uph4+C+oU+9AMmQqRN3eMOEEeet748DOf",
|
"X-Custom-Request-R8id": self._sign_request_value(request_guid, iv),
|
||||||
"X-Custom-Request-Siv": "p2IQRTitF8z7I39nBjdAqA==",
|
"X-Custom-Request-Siv": base64.b64encode(iv).decode("ascii"),
|
||||||
"X-Custom-Request-Ts": "1vB3Wwrt8YQ5U6t3XAzZ+Q=="
|
"X-Custom-Request-Ts": self._sign_request_value(timestamp_ms, iv),
|
||||||
}
|
}
|
||||||
|
|
||||||
def _build_search_payload(self,
|
def _build_search_payload(self,
|
||||||
|
|||||||
Reference in New Issue
Block a user