Open DavStock opened 7 years ago
Der Startbeleg muss einen Barumsatz von 0 haben.
BItte die Folien im Readme beachten. direkter link: https://github.com/a-sit-plus/at-registrierkassen-mustercode/blob/master/Dokumente/2016-12-12%20SIG-Check.pdf
Vielleicht haben Sie Padding-Zeichen (=) im JWS-Base64? Die sind in der JWS-Spez. nicht erlaubt.
@ErichFreitag entschuldige - ich habe den falschen maschinenlesbaren code genommen. Beim getesteten ist der Umsatz natürlich 0,00 !! Es ist egal welche Belegart ich teste ich bekomme immer denselben Fehler (bei Storno-, Null- oder Trainingsbelegen).
@asitplus-pteufl Das mit den "=" muss ich mir genauer ansehen. D.h. nach Base64Url Umwandlung der Plain-Payload müsste ich noch "=" mit "" ersetzen?! Danke.
ja, bitte unbedingt die folien von oben im Detail anschauen, da sind alle Punkte drinnen die bei der Sig-Erstellung schief gehen können.
Ich habe das Coding nun zusätzlich abgesichert wegen dem "=". Die Folien habe ich auch noch einmal durchstudiert und das Coding stimmt 1:1 mit der Vorgehensweise überein. Es passen auch alle Überprüfungen laut Testtool bis auf dieser kryptografische Fehler in der Signatur.
Was ich dabei nicht ganz verstehe - in der Meldung steht immer, dass dies mit einem öffentlichen Schlüssel abgeglichen wird. Welcher öffentlicher Schlüssel?
Hier noch mein maschinenlebarer Code als Startbeleg: qr-code-rep.txt
...und das Ergebnis via Testtool: 0000_cashbox_full.txt
Ich prüfe gerade noch die Signaturerstellung selbst (wegen DER-Format) aber da die Signierung vom aTrust Coding ausgeführt wird glaube ich nicht hier noch Fehler zu finden.
Ich probiers nochmal mit einer Anfrage und genaueren Daten. Habe leider nach wie vor diesen Fehler beim Startbeleg (bei jedem Beleg). Also Schritt für Schritt:
Umsatzzähler bei Startbeleg wird mit dem Wert 0 und dem AES verschlüsselt und sieht so aus (decrypting wird erfolgreich geprüft - Wert = 0):
fQeXmFQxrUJZ8OdM87aKsw==
Plain Payload besteht aus 12 Elementen:
_R1-AT1_KRO-CTG-WSRKA_271_2017-03-30T10:18:28_0,00_0,00_0,00_0,00_0,00_fQeXmFQxrUJZ8OdM87aKsw==_7F4D87C4_S1JPLUNURy0=
JWS-Header:
eyJhbGciOiJFUzI1NiJ9
JWS-Payload:
X1IxLUFUMV9LUk8tQ1RHLVdTUktBXzI3MV8yMDE3LTAzLTMwVDEwOjE4OjI4XzAsMDBfMCwwMF8wLDAwXzAsMDBfMCwwMF9mUWVYbUZReHJVSlo4T2RNODdhS3N3PT1fN0Y0RDg3QzRfUzFKUExVTlVSeTA9
Die beiden zusammen, also "JWS-Header.JWS-Payload" werden von "=" befreit, SHA256 verschlüsselt und über die aTrust API signiert (ich gehe bisher davon aus, dass diese kein DER-Format hat). Die zurückgebene Signatur wird Base64Url behandelt und sieht wie folgt aus:
RHtFNNxC0TttMqctr8bPIFPgsp1PXTyNLbOfBWuZ0oVtHgn1REoSDGEjaezILUYikYJG/FPu5RE0kWPnmL3VCA==
Hier liegt angeblich der Fehler. C# Coding zum erstellen der Signatur wäre wie folgt:
RKWrapper rkw = new RKWrapper();
SHA256Managed h = new SHA256Managed();
byte[] toBeHashed = Encoding.UTF8.GetBytes(string.Format("{0}.{1}", JWS_Protected_Header.Replace("=", ""), JWS_Payload.Replace("=", "")));
tbsBytes = h.ComputeHash(toBeHashed);
byte[] signature;
ret = rkw.Sign(tbsBytes, out signature);
JWS_Signature = Convert.ToBase64String(signature);
Zum Erstellen des QR-Codes (und zum Testen im Prüftool) nehme ich dann folgenden String (bestehend aus 13 Elementen):
_R1-AT1_KRO-CTG-WSRKA_271_2017-03-30T10:18:28_0,00_0,00_0,00_0,00_0,00_fQeXmFQxrUJZ8OdM87aKsw==_7F4D87C4_S1JPLUNURy0=_RHtFNNxC0TttMqctr8bPIFPgsp1PXTyNLbOfBWuZ0oVtHgn1REoSDGEjaezILUYikYJG/FPu5RE0kWPnmL3VCA==
Der QR-Code wird mit dem ECC-Level "M" und 2px pro Modul erstellt.
Schauen sie auch die anderen Issues zu diesem Thema durch. Manchmal macht z.B. schon die Signierfunktion auch einen Hash, sodass dann einer zuviel wäre.
@ErichFreitag habe das nach meinem Kommentar noch einmal kontrolliert und gesehen, dass dort auch ein Coding für einen Hash enthalten ist. Dies wäre der Auszug aus dem Coding welches ich 1:1 von aTrust übernommen habe:
IntPtr pSig = IntPtr.Zero;
IntPtr pTBS = IntPtr.Zero;
try
{
ulong tbsLen = (ulong)dataToSign.Length;
ulong sigLen = 256;
pSig = Marshal.AllocHGlobal(256);
pTBS = Marshal.AllocHGlobal(dataToSign.Length);
if (pSig == IntPtr.Zero || pTBS == IntPtr.Zero)
{
LastError = "Sign: memory allocation failed";
ret = 0x06;
}
else
{
Marshal.Copy(dataToSign, 0, pTBS, dataToSign.Length);
if (is32Bit)
{
Sign32(ref signature, ref ret, pSig, pTBS, tbsLen, sigLen);
}
else
{
Sign64(ref signature, ref ret, pSig, pTBS, tbsLen, sigLen);
}
}
}
catch (Exception ex)
{
ret = 0x6; // Function failed
LastError = "Sign Exception : " + ex.Message;
}
finally
{
// Free memory
Marshal.FreeHGlobal(pSig);
}
}
Meiner Meinung nach wird hier in den ersten paar Zeilen der Hash erstellt. Habe daher testhalber meinen Hash wieder entfernt und erneut signiert - die Fehlermeldung bleibt leider die selbe.
Wie sieht der Zertifikatseintrag im crypto-file aus?
Mein Crypto-File sieht aktuell wie folgt aus:
{ "base64AESKey" : "+PVmNm3LMsRxLvZkJDY/7g6tNjkJCPQH2kUHXyzCzy0=", "certificateOrPublicKeyMap" : { "7F4D87C4" : { "id" : "7F4D87C4", "signatureDeviceType" : "CERTIFICATE", "signatureCertificateOrPublicKey" : "049F18731B3BF3492B0744D34FB6B9C02E0B3E929E38B807157EFD0EBD996F533587988DA54A6B73DD45F974F5825FCFDC97CB0C47AA6953EF8E0BFC859CFBAA41" } } }
Ich habe hier mal den PublicKey der Karte verwendet. Es ist aber egal ob ich die Zertifikatsnummer bei "signatureCertificateOrPublicKey" eintrage oder den Public Key oder die Rohdaten der Karte.
Sie müssen das Zertifikat im .PEM-Format eintragen.
Wenn ich das .cer in ein .pem konvertiere erhalte ich nur eine neue Datei - wie trage ich die nun ein? Entschuldigen Sie @ErichFreitag - da steh ich doch etwas am Schlauch.
Das Zertifikat im .PEM-Format sieht typisch wie folgt aus: -----BEGIN CERTIFICATE----- MIIFPzCCAyegAwIBAgIELwe0czANBgkqhkiG9w0BAQsFADCBoTELMAkGA1UEBhMC QVQxSDBGBgNVBAoMP0EtVHJ1c3QgR2VzLiBmLiBTaWNoZXJoZWl0c3N5c3RlbWUg aW0gZWxl... -----END CERTIFICATE-----
Den Inhalt zwischen BEGIn und END nehmen sie, entfernen alle Zeilenumbrüche und tragen es als Zertifikatswert unter signatureCertificateOrPublicKey ein.
Nun sieht mein Crypto-File so aus:
{ "base64AESKey" : "+PVmNm3LMsRxLvZkJDY/7g6tNjkJCPQH2kUHXyzCzy0=", "certificateOrPublicKeyMap" : { "7F4D87C4" : { "id" : "7F4D87C4", "signatureDeviceType" : "CERTIFICATE", "signatureCertificateOrPublicKey" : "MIIFUDCCAzigAwIBAgIEf02HxDANBgkqhkiG9w0BAQsFADCBoTELMAkGA1UEBhMCQVQxSDBGBgNVBAoMP0EtVHJ1c3QgR2VzLiBmLiBTaWNoZXJoZWl0c3N5c3RlbWUgaW0gZWxla3RyLiBEYXRlbnZlcmtlaHIgR21iSDEjMCEGA1UECwwaQS1UcnVzdCBSZWdpc3RyaWVya2Fzc2UuQ0ExIzAhBgNVBAMMGkEtVHJ1c3QgUmVnaXN0cmllcmthc3NlLkNBMB4XDTE3MDIwMjA4NDM1NloXDTIyMDIwMjA3NDM1NlowQDELMAkGA1UEBhMCQVQxGjAYBgNVBAMMEUdMTiA5MTEwMDE1ODY3MDA4MRUwEwYDVQQFEwwxNzcxMjg1MTM5MzAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASfGHMbO/NJKwdE00+2ucAuCz6Snji4BxV+/Q69mW9TNYeYjaVKa3PdRfl09YJfz9yXywxHqmlT744L/IWc+6pBo4IBuTCCAbUwfwYIKwYBBQUHAQEEczBxMEYGCCsGAQUFBzAChjpodHRwOi8vd3d3LmEtdHJ1c3QuYXQvY2VydHMvQS1UcnVzdC1SZWdpc3RyaWVya2Fzc2UtQ0EuY2VyMCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcC5hLXRydXN0LmF0L29jc3AwDgYDVR0PAQH/BAQDAgbAMBEGA1UdDgQKBAhLV7tFSZMfhzBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsLmEtdHJ1c3QuYXQvY3JsL0EtVHJ1c3QtUmVnaXN0cmllcmthc3NlLkNBMAkGA1UdEwQCMAAwWAYDVR0gBFEwTzBNBgYqKAARARgwQzBBBggrBgEFBQcCARY1aHR0cDovL3d3dy5hLXRydXN0LmF0L2RvY3MvY3AvQS1UcnVzdC1SZWdpc3RyaWVya2Fzc2UwEwYDVR0jBAwwCoAIQEeeruOQ37YwLgYDVR0RBCcwJYEjcmVuYXRlLmtyb2dnZXJAa3JvZ2dlci13ZXJrc3RhdHQuYXQwHgYHKigACgELAQQTDBFHTE4gOTExMDAxNTg2NzAwODANBgkqhkiG9w0BAQsFAAOCAgEAerZprhyMOB/lTc6SIDDL757f4pnlZpQGOt+OK61r7mk2AKoQzc+PEn9ZNJ4/kuvBOdJOV86Kmi/BPZ9pF9u8zQ1RomK1qJpcnIAiFjXCVlr9lcy57eKlzQo8/w6v4TjbVcgaMGT14Wg6a8/GhD3ovhEfFhtWpav2ftbocF3JlW0JDNIzlvRORUxEHr7so/sVHYHK3l4xCaW2Ju53g8FMsrSCrkT3fwCGEo8GHl9DxZlrJ5aB4Bvn9Y9hiOYXdK495Ipod1wpMFlQXWIjAOyVam8pD5pqt7I5dGpNBZsnoT/W4D7M2u5XXN2sqfdfXB0G/9NC2Gt89eBeyWypxs2lFwaXaf7A9Pwd9bt7WekN/+RmMBV0xbGtslL83Lt7HUYTmv9NOdtnhbFL2+FH1+L01QLbRM4A4BWRqDEFvDKh8Tm5afvkWhNXk6xATK9LXUzkD0g7Tl9C3QeEWWYkpmbMvQKBmFQgT+jIzODChdLsnnZyU91v46oxa+52lCIl+59puBagbt4yBOBheWuJH51+YjvWw5Rt426Tc1HLPiw1YIULGRXMM2vjnzxUsSL1Psb1m/P+l3lldt17x6Z3kGuHqagKsRAYTlDNSh7nS3Fr+6gabZM/vgdmkRLNgPdI0tcO4SA/C33rqhUlgvDWsFGIwlT9itfQ6NCE0AJT4Qh2Zjw=" } } }
.....also mit Zertifikat im PEM Format. Fehlermeldung im Prüftool:
Die kryptographische Gültigkeit der Signatur ist nicht gegeben. Es scheint aber nicht der definierte Fall der ausgefallenen Sicherheitseinrichtung zuzutreffen.
Es hat sich leider nichts verändert 👎
Schön langsam - jetzt stimmt mal das. Haben sie jetzt betreff Hash oder nicht-Hash nochmals nachgesehen? Prüfen sie bitte auch, ob der BASE64-codierte Signaturwert 88 Zeichen lang ist.
Der zuletzt getestete Base64 codierte Signaturwert
RHtFNNxC0TttMqctr8bPIFPgsp1PXTyNLbOfBWuZ0oVtHgn1REoSDGEjaezILUYikYJG/FPu5RE0kWPnmL3VCA==
hat genau 88 Zeichen. Gehasht wird nur von aTrust Coding beim Signieren.
OK. Nachdem die Inhalte hier schon sehr unübersichtlich sind bitte nochmals von vorne:
Bitte um den DEP-Eintrag den sie prüfen (regkassen-ver-dep) als Datei. Bitte um den QR-Code des Belegs den sie prüfen (regkassen-ver-receipt) als Datei. Bitte um den cryptogaphicMaterialContainer.json als Datei. Bitte um die Prüfergebnisse beider Tools als Datei.
Hallo Herr @ErichFreitag - entschuldigen Sie die späte Antwort! Da kam noch einiges dazwischen. Wenn ich mich nicht täusche benötigen Sie folgende Files von mir: 0000_cashbox_full-DEPTEST.json.txt cryptographicMaterialContainer.json.txt qr-code-rep.json.txt 0000_cashbox_full-QRTEST.json.txt dep-export.json.txt
Egal wie ich das drehe ich habe das Gefühl, dass das Signieren selbst nicht passt. Wenn jede Prüfung erfolgreich bestanden wird aber das Signieren selbst anscheinend nicht?! Wäre für mich nun am naheliegendsten. Danke nochmals für Ihre Zeit.
Danke für die Daten.
Ohne Gewähr sehe ich mal die folgenden Fehler:
der Verkettungswert passt nicht. Möglicherweise soll der Eintrag einen Startbeleg darstellen, es ist aber nur ein Nullbeleg mit einer normalen Belegverkettung anstelle der Verkettung mit der Kassen-ID
im DEP-Export kommt das BASE64-Zeichen "/" vor - der Eintrag des DEP-Exports muss jedoch BASE64URL-codiert sein
unabhängig davon ist auch beim QR-Code-Eintrag die Signatur fehlerhaft, korrekt. Da bleibt leider nur, die Folien unter https://github.com/a-sit-plus/at-registrierkassen-mustercode/blob/master/Dokumente/2016-12-12%20SIG-Check.pdf durchzugehen und/oder sich anhand folgender Grafik durchzuhanteln.
Vielleicht können sie mit der BASE64-Codierung auch schon etwas eingrenzen. Das Zertifikat selbst sieht OK aus.
@ErichFreitag
Ich konnte nun, ohne zu wissen wie, erfolgreich jegliche Art von Belege signieren (keinerlei Änderungen am Coding)! Ich habe noch den Fehler mit dem Verkettungswert entdeckt und erfolgreich behoben (der Hash war inkorrekt).
Der DEP Export war leider auch fehlerhaft wegen, wie sie sagten, der Base64Url Codierung.
Der Umsatzzähler wurde nun auch richtiggestellt da das Decrypting anscheinend auch fehlerhaft war (inkorrekter Hash). Wie gesagt konnte ich nun erfolgreich alle Arten von Belege signieren und kümmere mich nun um das korrekte Eintragen ins DEP.
Vielen Dank für ihre Mühe und Zeit!
Gerne und fein, Herr Stockinger!
In einer ruhigen Minute sollten sie nochmals nachdenken, warum es plötzlich "ohne Änderungen" (?) funktioniert - das ist zumindest bemerkenswert, warum auch immer. Vielleicht die Summe der Anpassungen...
Daran arbeite ich gerade - die Ursache wäre höchst interessant da ich auch für die Zukunft wissen sollte was zu tun ist, wenn dieser Fehler doch wieder auftritt. Da ich die genannten Änderungen erst nachträglich gemacht habe, also nachdem die Kryptographie gepasst hat, ist es nun schwer nachzuvollziehen.
Hallo,
ich habe nun mehrere Versuche in verschiedenste Richtungen gemacht einen Startbeleg zu erstellen und komme absolut nicht weiter. Programmiert wurde in C# und anscheinend gibt es Probleme mit der Signatur bzw. dem maschinenlesbaren Code:
AES:
Ich habe alles nach bestem Wissen und Gewissen lt. BMF Detailanforderungen und aTrust Demos runtercodiert. Eventuell mache ich etwas mit den JWS Daten falsch, denn verwende ich die Base64Url für "Signatureinheit ausgefallen" sagt mir FON, dass der Beleg zwar korrekt signiert wurde aber eben, dass noch kein Startbeleg erstellt wurde. Versuche ich "Echtdaten" zu verschlüsseln und zu signieren bekomme ich den krypografischen Fehler. Hier die Meldung lt. Prüftool:
Es ist doch so, dass der JWS_Header mit "." getrennt mit der Base64Url codierten Payload verbunden, gehasht (UTF8?) - dann signiert und die Signatur als Base64String an die plain Payload angehängt wird um damit den maschinenlesbaren Code zu erhalten (siehe oben) oder nicht?
Coding sieht wie folgt aus (leichte Abänderungen für Demo):
RKWrapper rkw = new RKWrapper(); //*.dll von aTrust - initialisiert die Karte und holt die Certnumber
string payloadPlain = string.Format("_R1-{0}_{1}_{2}_{3}_{4}_{5}_{6}_{7}_{8}_{9}_{10}_{11}", ZDA, cashBoxID, BelegNr, rechDat, rechBetrag, "0,00", "0,00", "0,00", "0,00", umsatz, certSerNr, prevSig)
string JWS_Payload = Base64url(payloadPlain);
string toBeSignedStr = string.Format("{0}.{1}", JWS_Protected_Header, JWS_Payload);
byte[] tbsBytes = Encoding.UTF8.GetBytes(toBeSignedStr);
byte[] signature;
rkw.Sign(tbsBytes, out signature); // hier signiert aTrust *.dll meine Daten?!
string JWS_Signature = Convert.ToBase64String(signature);
string maschinenlesbarerCode = string.Format("{0}_{1}", payload, JWS_Signature);
Irgendeinen Punkt verstehe ich da anscheinen noch nicht ganz und bitte daher um eure Unterstützung.
Danke und LG
PS: Solltet ihr noch irgendwelche Infos / Daten brauchen bitte einfach bescheid geben!