From 9f6d86d32830b6563b94fe45d2b2efff1c47ff6d Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Fri, 14 Nov 2025 13:43:58 +0100 Subject: [PATCH] neue impl --- doc/KSeF/README.md | 16 +- .../ksef/KSeFAuthManager.java | 348 ++++++++++++++++-- .../ksef/KSeFAuthManagerTest.java | 30 ++ .../resources/ksef/example-invoice-01.xml | 138 +++++++ .../src/test/resources/ksef/test.json | 11 + 5 files changed, 514 insertions(+), 29 deletions(-) create mode 100644 office-alexander-logistics-app/src/test/resources/ksef/example-invoice-01.xml create mode 100644 office-alexander-logistics-app/src/test/resources/ksef/test.json diff --git a/doc/KSeF/README.md b/doc/KSeF/README.md index 4092003..821973e 100644 --- a/doc/KSeF/README.md +++ b/doc/KSeF/README.md @@ -1,13 +1,21 @@ # KSeF - Converter -Die Idee ist es auf Basis der KSeF FA(2) XSD-Spezifikation einen Java-Konverter zu entwickeln. - -## Github +Die Idee ist es Rechnungen nach Erhalt aus cargosoft über die KSeF FA(2) API an die Behörde zu senden. # API +Dokumentation: https://ksef-test.mf.gov.pl/docs/v2/index.html + Die Basis url ist: "https://ksef-test.mf.gov.pl/api/v2"; +Der Ablauf für die Übermittlung ist folgender: + +- vom Polnischen Goverment (bzw. AGL/Taxman) haben wir ein Secret bekommen (KSeFToken) +- wir machen den call `/security/public-key-certificates` um Security Zertifikate zu erhalten +- wir machen den call `/auth/challenge` um eine Challenge ID zu erhalten +- wir machen den call `/auth/ksef-token` um eine ReferenceID und einen AccessToken zu erhalten +- wir senden die Rechnung - mit dem öffentlichen Schlüssel verschlüsselt zusammen mit unserer ReferenceID und dem AccessToken an das Goverment. + ## Generieren einer Referenznummer. Man benötigt eine Referenznummer (Session ID). Dise kann man wie folgt generieren: @@ -24,6 +32,8 @@ Details: https://github.com/CIRFMF/ksef-docs/blob/main/sesja-interaktywna.md#2-wys%C5%82anie-faktury +Man muss die XML Datei mit dem öffentlichen Schlüssel der Behörde vor dem Versand verschlüsseln + ### Das Rechnungsformat: XML Datei diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ksef/KSeFAuthManager.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ksef/KSeFAuthManager.java index 682ccce..3b76360 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ksef/KSeFAuthManager.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ksef/KSeFAuthManager.java @@ -5,11 +5,16 @@ import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; import java.security.PublicKey; +import java.security.SecureRandom; import java.time.Duration; +import java.util.Base64; import java.util.Optional; import java.util.logging.Logger; +import javax.crypto.Cipher; + import org.eclipse.microprofile.config.inject.ConfigProperty; import org.imixs.workflow.exceptions.InvalidAccessException; import org.imixs.workflow.exceptions.PluginException; @@ -65,8 +70,19 @@ public class KSeFAuthManager { private String challenge = ""; private String refNumber = null; private String authToken = null; + private String accessToken = null; + private String refreshToken = null; private String symmetricCertBase64; // der Zertifikat-String aus der API private PublicKey symmetricPublicKey; // der extrahierte RSA Public Key + private String sessionRefNumber = null; + private String sessionValidUntil = null; + private String ksefTokenEncryptionCertBase64 = null; + private PublicKey ksefTokenEncryptionPublicKey; + private String challengeTimestamp = null; + + private byte[] sessionAesKey; + + private byte[] sessionIv; @PostConstruct void init() { @@ -94,6 +110,10 @@ public class KSeFAuthManager { // this.jsonb = JsonbBuilder.create(); } + public HttpClient getHttpClient() { + return httpClient; + } + public void setKsefToken(String token) { this.ksefToken = Optional.ofNullable(token); } @@ -115,12 +135,13 @@ public class KSeFAuthManager { * * @return */ - public String getEncodedToken() { + // public String getEncodedToken() { - String result = java.util.Base64.getEncoder().encodeToString(ksefToken.orElse("").getBytes()); + // String result = + // java.util.Base64.getEncoder().encodeToString(ksefToken.orElse("").getBytes()); - return result; - } + // return result; + // } public String getBaseURI() { return baseURI; @@ -182,9 +203,12 @@ public class KSeFAuthManager { // Annahme: response.body() ist ein String mit JSON-Inhalt JsonObject jsonObject = jsonb.fromJson(response.body(), JsonObject.class); challenge = jsonObject.getString("challenge"); + challengeTimestamp = jsonObject.getString("timestamp"); if (debug) { logger.info("│ ├── challenge: " + challenge); + logger.info("│ ├── timestamp: " + challengeTimestamp); } + } catch (Exception e) { logger.severe("├── ⚠️ Error parsing JSON response" + e.getMessage()); throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, @@ -210,6 +234,26 @@ public class KSeFAuthManager { logger.info("├── KSeF API auth ksef-token..."); String uri = baseURI + "/auth/ksef-token"; logger.info("│ ├── Endpoint: " + uri); + + if (ksefTokenEncryptionPublicKey == null) { + throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_CONFIG, + "Missing public key for KSeF token encryption (KsefTokenEncryption)."); + } + + // token|timestamp (aus challenge) + String token = ksefToken.orElseThrow(() -> new PluginException( + KSeFAuthManager.class.getSimpleName(), ERROR_CONFIG, "Missing KSeF API token")); + + String tokenWithTimestamp = token + "|" + challengeTimestamp; + + logger.info("│ ├── token|timestamp = " + tokenWithTimestamp); + + // Verschlüsseln mit RSA-OAEP SHA-256 + String encryptedToken = encryptWithPublicKey(tokenWithTimestamp, ksefTokenEncryptionPublicKey); + + logger.info("│ ├── Encrypted token (Base64): " + encryptedToken); + + // JSON payload zusammenbauen String jsonPayload = String.format( "{" + "\"challenge\": \"%s\"," + @@ -219,7 +263,9 @@ public class KSeFAuthManager { "}," + "\"encryptedToken\": \"%s\"" + "}", - challenge, ksefNip.get(), getEncodedToken()); + challenge, + ksefNip.get(), + encryptedToken); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(uri)) @@ -229,55 +275,102 @@ public class KSeFAuthManager { .build(); HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + logger.info("│ ├── Response Code = " + response.statusCode()); if (debug) { logger.info("Response: " + response.body()); } - // Prüfen, dass wir eine API-Antwort bekommen if (response.statusCode() >= 500) { throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_CONFIG, "Unable to access KSeF API: response=" + response.statusCode()); } - // JSON parsen und Werte speichern if (response.statusCode() == 200 || response.statusCode() == 202) { try (Jsonb jsonb = JsonbBuilder.create()) { JsonObject jsonObject = jsonb.fromJson(response.body(), JsonObject.class); - // Reference Number speichern refNumber = jsonObject.getString("referenceNumber"); logger.info("Extracted referenceNumber: " + refNumber); - // Authentication Token speichern JsonObject authTokenObject = jsonObject.getJsonObject("authenticationToken"); authToken = authTokenObject.getString("token"); logger.info("Extracted authenticationToken: " + authToken); } catch (Exception e) { - logger.severe("├── ⚠️ Error parsing JSON response" + e.getMessage()); throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, "Error parsing JSON response: " + e.getMessage()); } } } + @Lock(LockType.WRITE) + public void redeemAuthToken() throws Exception { + logger.info("├── KSeF API redeem authentication token..."); + String uri = baseURI + "/auth/token/redeem"; + logger.info("│ ├── Endpoint: " + uri); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(uri)) + .header("Accept", "application/json") + .header("Content-Type", "application/json") + .header("Authorization", "Bearer " + authToken) // authToken aus generateAuthToken() + .POST(HttpRequest.BodyPublishers.noBody()) // Payload leer, API erwartet nur Bearer im Header + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + logger.info("│ ├── Response Code = " + response.statusCode()); + if (debug) { + logger.info("Response: " + response.body()); + } + + // Prüfen auf Fehler + if (response.statusCode() >= 500) { + throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_CONFIG, + "Unable to access KSeF API: response=" + response.statusCode()); + } + + // JSON parsen und AccessToken + RefreshToken speichern + if (response.statusCode() == 200 || response.statusCode() == 201) { + try (Jsonb jsonb = JsonbBuilder.create()) { + JsonObject jsonObject = jsonb.fromJson(response.body(), JsonObject.class); + + JsonObject accessTokenObj = jsonObject.getJsonObject("accessToken"); + accessToken = accessTokenObj.getString("token"); // finaler Bearer für alle weiteren API-Calls + logger.info("Extracted accessToken: " + accessToken); + + JsonObject refreshTokenObj = jsonObject.getJsonObject("refreshToken"); + refreshToken = refreshTokenObj.getString("token"); // optional zum späteren Refresh + if (debug) { + logger.info("Extracted refreshToken: " + refreshToken); + } + + } catch (Exception e) { + logger.severe("├── ⚠️ Error parsing JSON response: " + e.getMessage()); + throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, + "Error parsing JSON response: " + e.getMessage()); + } + } else { + throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, + "Failed to redeem authentication token: " + response.statusCode()); + } + } + /** * Helper Method to read the certificates * * @throws Exception */ @Lock(LockType.WRITE) - public void getKsefPublicKeys() throws Exception { + public void loadKsefPublicKeys() throws Exception { - logger.info("├── KSeF API public-key-certificates..."); + logger.info("├── KSeF API load public-key-certificates..."); String uri = baseURI + "/security/public-key-certificates"; logger.info("│ ├── Endpoint: " + uri); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(uri)) .header("Accept", "application/json") - .header("Authorization", "Bearer " + authToken) .GET() .build(); @@ -290,26 +383,34 @@ public class KSeFAuthManager { if (response.statusCode() != 200) { throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, - "Unexpected response from KSeF certificates endpoint: " + response.statusCode()); + "Unexpected response: " + response.statusCode()); } - // JSON parsen try (Jsonb jsonb = JsonbBuilder.create()) { - var certArray = jsonb.fromJson(response.body(), JsonObject[].class); - for (JsonObject entry : certArray) { - // usage = ["KsefTokenEncryption"] oder ["SymmetricKeyEncryption"] + // WICHTIG: JsonArray lesen – NICHT JsonObject[] + jakarta.json.JsonArray certArray = jsonb.fromJson(response.body(), jakarta.json.JsonArray.class); + + for (var entryValue : certArray) { + + JsonObject entry = entryValue.asJsonObject(); var usages = entry.getJsonArray("usage"); - if (usages.stream().anyMatch(v -> v.toString().contains("SymmetricKeyEncryption"))) { - symmetricCertBase64 = entry.getString("certificate"); - logger.info("│ ├── SymmetricKeyEncryption certificate found."); - // Public Key extrahieren - symmetricPublicKey = extractPublicKeyFromCertificate(symmetricCertBase64); - logger.info("│ ├── ✓ RSA PublicKey successfully extracted."); - return; // gefunden – wir sind fertig + + if (usages != null && + usages.stream().anyMatch(v -> v.toString().contains("KsefTokenEncryption"))) { + + ksefTokenEncryptionCertBase64 = entry.getString("certificate"); + logger.info("│ ├── Found certificate with usage = KsefTokenEncryption"); + + ksefTokenEncryptionPublicKey = extractPublicKeyFromCertificate(ksefTokenEncryptionCertBase64); + + logger.info("│ ├── ✓ RSA PublicKey for KSeF Token Encryption extracted."); + return; } } - logger.warning("│ ├── ⚠️ No certificate with usage 'SymmetricKeyEncryption' found!"); + + throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, + "No certificate with usage = KsefTokenEncryption found"); } catch (Exception e) { throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, @@ -317,6 +418,193 @@ public class KSeFAuthManager { } } + @Lock(LockType.WRITE) + public void loadSymmetricKeyPublicKey() throws Exception { + + logger.info("├── KSeF API load symmetric public key..."); + String uri = baseURI + "/security/public-key"; + logger.info("│ ├── Endpoint: " + uri); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(uri)) + .header("Accept", "application/json") + .GET() + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + + logger.info("│ ├── Response Code = " + response.statusCode()); + if (debug) { + logger.info("Response: " + response.body()); + } + + if (response.statusCode() != 200) { + throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, + "Unexpected response: " + response.statusCode()); + } + + try (Jsonb jsonb = JsonbBuilder.create()) { + + JsonObject json = jsonb.fromJson(response.body(), JsonObject.class); + + symmetricCertBase64 = json.getString("certificate"); + logger.info("│ ├── Symmetric key certificate loaded"); + + symmetricPublicKey = extractPublicKeyFromCertificate(symmetricCertBase64); + + logger.info("│ ├── ✓ RSA PublicKey for session encryption extracted."); + + } catch (Exception e) { + throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, + "Error parsing public key JSON: " + e.getMessage()); + } + } + + /** + * Helper Method to open an interactive session + * + * @throws Exception + */ + @Lock(LockType.WRITE) + public void openInteractiveSession() throws Exception { + + if (accessToken == null) { + throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, + "Error - missing access token!"); + } + + logger.info("├── KSeF API open interactive session..."); + String uri = baseURI + "/sessions/online"; + logger.info("│ ├── Endpoint: " + uri); + + // --- AES Schlüssel erzeugen --- + SecureRandom rnd = new SecureRandom(); + byte[] aesKeyBytes = new byte[32]; // 256-bit AES key + rnd.nextBytes(aesKeyBytes); + byte[] ivBytes = new byte[16]; // 128-bit IV + rnd.nextBytes(ivBytes); + + // --- AES Key mit RSA encrypten --- + // Cipher rsa = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); + Cipher rsa = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); + rsa.init(Cipher.ENCRYPT_MODE, this.symmetricPublicKey); + byte[] encryptedAesKey = rsa.doFinal(aesKeyBytes); + + String jsonPayload = String.format( + "{" + + "\"formCode\": {" + + " \"systemCode\": \"FA (3)\"," + + " \"schemaVersion\": \"1-0E\"," + + " \"value\": \"FA\"" + + "}," + + "\"encryption\": {" + + " \"encryptedSymmetricKey\": \"%s\"," + + " \"initializationVector\": \"%s\"" + + "}" + + "}", + Base64.getEncoder().encodeToString(encryptedAesKey), + Base64.getEncoder().encodeToString(ivBytes)); + + if (debug) { + logger.info("│ ├── Payload: " + jsonPayload); + } + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(uri)) + // .header("Accept-Charset", "UTF-8") + .header("Accept", "application/json") + .header("Content-Type", "application/json") + .header("Authorization", "Bearer " + accessToken) + .POST(HttpRequest.BodyPublishers.ofString(jsonPayload)) + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + + logger.info("│ ├── Response Code = " + response.statusCode()); + if (debug) { + logger.info("Response: " + response.body()); + } + + if (response.statusCode() != 201) { + throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, + "Failed to open interactive session: " + response.statusCode()); + } + + // JSON parsen + // Session RefNumber aus Response extrahieren und speichern + + try (Jsonb jsonb = JsonbBuilder.create()) { + // Annahme: response.body() ist ein String mit JSON-Inhalt + JsonObject jsonObject = jsonb.fromJson(response.body(), JsonObject.class); + sessionRefNumber = jsonObject.getString("referenceNumber"); + sessionValidUntil = jsonObject.getString("validUntil"); + if (debug) { + logger.info("│ ├── Session ReferenceNumber = " + sessionRefNumber); + logger.info("│ └── Session ValidUntil = " + sessionValidUntil); + } + + // --- WICHTIG: AES Schlüssel speichern, wir brauchen ihn beim Upload --- + this.sessionAesKey = aesKeyBytes; + this.sessionIv = ivBytes; + } catch (Exception e) { + logger.severe("├── ⚠️ Error parsing JSON response" + e.getMessage()); + throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, + "Error parsing JSON response: " + e.getMessage()); + + } + + } + + /** + * Helper Method to analyze the auth status + * + * @return + * @throws Exception + */ + public JsonObject getAuthStatus() throws Exception { + if (refNumber == null) { + throw new IllegalStateException("Reference number is missing. Call generateAuthToken() first."); + } + + logger.info("├── KSeF API check authentication status..."); + + logger.info("Using public key algorithm: " + symmetricPublicKey.getAlgorithm()); + logger.info("Key format: " + symmetricPublicKey.getFormat()); + + String uri = baseURI + "/auth/" + refNumber; + logger.info("│ ├── Endpoint: " + uri); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(uri)) + .header("Accept", "application/json") + .header("Authorization", "Bearer " + authToken) + .GET() + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + logger.info("│ ├── Response Code = " + response.statusCode()); + if (debug) { + logger.info("Response: " + response.body()); + } + + if (response.statusCode() >= 500) { + throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_CONFIG, + "Unable to access KSeF API: response=" + response.statusCode()); + } + + try (Jsonb jsonb = JsonbBuilder.create()) { + JsonObject jsonObject = jsonb.fromJson(response.body(), JsonObject.class); + Boolean isRedeemed = jsonObject.containsKey("isTokenRedeemed") ? jsonObject.getBoolean("isTokenRedeemed") + : null; + logger.info("│ ├── isTokenRedeemed: " + isRedeemed); + return jsonObject; + } catch (Exception e) { + logger.severe("├── ⚠️ Error parsing JSON response: " + e.getMessage()); + throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API, + "Error parsing JSON response: " + e.getMessage()); + } + } + private PublicKey extractPublicKeyFromCertificate(String base64Cert) throws Exception { byte[] der = java.util.Base64.getDecoder().decode(base64Cert); @@ -329,4 +617,12 @@ public class KSeFAuthManager { return certificate.getPublicKey(); } + private String encryptWithPublicKey(String data, PublicKey publicKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(encryptedBytes); + + } + } diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/ksef/KSeFAuthManagerTest.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/ksef/KSeFAuthManagerTest.java index 54205e7..7155ee5 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/ksef/KSeFAuthManagerTest.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/ksef/KSeFAuthManagerTest.java @@ -92,4 +92,34 @@ public class KSeFAuthManagerTest { System.out.println("Received KSeF Token OK"); } + + @Test + @Order(4) + @DisplayName("Upload Invoice XML (AES-256 + RSA)") + public void testOpenInteractiveSession() throws Exception { + + // ---------- 1) Vorbereitung ---------- + // API initialisieren + manager.loadKsefPublicKeys(); + manager.getChallenge(); + manager.generateAuthToken(); // erzeugt authToken + refNumber + manager.getAuthStatus(); + manager.redeemAuthToken(); + + assertNotNull(manager.getAuthToken(), "AccessToken fehlt"); + assertNotNull(manager.getRefNumber(), "ReferenceNumber fehlt"); + + manager.openInteractiveSession(); + } + + @Test + @Order(5) + @DisplayName("Upload Invoice XML (AES-256 + RSA)") + public void testUploadInvoice() throws Exception { + + // ---------- 1) Vorbereitung ---------- + // API initialisieren + + } + } diff --git a/office-alexander-logistics-app/src/test/resources/ksef/example-invoice-01.xml b/office-alexander-logistics-app/src/test/resources/ksef/example-invoice-01.xml new file mode 100644 index 0000000..f1b7ac9 --- /dev/null +++ b/office-alexander-logistics-app/src/test/resources/ksef/example-invoice-01.xml @@ -0,0 +1,138 @@ + + + + FA + 3 + 2026-06-05T02:45:33.422866Z + Generator danych + + + + 9552521552 + Alexander Global Logistics GmbH + + + PL + Museumstr. 2-6 + 28195 Bremen + + + Lucja17@example.net + 86-588-03-53 + + + + + 3861610227 + Błaszczak - Lenart + + + PL + al. Szewczyk 7247 + 84-642 Żelechów + + + Jakubina82@gmail.com + 75-461-31-12 + + KL-2936 + 2 + 2 + + + PLN + #invoicing_date# + Lidzbark + FA/GRQMB-#invoice_number#/05/2025 + 2025-07-11 + 10652.38 + 2450.05 + 13102.43 + + 2 + 2 + 2 + 2 + + 1 + + + 1 + + 2 + + 1 + + + VAT + + 1 + 66465935-48e0-b7d9-ea88-d9d7ff4c6023 + Refined Concrete Pants + szt. + 8 + 295.81 + 2366.48 + 23 + + + 2 + 16d35141-c43f-98fc-5249-27d7b2845cf7 + Sleek Steel Pants + szt. + 10 + 402.96 + 4029.60 + 23 + + + 3 + 8647a6b5-f12a-3817-869e-010e4353686a + Incredible Steel Salad + szt. + 8 + 11.13 + 89.04 + 23 + + + 4 + 6a9b993d-33c1-eba2-f06c-7d8f22e7430b + Ergonomic Soft Fish + szt. + 8 + 34.03 + 272.24 + 23 + + + 5 + 05e42590-9bfb-10c2-a946-fd1641f705e7 + Intelligent Frozen Cheese + szt. + 3 + 501.86 + 1505.58 + 23 + + + 6 + dbb7aed5-fedb-96d7-0afd-c3d0c23740a7 + Ergonomic Metal Bike + szt. + 6 + 398.24 + 2389.44 + 23 + + + 1 + 2025-07-17 + 7 + + + \ No newline at end of file diff --git a/office-alexander-logistics-app/src/test/resources/ksef/test.json b/office-alexander-logistics-app/src/test/resources/ksef/test.json new file mode 100644 index 0000000..7a11fad --- /dev/null +++ b/office-alexander-logistics-app/src/test/resources/ksef/test.json @@ -0,0 +1,11 @@ +{ + "formCode": { + "systemCode": "FA (3)", + "schemaVersion": "1-0E", + "value": "FA" + }, + "encryption": { + "encryptedSymmetricKey": "bmrsRHK0uyMRB31FPLOocExKRbQMyiaqN0fzUVnwqiWWAnIySB/89iqHpGWOAiOkixrHIHRFQPHu8aUV33rv+SlCSfv/UIAuLO6FN3VgzVnXITUXFvWUE7Exi1GwJOHOPHp2+tk81Ca5WbPF3Hg3vQQoRo4YSYT7GRV63PVBSNrhHayXDRTT40dJEfLJ25/QzX7EuhV0PQU8sz/4j+kHFVEGTG6KkMN+sqctSRItauoUq7oNjoCw6zqPISvAu/FKxVmq5DxKMj2yFIJQfv9S+xIIpwQDYEPdI1s5ZD3vWtoApWUq4nH9Kbon1ma8yId5aV6PvlDG9oqKN+xvbpodzQ==", + "initializationVector": "81LUdc7vjDZtG239K+1WKg==" + } +} \ No newline at end of file