import base64
import requests
import json
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from datetime import datetime, timezone
from time import sleep
BASE_URL = "https://ksef-test.mf.gov.pl/api"
def authorisation_challenge(nip):
HEADERS = {
"Content-Type": "application/json",
"Accept": "application/json"
}
endpoint_url = f"{BASE_URL}/online/Session/AuthorisationChallenge"
request_body = {
"contextIdentifier": {
"type": "onip",
"identifier": nip
}
}
response = requests.post(endpoint_url, headers=HEADERS, data=json.dumps(request_body))
response.raise_for_status()
return response.json()
def iso_to_milliseconds(timestamp: str) -> int:
dt_obj = datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%fZ")
dt_obj = dt_obj.replace(tzinfo=timezone.utc)
epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
delta = dt_obj - epoch
milliseconds = int(delta.total_seconds() * 1000)
return milliseconds
def round_to_nearest_thousand(milliseconds: int) -> int:
return round(milliseconds / 1000) * 1000
def encrypt(public_key_str, token, timestamp):
public_key = RSA.importKey(public_key_str)
e = public_key.e
n = public_key.n
pubkey = RSA.construct((n, e))
text = token + '|' + timestamp
cipher = PKCS1_v1_5.new(pubkey)
encrypted_text = cipher.encrypt(bytes(text, encoding='utf-8'))
return base64.b64encode(encrypted_text).decode('utf-8')
PUBLIC_KEY = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwocTwdNgt2+PXJ2fcB7k1kn5eFUTXBeep9pHLx6MlfkmHLvgjVpQy1/hqMTFfZqw6piFOdZMOSLgizRKjb1CtDYhWncg0mML+yhVrPyHT7bkbqfDuM2ku3q8ueEOy40SEl4jRMNvttkWnkvf/VTy2TwA9X9vTd61KJmDDZBLOCVqsyzdnELKUE8iulXwTarDvVTx4irnz/GY+y9qod+XrayYndtU6/kDgasAAQv0pu7esFFPMr83Nkqdu6JD5/0yJOl5RShQXwlmToqvpih2+L92x865/C4f3n+dZ9bgsKDGSkKSqq7Pz+QnhF7jV/JAmtJBCIMylxdxI/xfDHZ5XwIDAQAB
-----END PUBLIC KEY-----""" # [klucz publiczny z dokumentacji API]
AUTH_TOKEN = "922E535070046F867A1C094EEB8867B30FAC9833E5256C021073AFCA772AD9C8" # [token autoryzacyjny wygenerowany na MF]
response_data = authorisation_challenge("1111111111")
print(response_data)
challenge_time_iso = response_data['timestamp']
wynik_challenge = response_data['challenge']
print('wynik challenge: ', wynik_challenge)
sleep(1)
challenge_time = iso_to_milliseconds(challenge_time_iso)
challenge_time = round_to_nearest_thousand(challenge_time)
print('challenge_time: ',challenge_time)
combined_data = f"{AUTH_TOKEN}|{challenge_time}"
print(f"Polaczony token i czas (Token|ChallengeTime): {combined_data}")
result = encrypt(PUBLIC_KEY, AUTH_TOKEN, str(challenge_time))
print(f"Token i czas z szyfrowania: {result}")
sleep(1)
xml_template = """
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns3:InitSessionTokenRequest xmlns="http://ksef.mf.gov.pl/schema/gtw/svc/online/types/2021/10/01/0001" xmlns:ns2="http://ksef.mf.gov.pl/schema/gtw/svc/types/2021/10/01/0001" xmlns:ns3="http://ksef.mf.gov.pl/schema/gtw/svc/online/auth/request/2021/10/01/0001">
<ns3:Context>
<Challenge>{challenge}</Challenge>
<Identifier xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns2:SubjectIdentifierByCompanyType">
<ns2:Identifier>1111111111</ns2:Identifier>
</Identifier>
<DocumentType>
<ns2:Service>KSeF</ns2:Service>
<ns2:FormCode>
<ns2:SystemCode>FA (2)</ns2:SystemCode>
<ns2:SchemaVersion>1-0E</ns2:SchemaVersion>
<ns2:TargetNamespace>http://crd.gov.pl/wzor/2023/06/29/12648/</ns2:TargetNamespace>
<ns2:Value>FA</ns2:Value>
</ns2:FormCode>
</DocumentType>
<Token>{token}</Token>
</ns3:Context>
</ns3:InitSessionTokenRequest>
"""
formatted_xml = xml_template.format(challenge=wynik_challenge, token=result)
init_token_endpoint = "https://ksef-test.mf.gov.pl/api/online/Session/InitToken"
xml_headers = {
"Content-Type": "application/octet-stream",
"Accept": "application/json"
}
response = requests.post(init_token_endpoint, headers=xml_headers, data=formatted_xml)
print (response.status_code)
print (response.text)
to jest poprawiony kod. Challenge idzie do <Challenge>{challenge}</Challenge> a Token i czas z challenge jest szyfrowany i idzie do <Token>{token}</Token>.
Cały wynik z konsoli dalej 400:
{'timestamp': '2023-09-19T02:43:53.022Z', 'challenge': '20230919-CR-B9CA737CBD-A80538ABB3-94'}
wynik challenge: 20230919-CR-B9CA737CBD-A80538ABB3-94
challenge_time: 1695091433000
Polaczony token i czas (Token|ChallengeTime): 922E535070046F867A1C094EEB8867B30FAC9833E5256C021073AFCA772AD9C8|1695091433000
Token i czas z szyfrowania: OfdQR+b5bf9ZvGOxQ2+zeKnp0KNSGFs7nKqE71iBUL0b49u5y2+doiwqGqFF+4rNEk8/qxslLkabnkUvDrHoHShQl2K+WjSt25LBP6fJqWkNE+9FTNTzOl6jF6uEFPtTnsbxx3qc28WKGOzmvfDVYdOnk7upoHa5loXv2UbVYQvJBlkk9sWpLQw+ykPK3I7MZy6zx/0/n3QF6EbNaBEr/GjjXGJtjRD+YHpImSXniIQkOe1zjyM5TO3DCIqrBOnZeJCqThmxQYwmihqheKtmzH/KkwGnolFTWaUYWlwZHhqb3xRegWUDIeHxTC62qejcjLSCmlaPyPi00Bqga6rLXg==
400
{"exception":{"serviceCtx":"srvTEMFB","serviceCode":"20230919-EX-7A3F07718B-4CF1EBAC1C-4A","serviceName":"online.session.session.token.init","timestamp":"2023-09-19T02:43:55.363Z","referenceNumber":"20230919-SE-413CF10753-20F4A6C0E7-16","exceptionDetailList":[{"exceptionCode":21401,"exceptionDescription":"Dokument nie jest zgodny ze schemą (xsd)."}]}}
Może problem jest w timestamp, gdzie pokazuje o 2 godziny wcześniej niż aktualna godzina .....