Autoryzacja KSeF

SK
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5
0

Dzień dobry,

  1. czy swagger na wersji testowej działa (jest 6 grudnia 2022). Walcze już dość długo z InitSessionTokenRequest i teraz mam TypeError: NetworkError when attempting to fetch resource. Gdy tak przez nieuwagę odpaliłem to na serwerze produkcyjnym to miałem "Plik niezgodny ze strukurą xsd".
  2. w/w TokenRequest : nie mogę tego zwalczyć. w aplikacji dostaję 400 - złe żądanie. Challenge robię, pobieram czas, tłumaczę go na unix (mam nadzieję że ok, robię to sam licząc, odejmując godzinę i w milisekundach). szyfruję kluczem publicznym RSA ten zbitek z tokena +|+ ten czas, robie base64. Tak zaszyfrowany token wygląda teraz dość dobrze , wcześniej bywało różnie. Szyfruję za pomocą openssl, kluczem pobranym ze strony ksef. I ciągle ..400.
  3. Tokena zrobiłem gdzieś na stronie ksef - dla wersji testowej.
  4. Czy dobrze rozumiem temat : najpierw /online/Session/AuthorisationChallenge, potem /online/Session/InitToken ? Nie muszę wcześniej specjanie się logować, nie muszę deklarować kluczy aes itd. ? Cały czas mówię o wersji testowej.

Ktoś mógłby mi wskazać palcem właściwą drogę ?
Używam VB z Visual Studio 2019.

Z góry dziekuję.

Kopiuj
<?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>20221206-CR-E0D0A1F48F-02E6C517DD-C4</Challenge>
      <Identifier xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns2:SubjectIdentifierByCompanyType">
          <ns2:Identifier>58xxxxxxx1</ns2:Identifier>
      </Identifier>
      <DocumentType>
          <ns2:Service>KSeF</ns2:Service>
          <ns2:FormCode>
              <ns2:SystemCode>FA (1)</ns2:SystemCode>
              <ns2:SchemaVersion>1-0E</ns2:SchemaVersion>
              <ns2:TargetNamespace>http://crd.gov.pl/wzor/2021/11/29/11089/</ns2:TargetNamespace>
              <ns2:Value>FA</ns2:Value>
          </ns2:FormCode>
      </DocumentType>
      <Token>kCEwEepHM82t8OYkKJdJJedZ5o2pkefAS5DfO1MZt0p71VKUqGZ0CXJMvhioieywOL03+T3wElpmU04UgVPOnaRIG74LHC+3D42hgGwJfijzm4Vrf1hOL4vGkiY2tgO82xrDBDLsjJo4i4BTDyKP9hPbAiCqwFKCEC61/FTXE1IiY6UDN+EZxyP5YwRRkz5OTlz5a6J0mzWhKlYKsPNg4/8FyEKoD3U26JQ+qCpCHdFk7P8PeuuHHW7hyg+6LAY7BJM8Omk2YhJopFE7CdH5S8hHd+IDB9SRiU4ADMPrKdmNXc77MrJeJyOUD3+vG3R2VBBdV1dszf+9rveOAZLjiw==</Token>
	</ns3:Context>
</ns3:InitSessionTokenRequest>
CF
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8
0

rozwiązałeś może ten problem ? Bo mam to samo, robie tak jak Ty i nic.

Michał Podbielski
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5
0

@casebe fx: Opisz dokładniej jaki masz problem możesz też wrzucić request, bo problem z pierwszego postu jest dość stary i np zmieniła się struktura i namespace KSeF do jakiego się logujemy. Ja przeszedłem w tym tygodniu przez proces autoryzacji i wysyłki faktury na środowisku przedprodukcyjnym (demo) bez problemu wiec może będę umiał pomóc.

CF
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8
0
Michał Podbielski napisał(a):

@casebe fx: Opisz dokładniej jaki masz problem możesz też wrzucić request, bo problem z pierwszego postu jest dość stary i np zmieniła się struktura i namespace KSeF do jakiego się logujemy. Ja przeszedłem w tym tygodniu przez proces autoryzacji i wysyłki faktury na środowisku przedprodukcyjnym (demo) bez problemu wiec może będę umiał pomóc.

Od dwóch tygodni próbuję zainicjować sesję w python, ale ciagle mam błąd 400 lub 415. Nie wiem już o co chodzi. Kod python raczej jest ok, tu chyba chodzi o ta schemę xsd i xml. @alapierre próbował mnie naprowadzić, ale ciągle wyskakuje błąd.
Naprawdę byłbym bardzo wdzięczny, gdybyś spojrzał na mój kod.
Pozdrawiam!

Kopiuj
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-----"""  # [Insert your public key here]
AUTH_TOKEN = "922E535070046F867A1C094EEB8867B30FAC9833E5256C021073AFCA772AD9C8"  # [Insert your auth token here]

response_data = authorisation_challenge("1111111111")
print(response_data)
challenge_time_iso = response_data['timestamp']
sleep(1)

challenge_time = iso_to_milliseconds(challenge_time_iso)
challenge_time = round_to_nearest_thousand(challenge_time)

combined_data = f"{AUTH_TOKEN}|{challenge_time}"
print(f"Combined token and challenge time (Token|ChallengeTime): {combined_data}")

result = encrypt(PUBLIC_KEY, AUTH_TOKEN, str(challenge_time))
print(f"Encoded token and challenge time: {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=challenge_time_iso, 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)

Michał Podbielski
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5
0

chyba wiem co jest źle
<Challenge>{challenge}</Challenge>
i jako {challenge} podstawiasz (challenge=challenge_time_iso } czyli challenge_time_iso = response_data['timestamp'] jeśli dobrze rozumiem kod
a tam powinno być respond_data['challenge'] czyli Token wyzwania autoryzacyjnego otrzymanego razem ze znacznikiem czasowym.

CF
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8
0
Michał Podbielski napisał(a):

chyba wiem co jest źle
<Challenge>{challenge}</Challenge>
i jako {challenge} podstawiasz (challenge=challenge_time_iso } czyli challenge_time_iso = response_data['timestamp'] jeśli dobrze rozumiem kod
a tam powinno być respond_data['challenge'] czyli Token wyzwania autoryzacyjnego otrzymanego razem ze znacznikiem czasowym.

Bardzo dziękuję za pomoc, ale walczę dalej :)

Rozumiem, że w <Challenge>{challenge}</Challenge> ma mieścić się token/challenge + timestamp z challenge ?
A w jakim formacie (to znaczy jak połaczony) ma byc ten {challenge} ?

Czy ma być to 20230918-CR-C4ED688DCD-8C1C588180-07 (+ | : ???) 2023-09-18T19:32:53.677Z czyli np 20230918-CR-C4ED688DCD-8C1C588180-072023-09-18T19:32:53.677Z
czy na przykład 20230918-CR-C4ED688DCD-8C1C588180-07+2023-09-18T19:32:53.677Z ?
czy może 20230918-CR-C4ED688DCD-8C1C588180-07+1695065574000 , 20230918-CR-C4ED688DCD-8C1C588180-07|1695065574000 czy jeszcze inaczej.

na webinarze z MF gość podstawia 20230918-CR-C4ED688DCD-8C1C588180-07 i 2023-09-18T19:32:53.677Z . gdzieś na forum czytałem , że mają to być milisekundy w unix.

pogubiłem się trochę....

Michał Podbielski
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5
0

To jest tak po wywołaniu AuthorisationChallenge otrzymujesz:
{"timestamp":"2023-09-18T19:55:21.654Z","challenge":"20230918-CR-B56C507960-8829354003-C6"}

i 20230918-CR-B56C507960-8829354003-C6 trafia bezposrednio do <Challenge> jako jawny tekst
a do <Token> trafia zaszyfrowane kluczem publicznym 99AC8862________________64A0E2|1695066921654
gdzie - 99AC8862__________________64A0E2 to token wygenerowany na koncie KSEF
a 1695066921654 - liczba milisekund od 1970-01-01

CF
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8
0
Kopiuj
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 .....

Michał Podbielski
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5
0

to na pewno nie to bo wtedy byłby błąd o niezgodności czasu , ale dlaczego xml_template zaczynasz od podwójnego cudzysłowy, nie znam python ale jeśli powoduje to że cały xml jest wysyłany w cudzysłowie to na pewno będzie niezgodnie z xsd , poza tym jak złożyłem xml z danych podanych to zwalidował się z xsd.

Ayca Cetin
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 11
0

Hello,

We want to create demo token for our clients on and on new process, we are trying to handle this without using ksef-demo portal.

As a first step, we are generating challenge id as below;

screenshot-20251017081118.png

After that, we added challenge id to below XML for signing the document on https://moj.gov.pl/nforms/signer/upload?xFormsAppName=SIGNER.

screenshot-20251017081132.png
Later, we received signed XML from our client successfully.

Then, when we send this signed XML to https://ksef-demo.mf.gov.pl/api/v2/auth/xades-signature?verifyCertificateChain=false we are able to generate token as below;
screenshot-20251017081146.png
After that, we are using reference number API to control token and we are facing below error;
screenshot-20251017081205.png

screenshot-20251017081219.png
We used this method for both trusted profile and qualified signature and on both methods, we faced these errors. What could be the reason of this issue?

Michał Podbielski
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5
0

I see two possible reasons:
the first is that the person signing the XML is not registered with the company to handle KSEF via the ZAW-FA form.
The second is that the step of generating JWT tokens via {baseUrl}/api/v2/auth/token/redeem was skipped. The token received after signing the XML should be used for this purpose.
Which API URL are you using?
Yesterday, I managed to complete the entire process, including sending the invoice to the https://ksef-demo.mf.gov.pl/ service at https://ksef-tr-api-huazctbsfvduh5gg.a01.azurefd.net/api/v2

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.