Krajowy system e-Faktur

DVI Kesatuan
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 14
0

Mam problem z autoryzacją za pomocą tokenu. Poniżej wszystko co wykonałem.

  1. Z git'a MF pobrałem sobie ich .netowy dodatek KSeF Client. W konfiguracji dałem BaseUrl jako adres testowy https://ksef-test.mf.gov.pl/api i go uruchomiłem. Następnie skorzystałem z "/auth/access-token" wypełniając w następujący sposób:
    screenshot-20251021083732.png
    Nip jest nip'em rzeczywistym firmy, w której pracuje. Dostaje accessToken i refreshToken.
  2. Następnie za pomocą uzyskanego tokenu wywołałem endpoint "v2/tokens" podając wszystkie możliwe uprawnienia
    screenshot-20251021084030.png
  3. Dostaje w odpowiedzi wygenerowany token. Teraz przechodzę już do swojego rozwiązania.
  4. Tworzę challenge za pomocą "v2/auth/challenge"
  5. Za pomocą uzyskanego challengu tworzę base64 za pomocą poniższego kodu. Jako klucz publiczy mam wpisane "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxsyeiYiWB2+KFxEpQGoNQa6W8Pc4kWGl8V+sBMdW3Fqh0lhKiqKfpH5RWLDmZ30EzkKJ5+IdaWYFoijhYxDBIBhINVQKlBZvEVd6CfPJUJypa94eRO5cc6IPNI35aMhfKP/Kc4A/OiT2J4nyCz6BV98xOXCAlyDPD73XM6O2ormL6gUb673zvjOIakf39tAPPVgWIDuX7GDZYGebN7LXoGvjPo5YDqC2KN51ofLbO+n74iei5OaGN94Ap52vI7uzK2g/hQslOd0Avl2U1kwRnnF0yzwbDzRrHqPCHUYxVp5nHdo+jHe1CNoa6gt0m6pn1StYcitSXKg2hTNjnes6TQIDAQAB"
    screenshot-20251021085205.png
  6. Potem wywołuje "v2/auth/ksef-token" z następującym body:
    screenshot-20251021085917.png
  7. Dostaje następującą odpowiedź
    screenshot-20251021085953.png
  8. Następnie request na "v2/auth/{sessionToken.ReferenceNumber}" z nagłówkiem Authorization = Bearer AuthenticationToken.Token i tu już błędna odpowiedź
    screenshot-20251021091053.png
    Co jest nie tak w moich operacjach?
Dzyszla
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 202
0

Przechodzę po mału do wysyłki wsadowej i... Czy ja dobrze czytam, że w zaszyfrowanych częściach ZIP mają być zaszyfrowane faktury? Dwukrotne szyfrowanie?

N1ebieski
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 27
0

W nawiązaniu do dyskusji https://4programmers.net/Forum/Nietuzinkowe_tematy/355933-krajowy_system_e_faktur?p=2007925#id2007925 w kwestii integracji autoryzacji do KsEF za pomocą certyfikatów w przypadku aplikacji webowych, czyli zarządzanych z poziomu przeglądarki postanowiłem w ramach nauki napisać prostą apkę, żeby wypróbować rozwiązanie zaproponowane przez @sosoba - czyli klucz prywatny zaszyfrowany przez Web Cyrpto i zapisany jako nieeksportowalny w IndexedDB przeglądarki.

Faktycznie działa to całkiem fajnie. Jeśli kogoś to interesuje to zapraszam > https://github.com/N1ebieski/ksef-app-example.test Wszystkie szczegóły w README.

ZB
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 67
0

Jak wygląda kwestia identyfikatorów płatności? W API tego nie widzę, ma to być później dodane (tak jak choćby zgłaszanie faktur scamowych) czy nie?
A jeśli tak to w podobnej filozofii co poprzednio czy inaczej?

R1
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 3
0

Proszę o pomoc osobę, która robiła integrację w PHP
Dotarłem do etapu wysyłki faktury i niestety cały czas ""Rozmiar faktury nie zgadza się z zadeklarowaną wartością" - cały dzień z tym się męczę 😢

Sprawdziłem skrypt z 3 AI i fakturę przyjmuje ale niestety później w sesji po weryfikacji odrzuca z błędem rozmiaru

Kopiuj
"status": {
                "code": 430,
                "description": "Błąd weryfikacji pliku faktury",
                "details": [
                    "Rozmiar faktury nie zgadza się z zadeklarowaną wartością: '2474'."
                ]
            }

Skrypt którym generuję dane:

Kopiuj
<?php
declare(strict_types=1);

require __DIR__ . '/encryptor/vendor/autoload.php';

use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Crypt\RSA;

// --- Ścieżki do plików ---
$xmlPath = 'ksef_faktura_2.xml';
$publicKeyPath = 'mf_public_symmetric.pem';

// --- Wczytaj XML i Wyczyść/Znormalizuj ---
$rawXmlContent = file_get_contents($xmlPath);
$originalXml = $rawXmlContent;
if ($rawXmlContent === false) {
    http_response_code(500);
    die(json_encode(['error' => "Nie można wczytać pliku XML: $xmlPath"]));
}

file_put_contents('debug_original_xml.bin', $originalXml);

// --- Oblicz hash i rozmiar faktury (na podstawie znormalizowanego XML) ---
$invoiceHash = base64_encode(hash('sha256', $originalXml, true));

// Używamy strlen() na znormalizowanej treści, co daje rozmiar w bajtach.
$invoiceSize = strlen($originalXml);

// --- Generuj klucz AES i IV ---
try {
    $symmetricKey = random_bytes(32); // 256-bit AES
    $iv = random_bytes(16);           // 128-bit IV
} catch (Exception $e) {
    http_response_code(500);
    die(json_encode(['error' => 'Błąd generowania losowych bajtów: ' . $e->getMessage()]));
}

// --- Szyfruj XML za pomocą AES-256-CBC ---
// 2. KOREKTA: Zmieniono OPENSSL_RAW_DATA na 0, by wymusić standardowe dopełnienie PKCS#7.
$encrypted = openssl_encrypt($originalXml, 'aes-256-cbc', $symmetricKey, OPENSSL_RAW_DATA, $iv);
if ($encrypted === false) {
    http_response_code(500);
    die(json_encode(['error' => 'Błąd szyfrowania AES: ' . openssl_error_string()]));
}

// --- Połącz IV + zaszyfrowany XML ---
$encryptedWithIv = $iv . $encrypted;
file_put_contents('debug_encrypted_invoice.bin', $encryptedWithIv);
$encryptedBase64 = base64_encode($encryptedWithIv);

// --- Oblicz hash i rozmiar zaszyfrowanej faktury ---
$encryptedHash = base64_encode(hash('sha256', $encryptedWithIv, true));
$encryptedSize = strlen($encryptedWithIv);


// --- Wczytaj certyfikat MF do zaszyfrowania klucza AES ---
$publicKeyPem = file_get_contents($publicKeyPath);
if ($publicKeyPem === false) {
    http_response_code(500);
    die(json_encode(['error' => "Nie można wczytać klucza publicznego: $publicKeyPath"]));
}

try {
    $public = PublicKeyLoader::load($publicKeyPem);
    $publicOaep = $public
        ->withPadding(RSA::ENCRYPTION_OAEP)
        ->withHash('sha256')
        ->withMGFHash('sha256');

    $encryptedKeyRaw = $publicOaep->encrypt($symmetricKey);
    $encryptedKeyBase64 = base64_encode($encryptedKeyRaw);
} catch (\Throwable $e) {
    http_response_code(500);
    die(json_encode(['error' => 'Błąd szyfrowania klucza RSA: ' . $e->getMessage()]));
}

// --- Przygotuj dane JSON ---
$initializationVectorBase64 = base64_encode($iv);

// ... [Kod diagnostyczny] ...
// Oblicz rozmiary lokalnie
$size_original_strlen = strlen($originalXml);
$size_encrypted_strlen = strlen($encryptedWithIv);

$diagnostics = [
    'invoiceSize_sent' => $invoiceSize,
    'size_original_strlen_normalized' => $size_original_strlen,
    'encryptedSize_sent' => $encryptedSize,
    'size_encrypted_strlen' => $size_encrypted_strlen,
    'encryptedInvoiceHash_sent' => $encryptedHash,
];

$sessionRequest = [
    "formCode" => [
        "systemCode" => "FA (2)",
        "schemaVersion" => "1-0E",
        "value" => "FA"
    ],
    "encryption" => [
        "encryptedSymmetricKey" => $encryptedKeyBase64,
        "initializationVector" => $initializationVectorBase64
    ]
];

$invoicePayload = [
    "invoiceHash" => $invoiceHash,
    "invoiceSize" => $invoiceSize,
    "encryptedInvoiceHash" => $encryptedHash,
    "encryptedInvoiceSize" => $encryptedSize,
    "encryptedInvoiceContent" => $encryptedBase64,
    "offlineMode" => false
];

// --- Wyślij wynik JSON do stdout ---
header('Content-Type: application/json; charset=utf-8');
echo json_encode([
                     "sessionRequest" => $sessionRequest,
                     "invoicePayload" => $invoicePayload
                 ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

M4
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 61
0

Działa Wam wysyłka faktury na testowym bo mi cały czas zwraca status 150 i trwa przetwarzanie ?

BS
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 34
0

czy robicie swoją obsługę certyfikatów (csr i pobranie)? czy raczej czekacie na ich apke? Jeżeli swoją to czemu? 😀

ZB
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 67
0

W KSeF 1.0 przy wysyłaniu faktury i sprawdzaniu statusu były takie skomplikowane tabele przepływu:

Możliwe statusy:
• <100 – 200) – 1** - kod inicjalny, proces jest w trakcie lub został zainicjowany, ale nie został jeszcze rozpoczęty
• <200 – 300) – 2** - kod terminalny – sukces – proces został zakończony poprawnie
• <300 – 400) – 3** - kod operacyjny – proces jest w trakcie realizacji zdefiniowanych zadań
• <400 – 500) – 4** - kod terminalny – błąd – proces został zakończony ze względu na wystąpienie błędu biznesowego

Kopiuj
Nazwa                       Opis                                                            Start Sukces Błąd
------------                ------------------                                              ---    ---    ---
Authorise                   Autoryzacja podprocesu                                          100    310    410
Security                    Weryfikacja wyników podprocesu uwierzytelniania                 310    315    415
Decrypt                     Odszyfrowanie zaszyfrowanego dokumentu faktury                  315    320    420
VerifyInvoiceSemantics      Weryfikacja semantyki dokumentu faktury                         320    325    425
VerifyInvoiceEssentials     Weryfikacja założeń biznesowych dokumentu faktury               325    330    430
BeforeAccept                Oczekiwania na pozostałe dokumenty faktur z paczki wsadowej     330    335    435
Accept                      Akceptacja faktury oraz generowanie numeru KSeF                 335    340    440
ArchiveData                 Archiwizacja danych faktury                                     340    200    445

W KSeF 2.0 widzę tylko prostą listę kodów odpowiedzi, gdzie w ogóle nie ma nawet statusów z grupy 300.

Czy coś przegapiłem?

JO
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8
0

Czy ktoś jeszcze ma problem z uzyskaniem tokena w środowisku demonstracyjnym? Zacząłem uwierzytelnianie za pomocą kwalifikowanej pieczęci, ale za każdym razem, gdy próbuję uzyskać status uwierzytelnienia, otrzymuję tylko kod statusu 100 (Uwierzytelnianie w toku).

MC
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 2
0

Utworzyłem firmę ze swoim nipem w testowym ksef. Nie wiem czy to jest dobra taktyka testowania aplikacji. Aby odbierać ktoś musi wystawiać mi fikcyjne faktury w testowym ksef, abym mógł je pobierać i dalej implementować. Nie wiem jak można rozwiązać tą sprawę

M4
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 61
0

Jak długo czekacie na status 200 przy eksporcie faktur, czekam 5 minut i cały czas 100 ?

V2
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 32
0

Dla KSeF 1.0 miałem zrobione szyfrowanie (tokenów, paczek ZIP itp.) przy użyciu Windows'owego CryptoAPI. Czy komuś udało się zrobić w CryptoAPI to samo dla KSeF 2.0, w którym wymagania odnośnie sposobu szyfrowania są inne, lub wie czy/jak to można zrobić?

DR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 4
0

przestała działać sama z siebie wysyłka faktury przez aplikację dostarczoną przez MF .. jeszcze 2 dni temu wszystko działało, interaktywnie jak i batchowo.
Robiłęm integrację z docelową aplikacją i ponieważ status faktury długo się nie zmieniał to sprawdziłem "orygianlny" kod i też nie działa.

return await restClient.SendAsync<OpenOnlineSessionResponse, OpenOnlineSessionRequest>(HttpMethod.Post,
"/api/v2/sessions/online",
requestPayload,
accessToken,
RestClient.DefaultContentType,
cancellationToken).ConfigureAwait(false);
// >>
using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false); //wywala się w tym miejscu i nawet try-catch tego nie łapie

Może ktoś potwierdzić czy jest problem z https://ksef-test.mf.gov.pl/api/v2/sessions/online ?

ZB
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 67
0

screenshot-20251022115340.png

Projekt KSeF i termin luty 2026 ;-)

BS
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 34
0

certyfikaty -
robisz Pobranie danych do wniosku certyfikacyjnego gdzie napisane jest:
"System na tej podstawie zwraca komplet atrybutów DN (X.500 Distinguished Name), które muszą zostać użyte przy budowie żądania certyfikacyjnego (CSR). Modyfikacja tych danych spowoduje odrzucenie wniosku."
ale rozumiem, że imie i nazwisko, pesel to moge podac inne?

JO
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8
0

Czy ktoś zna wymagania dotyczące uwierzytelniania za pomocą KSeF w środowisku demonstracyjnym? W wersji testowej po prostu wygenerowałem certyfikat z podpisem własnym dla losowego numeru NIP i uzyskałem token dostępu za pomocą XAdES. Teraz próbuję uwierzytelnić się za pomocą XAdES, używając oficjalnej pieczęci kwalifikowanej w wersji demonstracyjnej. KSeF zwraca numer referencyjny uwierzytelniania, ale kod statusu to zawsze 100. Czy muszę najpierw gdzieś zarejestrować ten numer NIP?

V2
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 32
0

Algorytm obliczania sumy kontrolnej numeru KSeF 2.0 jest opublikowany. A co z numerami sesji? W KSeF 1.0 działał dla nich analogiczny algorytm obliczania sumy kontrolnej jak dla numerów KSeF. A jak jest dla KSeF 2.0?

WJ
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 47
0

W testach biblioteki MF pojawiła się przykładowa implementacja obsługi limitów API.

Przykład zastosowania:

Kopiuj
OperationResponse response = await KsefRateLimitWrapper.ExecuteWithRetryAsync(
    ksefApiCall: ct => KsefClient.ExportInvoicesAsync(request, _accessToken, ct, includeMetadata: true),
    endpoint: KsefApiEndpoint.InvoiceExport,
    cancellationToken: CancellationToken);

wraz z pobieżnym opisem pobrałem z tego nowego dokumentu.

Na marginesie: ten nowy dokument opisuje złożony przypadek pobierania z KSeF paczek z wieloma fakturami. Okna czasowe w tym przykładzie specjalnie ustawiono tak, by pojawiły się duplikaty, i proponują, jak sobie z nimi radzić.

SI
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 27
0

Witam, chciałbym poprosić by ktoś wystawił jakieś faktury na środowisku demo na nip 5420201038

M4
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 61
0

Mam pytanko,w firmie której pracuje jest 10 spółek i mam do nich pobierać faktury z KSEF, jak dobrze rozumiem powinienem mieć 10 certyfikatów, dla każdej robię autoryzacje, dostaje token, inicjuje sesje i pobieram faktury ?

ZB
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 67
0

https://ksiegowosc.infor.pl/ksef/7390916,mcu-rusza-1-listopada-2025-jak-uzyskac-certyfikat-ksef.html

cytat:

„Aby uzyskać certyfikat KSeF należy złożyć wniosek. Będzie to możliwe od 1 listopada 2025 r. za pośrednictwem Modułu Certyfikatów i Uprawnień (MCU), który zostanie udostępniony w domenie KSeF. MCU możliwi również nadawanie uprawnień użytkownikom przyszłego systemu KSeF 2.0. Funkcjonalności MCU będą dostępne wyłącznie z poziomu przeglądarki, nie będą dostępne z poziomu API

i drugi:

„generowanie certyfikatów w MCU jest rozwiązaniem tymczasowym przewidzianym na okres od listopada 2025 r. do końca stycznia 2026 r. Od lutego 2026 r. funkcjonalność będzie dostępna w KSeF 2.0 oraz w Aplikacji Podatnika KSeF 2.0. Uprawnienia nadane w MCU zostaną przeniesione do KSeF 2.0, a więc i do Aplikacji Podatnika KSeF 2.0”

To będzię w API generowanie i odnawianie certyfikatów czy nie? Trochę się pogubiłem.

GM
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 31
0

Cześć forumowicze, potrzebuję pomocy, czy ktoś z Was ma i może udostępnić paczkę faktur z plikiem metadata.json w środku ? Wystarczą mi 2 faktury, więcej też może być. Z góry dzięki.

N1ebieski
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 27
0

Czy ktoś może potwierdzić, że pobieranie faktur z tego endpointu działa > https://ksef-test.mf.gov.pl/docs/v2/index.html#tag/Pobieranie-faktur/paths/~1api~1v2~1invoices~1exports~1%7BreferenceNumber%7D/get

Coś co działało jeszcze tydzień temu, teraz już nie działa. Zwraca 410 Błąd walidacji danych wejściowych, żeby było śmieszniej zwraca to jako część response 200 OK oczywiście bez details. A dokumentacja jasno pokazuje, że to powinno być zwrócone jako 400 Bad Request z details.

K2
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 19
0

"Oduwierzytelnienie" ;) - pewnie głupie pytanie: czy i jak się "wylogować" z uwierzytelnionej sesji?
Ja wykonałem unieważnienie przez EP auth/sessions/{referenceNumber} metodą DELETE podając nr ref. mojej sesji (z lenistwa nie obsłużyłem przez auth/sessions/current).
Potem wylistowałem sesje i rzeczywiście jej już tam nie było - ale wylistować mogłem(!). Czyli wynika z tego, że accessToken nadal żyje i ma się dobrze. Nie uda się puścić refreshToken bo nie ma sesji o tym nr ref. - i dobrze. Więc ostatecznie sesja będzie jeszcze aktywna do czasu wygaśnięcia accessToken - nie ładnie ;)

ZB
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 67
0

Wysyłka wsadowa. W dokumentacji radzą, by sobie policzyć hasza każdej faktury przed jej wpakowaniem do ZIPa i stworzyć mapę faktura-hasz, żeby potem przy pobieraniu danych nie zgubić powiązania. Ja mam jednak pytanie czy skoro w wyniku endpointa pobierającego status i dane faktur:

/api/v2/sessions/{referenceNumber}/invoices

poza haszem jest też pole invoiceNumber to czy to nie wystarczy do wiązania?
Pytam praktyków, bo robię wsadowo i nie wiem jak to najlepiej ugryźć.

Marcin Konopka
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Siedlce
  • Postów: 15
0

Dzień dobry po długim czasie.
Dziwna rzecz się dzieje z logowanie tokenem w systemie testowym. Przy np. takim timestamp '2025-10-23T11:10:25.5287065+00:00' jest błąd logowania "Invalid timestamp), a przy np. takim '2025-10-23T11:12:48.0685421+00:00' zalogowało i tak 50% logowań złych i 50% dobrych.
Może ktoś zauważy jaką prawidłowość na podstawie przykładów
( timestamp z challenge:
Token z timestamp przez zakodowaniem: )

Logowania dobre:
'2025-10-23T11:15:58.5223039+00:00'
'20251023-EC-1DDAE2E000-215026F7F6-9A|nip-1149394432|827e98556adc48b9bf8473eaa8fd63d4af9d454af13c4ff1b0a5b859fd9a250f|1761218158522'

'2025-10-23T11:19:18.117144+00:00'
'...|1761218358117'

'2025-10-23T11:19:57.6530498+00:00'
'...|1761218397653'

Logowania złe:

'2025-10-23T11:16:38.2847874+00:00'
'...|1761218198285'

'2025-10-23T11:17:25.8415043+00:00'
'...|1761218245842'

'2025-10-23T11:18:31.9089653+00:00'
'...|1761218311909'

Pozdrawiam
Marcin Konopka

PS. Ponieważ problem rozwiązałem (obszedłem) zamieszczam kod: (obcinam nanosekundy do milisekund - 3 cyfry)

Kopiuj
function GetTimeStampInMsec(ts: string; RoundUp: boolean): Int64; overload;
var
  iDotPos, iTZPos, DigitsToRemove, FractionalDigitsCount: Integer;
begin
  if ts = 'null' then
    Exit(0);

  if not RoundUp then
  begin
    iDotPos := ts.IndexOf('.');
    if iDotPos > -1 then
    begin
      iTZPos := ts.IndexOfAny(['+', '-', 'Z'], iDotPos + 1);
      if iTZPos < 0 then
        iTZPos := ts.Length;

      FractionalDigitsCount := iTZPos - iDotPos - 1;
      DigitsToRemove := Max(0, FractionalDigitsCount - 3);

      if DigitsToRemove > 0 then
        ts := ts.Remove(iDotPos + 4, DigitsToRemove);
    end;
  end;

  Result := GetDateTimeInMSec(TimeStampStringtoDatetime(ts));
end;

gdzie:

Kopiuj
function TimeStampStringtoDatetime(tss:string):TDateTime;
begin
  if (tss='') or SameText(tss,'null') or not TryISO8601ToDate(tss, Result, true) then
    Result:=0
end;
Kopiuj
function GetDateTimeInMSec(const dt:TDateTime):Int64; overload;
begin
  Result:=(DateTimeToUnix(dt)*1000)+MilliSecondOf(dt);
end;

a użycie to:

Kopiuj
GetTimeStampInMsec(timestamp, false);
N1ebieski
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 27
FL4RE
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 27
0

Na prośbę @v2 oraz z racji tego, że trzeba sobie pomagać wrzucam implementacje z użyciem CryptoAPI (bcrypt.dll) w Delphi. Próbowałem uzyskać to samo w SecureBlackBox ale niestety z braku dokumentacji się poddałem. Jednak udało się to @Dzyszla , jego kod jest kilka stron wcześniej

Kopiuj

type
  NTSTATUS = Longint;
  ULONG = Cardinal;
  PUCHAR = PByte;
  BCRYPT_ALG_HANDLE = Pointer;
  BCRYPT_KEY_HANDLE = Pointer;

function BCryptOpenAlgorithmProvider(out phAlgorithm: BCRYPT_ALG_HANDLE; pszAlgId, pszImplementation: PWideChar; dwFlags: ULONG): NTSTATUS; stdcall; external 'bcrypt.dll';
function BCryptGetProperty(hObject: Pointer; pszProperty: PWideChar; pbOutput: PUCHAR; cbOutput: ULONG; out pcbResult: ULONG; dwFlags: ULONG): NTSTATUS; stdcall; external 'bcrypt.dll';
function BCryptSetProperty(hObject: Pointer; pszProperty: PWideChar; pbInput: PUCHAR; cbInput: ULONG; dwFlags: ULONG): NTSTATUS; stdcall; external 'bcrypt.dll';
function BCryptImportKey(hAlgorithm: BCRYPT_ALG_HANDLE; hImportKey: BCRYPT_KEY_HANDLE; pszBlobType: PWideChar; out phKey: BCRYPT_KEY_HANDLE; pbKeyObject: PUCHAR; cbKeyObject: ULONG; pbInput: PUCHAR; cbInput: ULONG; dwFlags: ULONG): NTSTATUS; stdcall; external 'bcrypt.dll';
function BCryptEncrypt(hKey: BCRYPT_KEY_HANDLE; pbInput: PUCHAR; cbInput: ULONG; pPaddingInfo: Pointer; pbIV: PUCHAR; cbIV: ULONG; pbOutput: PUCHAR; cbOutput: ULONG; out pcbResult: ULONG; dwFlags: ULONG): NTSTATUS; stdcall; external 'bcrypt.dll';
function BCryptDestroyKey(hKey: BCRYPT_KEY_HANDLE): NTSTATUS; stdcall; external 'bcrypt.dll';
function BCryptCloseAlgorithmProvider(hAlgorithm: BCRYPT_ALG_HANDLE; dwFlags: ULONG): NTSTATUS; stdcall; external 'bcrypt.dll';
function lstrlenW(ps: PWideChar): Integer; stdcall; external 'kernel32.dll' name 'lstrlenW';
function BCryptGenerateSymmetricKey(hAlgorithm: BCRYPT_ALG_HANDLE; out phKey: BCRYPT_KEY_HANDLE; pbKeyObject: PUCHAR; cbKeyObject: ULONG; pbSecret: PUCHAR; cbSecret: ULONG; dwFlags: ULONG): NTSTATUS; stdcall; external 'bcrypt.dll';

const
  STATUS_SUCCESS           = 0;
  STATUS_BUFFER_TOO_SMALL  = $C0000023;
STATUS_INVALID_PARAMETER = NTSTATUS($C000000D);
  BCRYPT_AES_ALGORITHM   : PWideChar = 'AES';
  BCRYPT_OBJECT_LENGTH   : PWideChar = 'ObjectLength';
  BCRYPT_CHAINING_MODE   : PWideChar = 'ChainingMode';
  BCRYPT_CHAIN_MODE_CBC  : PWideChar = 'ChainingModeCBC';
  BCRYPT_BLOCK_PADDING   = $00000001;
  BCRYPT_KEY_DATA_BLOB         : PWideChar = 'KeyDataBlob';
  BCRYPT_KEY_DATA_BLOB_MAGIC   = $4279654B; // 'KeyB'
  BCRYPT_KEY_DATA_BLOB_VERSION = 1;

type
  BCRYPT_KEY_DATA_BLOB_HEADER = packed record
    dwMagic   : ULONG;
    dwVersion : ULONG;
    cbKeyData : ULONG;
  end;

type
  ECNG = class(Exception);

class function TKsefInvoiceSender.AES_CBC_Encrypt_PKCS7_BCrypt(const Plain, Key, IV: TBytes): TBytes;
var
  hAlg: BCRYPT_ALG_HANDLE;
  hKey: BCRYPT_KEY_HANDLE;
  st: NTSTATUS;
  objLen, need, got, cb: ULONG;
  keyObj: TBytes;
  hdr: BCRYPT_KEY_DATA_BLOB_HEADER;
  blob, ivLocal: TBytes;

  procedure Check(const Where: string; Code: NTSTATUS);
  begin
    if Code <> STATUS_SUCCESS then
      raise ECNG.CreateFmt('%s failed: 0x%.8x', [Where, Cardinal(Code)]);
  end;

  procedure SetCBCOnKey(const KeyHandle: BCRYPT_KEY_HANDLE);
  var L: ULONG;
  begin
    L := (lstrlenW(BCRYPT_CHAIN_MODE_CBC)+1) * SizeOf(WideChar);
    Check('BCryptSetProperty(ChainingModeCBC)',
      BCryptSetProperty(KeyHandle, BCRYPT_CHAINING_MODE, PUCHAR(BCRYPT_CHAIN_MODE_CBC), L, 0));
  end;

  function DoEncrypt(const KeyHandle: BCRYPT_KEY_HANDLE): TBytes;
  begin
    ivLocal := Copy(IV);
    need := 0;
    Check('BCryptEncrypt(size)',
      BCryptEncrypt(KeyHandle, PUCHAR(@Plain[0]), Length(Plain),
                    nil, PUCHAR(@ivLocal[0]), Length(ivLocal),
                    nil, 0, need, BCRYPT_BLOCK_PADDING));
    SetLength(Result, need);
    ivLocal := Copy(IV);
    got := 0;
    Check('BCryptEncrypt',
      BCryptEncrypt(KeyHandle, PUCHAR(@Plain[0]), Length(Plain),
                    nil, PUCHAR(@ivLocal[0]), Length(ivLocal),
                    PUCHAR(@Result[0]), need, got, BCRYPT_BLOCK_PADDING));
    SetLength(Result, got);
  end;

  function TryImportKey(NeedKeyObj: Boolean; out KeyHandle: BCRYPT_KEY_HANDLE): NTSTATUS;
  begin
    if NeedKeyObj then
    begin
      objLen := 0; cb := 0;
      Result := BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, PUCHAR(@objLen), SizeOf(objLen), cb, 0);
      if (Result <> STATUS_SUCCESS) then Exit;
      if (cb <> SizeOf(objLen)) or (objLen = 0) then
      begin
        Result := STATUS_INVALID_PARAMETER;
        Exit;
      end;
      SetLength(keyObj, objLen);
      Result := BCryptImportKey(hAlg, nil, BCRYPT_KEY_DATA_BLOB, KeyHandle,
                                PUCHAR(@keyObj[0]), objLen,
                                PUCHAR(@blob[0]), Length(blob), 0);
    end
    else
    begin
      Result := BCryptImportKey(hAlg, nil, BCRYPT_KEY_DATA_BLOB, KeyHandle,
                                nil, 0,
                                PUCHAR(@blob[0]), Length(blob), 0);
    end;
  end;

  function GenerateKey(out KeyHandle: BCRYPT_KEY_HANDLE): NTSTATUS;
  begin
    objLen := 0; cb := 0;
    Result := BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, PUCHAR(@objLen), SizeOf(objLen), cb, 0);
    if Result <> STATUS_SUCCESS then Exit;
    if (cb <> SizeOf(objLen)) or (objLen = 0) then
    begin
      Result := STATUS_INVALID_PARAMETER;
      Exit;
    end;
    SetLength(keyObj, objLen);
    Result := BCryptGenerateSymmetricKey(hAlg, KeyHandle, PUCHAR(@keyObj[0]), objLen,
                                         PUCHAR(@Key[0]), Length(Key), 0);
  end;

begin
  if Length(Key) <> 32 then raise ECNG.Create('AES key must be 32 bytes');
  if Length(IV)  <> 16 then raise ECNG.Create('IV must be 16 bytes');

  hdr.dwMagic   := BCRYPT_KEY_DATA_BLOB_MAGIC;
  hdr.dwVersion := BCRYPT_KEY_DATA_BLOB_VERSION;
  hdr.cbKeyData := Length(Key);
  SetLength(blob, SizeOf(hdr) + Length(Key));
  Move(hdr, blob[0], SizeOf(hdr));
  Move(Key[0], blob[SizeOf(hdr)], Length(Key));

  hAlg := nil;
  Check('BCryptOpenAlgorithmProvider',
    BCryptOpenAlgorithmProvider(hAlg, BCRYPT_AES_ALGORITHM, nil, 0));
  try
     hKey := nil;
    st := TryImportKey(False, hKey);
    if st = STATUS_SUCCESS then
    begin
      try
        SetCBCOnKey(hKey);
        Exit(DoEncrypt(hKey));
      finally
        BCryptDestroyKey(hKey);
      end;
    end;
    hKey := nil;
    st := TryImportKey(True, hKey);
    if st = STATUS_SUCCESS then
    begin
      try
        SetCBCOnKey(hKey);
        Exit(DoEncrypt(hKey));
      finally
        BCryptDestroyKey(hKey);
      end;
    end;


    hKey := nil;
    Check('BCryptGenerateSymmetricKey', GenerateKey(hKey));
    try
      SetCBCOnKey(hKey);
      Exit(DoEncrypt(hKey));
    finally
      BCryptDestroyKey(hKey);
    end;
  finally
    BCryptCloseAlgorithmProvider(hAlg, 0);
  end;
end;
MG
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 46
0

Na demo przy pytaniu o ilość dostępnych certyfikatów mam {"remaining":4,"limit":6},"certificate":{"remaining":0,"limit":2}},
ale przy generowaniu certyfikatu jest komunikat "Osiągnięto limit dopuszczalnej ilości posiadanych certyfikatów". Czy to jest tak, że dopuszczalną ilość liczy się nie dla każdej pary NIP+pesel tylko pesel w systemie KSef? Testuje na 3 NIP-ach i to jest przy kolejnej 3-ciej.

FL4RE
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 27
0

@v2 sorry nie wrzucilem calosci, tu masz RSA-OAEP w CryptoAPI

Kopiuj

type
  ULONG   = Cardinal;
  NTSTATUS= Longint;
  PUCHAR  = PByte;
  LPCVOID = Pointer;
  LPVOID  = Pointer;
  LPCSTR  = PAnsiChar;
  LPCWSTR = PWideChar;





  BCRYPT_KEY_HANDLE = Pointer;

  BCRYPT_OAEP_PADDING_INFO = record
    pszAlgId: LPCWSTR;
    pbLabel: PUCHAR;
    cbLabel: ULONG;
  end;
  PBCRYPT_OAEP_PADDING_INFO = ^BCRYPT_OAEP_PADDING_INFO;

const
  BCRYPT_PAD_OAEP      = $00000004;

  STATUS_SUCCESS       = 0;

function CryptDecodeObject(dwCertEncodingType: DWORD; lpszStructType: LPCSTR;
  pbEncoded: PByte; cbEncoded: DWORD; dwFlags: DWORD; pvStructInfo: Pointer;
  var pcbStructInfo: DWORD): BOOL; stdcall; external 'Crypt32.dll' name 'CryptDecodeObject';

function CryptImportPublicKeyInfoEx2(dwCertEncodingType: DWORD;
  pInfo: PCERT_PUBLIC_KEY_INFO; dwFlags: DWORD; pAuxInfo: Pointer;
  var phKey: BCRYPT_KEY_HANDLE): BOOL; stdcall; external 'Crypt32.dll' name 'CryptImportPublicKeyInfoEx2';

function BCryptEncrypt(hKey: BCRYPT_KEY_HANDLE; pbInput: PUCHAR; cbInput: ULONG;
  pPaddingInfo: PBCRYPT_OAEP_PADDING_INFO; pbIV: PUCHAR; cbIV: ULONG;
  pbOutput: PUCHAR; cbOutput: ULONG; out pcbResult: ULONG; dwFlags: ULONG): NTSTATUS; stdcall; external 'Bcrypt.dll';

function BCryptDestroyKey(hKey: BCRYPT_KEY_HANDLE): NTSTATUS; stdcall; external 'Bcrypt.dll';


function EncryptRSA_OAEP256_WithSPKI(const SPKI_DER, Plain: TBytes): TBytes;
var
  DecodedSize: DWORD;
  Decoded: TBytes;
  PubInfo: PCERT_PUBLIC_KEY_INFO;


  hKey: BCRYPT_KEY_HANDLE;
  Status: NTSTATUS;

  Oaep: BCRYPT_OAEP_PADDING_INFO;
  Need, Got: ULONG;
begin
  Result := nil;
  if (Length(SPKI_DER) = 0) or (Length(Plain) = 0) then
    raise Exception.Create('EncryptRSA_OAEP256: empty input.');
  DecodedSize := 0;
  if (not CryptDecodeObject(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,
                            X509_PUBLIC_KEY_INFO,
                            PByte(@SPKI_DER[0]), Length(SPKI_DER),
                            0, nil, DecodedSize)) and
     (GetLastError <> ERROR_MORE_DATA) then
    raise Exception.Create('CryptDecodeObject(size) failed');

  SetLength(Decoded, DecodedSize);
  if not CryptDecodeObject(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,
                           X509_PUBLIC_KEY_INFO,
                           PByte(@SPKI_DER[0]), Length(SPKI_DER),
                           0, @Decoded[0], DecodedSize) then
    raise Exception.Create('CryptDecodeObject failed');

  PubInfo := PCERT_PUBLIC_KEY_INFO(@Decoded[0]);
  hKey := nil;
  if not CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,
                                     PubInfo, 0, nil, hKey) then
    raise Exception.Create('CryptImportPublicKeyInfoEx2 failed');

  try
    Oaep.pszAlgId := 'SHA256';
    Oaep.pbLabel  := nil;
    Oaep.cbLabel  := 0;

    Need := 0;
    Status := BCryptEncrypt(hKey,
                            PUCHAR(@Plain[0]), Length(Plain),
                            @Oaep,
                            nil, 0,
                            nil, 0,
                            Need,
                            BCRYPT_PAD_OAEP);
    if Status <> STATUS_SUCCESS then
      raise Exception.CreateFmt('BCryptEncrypt(size) failed: 0x%.8x', [Cardinal(Status)]);


    SetLength(Result, Need);
    Status := BCryptEncrypt(hKey,
                            PUCHAR(@Plain[0]), Length(Plain),
                            @Oaep,
                            nil, 0,
                            PUCHAR(@Result[0]), Need,
                            Got,
                            BCRYPT_PAD_OAEP);
    if Status <> STATUS_SUCCESS then
      raise Exception.CreateFmt('BCryptEncrypt failed: 0x%.8x', [Cardinal(Status)]);
    SetLength(Result, Got);
  finally
    BCryptDestroyKey(hKey);
  end;
end;```

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.