diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/ksef/Params.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/ksef/Params.java index 37bdf5b..f34bb3f 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/ksef/Params.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/ksef/Params.java @@ -2,7 +2,6 @@ package com.alexanderlogistics.ksef; public class Params { public static final String TOKEN = "20251113-EC-2751AC3000-5C5466924B-62|nip-9552521552|fc0c95d020dc45d4af491fb0aa755f3f3f674b33ecbf4250a88ea6e36fa8340b"; - public static final String TOKENx = "20251113-EC-2751AC3000-5C5466924B-62.nip-9552521552.fc0c95d020dc45d4af491fb0aa755f3f3f674b33ecbf4250a88ea6e36fa8340b"; public static final String NIP = "9552521552"; public static String getEncodedToken() { @@ -10,4 +9,5 @@ public class Params { return result; } + } diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/ksef/TestKSeF.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/ksef/TestKSeF.java index 56a5a6c..5465fee 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/ksef/TestKSeF.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/ksef/TestKSeF.java @@ -1,14 +1,27 @@ package com.alexanderlogistics.ksef; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.util.Base64; import java.util.logging.Logger; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; + import org.imixs.workflow.exceptions.ModelException; import org.imixs.workflow.exceptions.PluginException; import org.junit.jupiter.api.BeforeAll; @@ -44,6 +57,9 @@ public class TestKSeF { private final ObjectMapper objectMapper = new ObjectMapper(); + private String refNumber = null; + private String authToken = null; + @BeforeAll public static void setupClass() { logger.info("=== KSeF 2.0 Client Test ==="); @@ -119,6 +135,106 @@ public class TestKSeF { // Prüfen, dass wir eine API-Antwort bekommen assertTrue(response.statusCode() < 500, "API sollte antworten, Status < 500"); assertFalse(response.body().contains(""), "Response sollte keine HTML-Seite sein"); + + // JSON parsen und Werte speichern + if (response.statusCode() == 200 || response.statusCode() == 202) { + JsonNode jsonNode = objectMapper.readTree(response.body()); + + // Reference Number speichern + refNumber = jsonNode.get("referenceNumber").asText(); + logger.info("Extracted referenceNumber: " + refNumber); + + // Authentication Token speichern + authToken = jsonNode.get("authenticationToken").get("token").asText(); + logger.info("Extracted authenticationToken: " + authToken); + } + } + + @Test + @Order(3) + @DisplayName("KSeF Public Key Zertifikate abrufen") + public void testFetchKsefPublicKeys() throws Exception { + assertNotNull(Params.TOKEN, "KSEF_TOKEN environment variable nicht gesetzt"); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(KSEF_TEST_URL + "/security/public-key-certificates")) + .header("Accept", "application/json") + .header("Authorization", "Bearer " + Params.TOKEN) // Access Token nutzen + .GET() + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + + System.out.println("Status: " + response.statusCode()); + System.out.println("Response: " + response.body()); + + // Assertions + assertEquals(200, response.statusCode(), "Request sollte 200 OK zurückgeben"); + assertTrue(response.body().contains("certificate"), "Response sollte ein Feld 'certificate' enthalten"); + } + + @Test + @Order(4) + @DisplayName("Rechnung übermitteln an KSeF 2.0 API") + public void testSendInvoice() throws Exception { + assertNotNull(refNumber, "ReferenceNumber nicht gesetzt"); + + // 1. Rechnung laden + Path invoicePath = Paths.get("test/resources/test-invoice.xml"); + byte[] invoiceBytes = Files.readAllBytes(invoicePath); + int invoiceSize = invoiceBytes.length; + + // 2. SHA-256 Hash der Originalrechnung + MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); + byte[] invoiceHashBytes = sha256.digest(invoiceBytes); + String invoiceHash = Base64.getEncoder().encodeToString(invoiceHashBytes); + + // 3. AES-256 Schlüssel + IV generieren + KeyGenerator keyGen = KeyGenerator.getInstance("AES"); + keyGen.init(256); + SecretKey aesKey = keyGen.generateKey(); + byte[] ivBytes = new byte[16]; + new SecureRandom().nextBytes(ivBytes); + IvParameterSpec iv = new IvParameterSpec(ivBytes); + + // 4. Rechnung verschlüsseln (AES-256-CBC + PKCS7Padding) + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, aesKey, iv); + byte[] encryptedInvoiceBytes = cipher.doFinal(invoiceBytes); + + int encryptedInvoiceSize = encryptedInvoiceBytes.length; + byte[] encryptedInvoiceHashBytes = sha256.digest(encryptedInvoiceBytes); + String encryptedInvoiceHash = Base64.getEncoder().encodeToString(encryptedInvoiceHashBytes); + + // 5. Verschlüsselten Inhalt Base64 codieren + String encryptedInvoiceContent = Base64.getEncoder().encodeToString(encryptedInvoiceBytes); + + // 6. JSON-Payload erstellen + String jsonPayload = "{" + + "\"invoiceHash\":\"" + invoiceHash + "\"," + + "\"invoiceSize\":" + invoiceSize + "," + + "\"encryptedInvoiceHash\":\"" + encryptedInvoiceHash + "\"," + + "\"encryptedInvoiceSize\":" + encryptedInvoiceSize + "," + + "\"encryptedInvoiceContent\":\"" + encryptedInvoiceContent + "\"," + + "\"offlineMode\":false" + + "}"; + + // 7. HTTP POST Request an die API + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(KSEF_TEST_URL + "/sessions/online/" + refNumber + "/invoices")) + .header("Content-Type", "application/json") + .header("Accept", "application/json") + .header("Authorization", "Bearer " + authToken) // Token aus Authentifizierung + .POST(HttpRequest.BodyPublishers.ofString(jsonPayload)) + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + + System.out.println("Status: " + response.statusCode()); + System.out.println("Response: " + response.body()); + + assertEquals(200, response.statusCode(), "Invoice Upload sollte Status 200 zurückgeben"); + assertTrue(response.body().contains("invoiceId"), "Antwort sollte field invoiceId enthalten"); } }