BMF-RKSV-Technik / at-registrierkassen-mustercode

111 stars 39 forks source link

Kryptographische Gültigkeit der Signatur nicht gegeben #133

Closed WalMi closed 8 years ago

WalMi commented 8 years ago

Guten Abend,

ich will derzeit das Kassasystem implementieren, habe vor einigen Tagen das A-Trust-Signaturmodul erhalten und habe seitdem mit dem Problem zu kämpfen, dass die Kryptographische Gültigkeit der Signatur nicht gegeben ist. Angefügt habe ich hier die Meldung des Prüftools (hier nur mit dem Startbeleg, es ist überall das gleiche. Alles andere passt, aber die Signatur eben nicht) Meldung-dep.txt

Die Überprüfung erfolgte mit der 0.6-Version - mit der 0.7 Version ist es das gleiche. Hier die Grunddaten der Überprüfung (da die Übertragung von *.json nicht funktioniert hat, habe ich überall ein .txt dazugehängt) : cryptographicMaterialContainer.json.txt dep-export.json.txt qr-code-rep.json.txt

Ich arbeite mit Eurem Java-Code - also mit Eurer virtuellen Signierung funktioniert alles. Das Signiermodul habe ich mit dem APDU-Java-Code von A-Trust angebunden. Der Zugriff auf die Karte läuft einwandfrei, auch der Mustercode von A-Trust funktioniert. Habe jetzt den Fehler auf einige Zeilen lokalisiert - aber stecke hier schon seit mehreren Tagen fest :

In der Funktion public String signMachineCodeRepOfReceipt wird am Anfang der zu verschlüssende Text genieriert. machineCodeRepOfReceipt ist [_R1-AT100_CASHBOX-DEMO-1_CASHBOX-DEMO-1-Receipt-ID-1_2016-09-07T23:10:18_0,00_0,00_0,00_0,00_0,00_4r1iIdZGeAQ=_64a659a9cg8hNU5ihto=] Daraus wird mit den Zeilen

String jwsHeaderBase64Url = "eyJhbGciOiJFUzI1NiJ9";
String jwsPayloadBase64Url = CashBoxUtils.base64Encode(machineCodeRepOfReceipt.getBytes(), true);
String jwsDataToBeSigned = jwsHeaderBase64Url + "." + jwsPayloadBase64Url;

der zu signierende Text gemacht - hier der passende Wert - jwsDataToBeSigned:[eyJhbGciOiJFUzI1NiJ9.X1IxLUFUMTAwX0NBU0hCT1gtREVNTy0xX0NBU0hCT1gtREVNTy0xLVJlY2VpcHQtSUQtMV8yMDE2LTA5LTA3VDIzOjEwOjE4XzAsMDBfMCwwMF8wLDAwXzAsMDBfMCwwMF80cjFpSWRaR2VBUT1fNjRhNjU5YTlfY2c4aE5VNWlodG89]

Folgende beiden Zeilen tun dies dann in ein byte[] umwandeln, und anschließend mit der Funktion signieren, die ich vom A-Trust-Mustercode so übernommen habe und offensichtlich auch zu funktionieren scheint :

byte[] ZuSignieren = jwsDataToBeSigned.getBytes();
byte[] signatureResult = MySigMod.signData(ZuSignieren);

Das Ergebnis signatureResult ist lt. den Issues hier nicht DER-Codiert (bringt auch eine Fehlermeldung), so dass hier im Gegensatz zu Eurem Code diese Umwandlung nicht stattfindet. Lt. den Issues hier enthält signatureResult die beiden Vektoren für den Schlüssel, und brauchen nur mehr mit Base64 umgewandelt zu werden und dann als fertiges Ergebnis angehängt zu werden :

String jwsSignatureBase64Url = CashBoxUtils.base64Encode(signatureResult, true);
System.out.println("ManualJWSModuleNeu Zeile101 - return:["+jwsHeaderBase64Url + "." + jwsPayloadBase64Url + "." + jwsSignatureBase64Url+"]");

In dem Fall ist der Wert von jwsSignatureBase64Url: [_yZ2_lSPPHEeracBFZt6YE2zEl8vdQfeqzwHh6amHYrXCdLfYFtbUrIgHSyYhjYzLaUakIGhwv4ZsQvEdhuarA] und der gesamte Rückgabewert der Funktion lautet (und damit auch der gleiche Wert, der im DEP-File steht und für die Verification verwendet wird) : [_eyJhbGciOiJFUzI1NiJ9.X1IxLUFUMTAwX0NBU0hCT1gtREVNTy0xX0NBU0hCT1gtREVNTy0xLVJlY2VpcHQtSUQtMV8yMDE2LTA5LTA3VDIzOjEwOjE4XzAsMDBfMCwwMF8wLDAwXzAsMDBfMCwwMF80cjFpSWRaR2VBUT1fNjRhNjU5YTlfY2c4aE5VNWlodG89.yZ2_lSPPHEeracBFZt6YE2zEl8vdQfeqzwHh6amHYrXCdLfYFtbUrIgHSyYhjYzLaUakIGhwv4ZsQvEdhuarA]

So, und jetzt stecke ich fest. Ich habe folgende Möglichkeiten im Visier :

Der Fehler ist in der Vorbereitung der zu Signierenden Daten Aber bis zu der Variable ZuSignieren bin ich noch identisch mit der funktionierenden Version ohne externem Signiermodul. Was mir hier einfallen würde - ich brauche die SHA256 Verschlüsselung ja nicht selber vorzunehmen - das wird ja vom Signiermodul gemacht ?

Der Fehler steckt in der Signierung Da weiss ich nicht, wie ich das anders machen kann oder wie ich das überprüfen kann. Habe einfach die Funktion der Testumgebung genommen, und hier die Daten übergeben. Aber das verwendete Zertifikat müsste stimmen, habe ja nur eines. Ich müsste irgendwie zur Kontrolle das Ergebnis wieder mit meinem öffentlichen Schlüssel dekodieren, und sollte dann die Eingabe bekommen. Da fehlt mir jetzt der Ansatz dazu, aber das macht ja eigentlich das Verifications-Tool. Was mir auffällt - in Eurem Mustercode sprecht Ihr von "SHA256withECDSA" In meinem Zertifikat liest man ein sha256RSA aber auch ein _ECDSAP256 womit es ja eigentlich wieder passen müsste?

Der Fehler steckt in der Nachbereitung Also was ich nicht mache (und auch einen Fehler bringt), ist die DER-Decodierung mit byte[] jwsSignature = CryptoUtil.convertDEREncodedSignatureToJWSConcatenated(signatureResult); So ist es bei Euch im Originalcode drin - aber brauchen wir lt. den Issues hier ja nicht ? Habe einfach direkt mit base64Encode umgewandelt.

Der Fehler ist bei der Überprüfung Würde bedeuten, dass das Zertifikat nicht stimmt? Also ich habe mal das Zertifikat als Base64 exportiert, und dann in den crypthographicMaterialContainer eingefügt. War identisch mit dem vom Programm generiertem, und erzeugte einen Fehler. Auf Wunsch kann ich gerne das Zertifikat nachreichen, weiss jetzt nicht, ob das so gut ist, es öffentlich dazuzuhängen? Obwohl es ja nur der öffentliche Schlüssel ist.

Sonst fällt mir derzeit nichts mehr ein, komme einfach nicht weiter. Vielleicht sehe ich den Wald vor lauter Bäumen nicht, könnt Ihr mir weiterhelfen ?

Noch eine Frage - die Kassen-ID fängt mit _R1-AT100 an. Das AT100 steht ja für den Verschlüsselungsprovider, wäre also A-Trust. Ist der Code richtig ? Oder sogar der Grund, warum die Verification einen Fehler produziert ?

Danke im Voraus für Eure Hilfe, LG Michael

MartinDomig commented 8 years ago

Noch eine Frage - die Kassen-ID fängt mit _R1-AT100 an. Das AT100 steht ja für den Verschlüsselungsprovider, wäre also A-Trust. Ist der Code richtig ? Oder sogar der Grund, warum die Verification einen Fehler produziert ?

Nein. A-Trust ist AT1, Globaltrust wäre AT2.

Wenn ich nicht irre steht AT100 für ein geschlossenes Gesamtsystem.

-Martin

Am 23.09.2016 um 23:52 schrieb WalMi notifications@github.com:

Guten Abend,

ich will derzeit das Kassasystem implementieren, habe vor einigen Tagen das A-Trust-Signaturmodul erhalten und habe seitdem mit dem Problem zu kämpfen, dass die Kryptographische Gültigkeit der Signatur nicht gegeben ist. Angefügt habe ich hier die Meldung des Prüftools (hier nur mit dem Startbeleg, es ist überall das gleiche. Alles andere passt, aber die Signatur eben nicht) Meldung-dep.txt

Die Überprüfung erfolgte mit der 0.6-Version - mit der 0.7 Version ist es das gleiche. Hier die Grunddaten der Überprüfung (da die Übertragung von *.json nicht funktioniert hat, habe ich überall ein .txt dazugehängt) : cryptographicMaterialContainer.json.txt dep-export.json.txt qr-code-rep.json.txt

Ich arbeite mit Eurem Java-Code - also mit Eurer virtuellen Signierung funktioniert alles. Das Signiermodul habe ich mit dem APDU-Java-Code von A-Trust angebunden. Der Zugriff auf die Karte läuft einwandfrei, auch der Mustercode von A-Trust funktioniert. Habe jetzt den Fehler auf einige Zeilen lokalisiert - aber stecke hier schon seit mehreren Tagen fest :

In der Funktion public String signMachineCodeRepOfReceipt wird am Anfang der zu verschlüssende Text genieriert. machineCodeRepOfReceipt ist [R1-AT100_CASHBOX-DEMO-1_CASHBOX-DEMO-1-Receipt-ID-1_2016-09-07T23:10:18_0,00_0,00_0,00_0,00_0,00_4r1iIdZGeAQ=_64a659a9_cg8hNU5ihto=] Daraus wird mit den Zeilen

String jwsHeaderBase64Url = "eyJhbGciOiJFUzI1NiJ9"; String jwsPayloadBase64Url = CashBoxUtils.base64Encode(machineCodeRepOfReceipt.getBytes(), true); String jwsDataToBeSigned = jwsHeaderBase64Url + "." + jwsPayloadBase64Url; der zu signierende Text gemacht - hier der passende Wert - jwsDataToBeSigned:[eyJhbGciOiJFUzI1NiJ9.X1IxLUFUMTAwX0NBU0hCT1gtREVNTy0xX0NBU0hCT1gtREVNTy0xLVJlY2VpcHQtSUQtMV8yMDE2LTA5LTA3VDIzOjEwOjE4XzAsMDBfMCwwMF8wLDAwXzAsMDBfMCwwMF80cjFpSWRaR2VBUT1fNjRhNjU5YTlfY2c4aE5VNWlodG89]

Folgende beiden Zeilen tun dies dann in ein byte[] umwandeln, und anschließend mit der Funktion signieren, die ich vom A-Trust-Mustercode so übernommen habe und offensichtlich auch zu funktionieren scheint :

byte[] ZuSignieren = jwsDataToBeSigned.getBytes(); byte[] signatureResult = MySigMod.signData(ZuSignieren); Das Ergebnis signatureResult ist lt. den Issues hier nicht DER-Codiert (bringt auch eine Fehlermeldung), so dass hier im Gegensatz zu Eurem Code diese Umwandlung nicht stattfindet. Lt. den Issues hier enthält signatureResult die beiden Vektoren für den Schlüssel, und brauchen nur mehr mit Base64 umgewandelt zu werden und dann als fertiges Ergebnis angehängt zu werden :

String jwsSignatureBase64Url = CashBoxUtils.base64Encode(signatureResult, true); System.out.println("ManualJWSModuleNeu Zeile101 - return:["+jwsHeaderBase64Url + "." + jwsPayloadBase64Url + "." + jwsSignatureBase64Url+"]"); In dem Fall ist der Wert von jwsSignatureBase64Url: [yZ2_lSPPHEeracBFZt6YE2zEl8vdQfeqzwHh6amHYrXCdLfYFtbUrIgHSyYhjY_zLaUakIGhwv4ZsQvEdhuarA] und der gesamte Rückgabewert der Funktion lautet (und damit auch der gleiche Wert, der im DEP-File steht und für die Verification verwendet wird) : [eyJhbGciOiJFUzI1NiJ9.X1IxLUFUMTAwX0NBU0hCT1gtREVNTy0xX0NBU0hCT1gtREVNTy0xLVJlY2VpcHQtSUQtMV8yMDE2LTA5LTA3VDIzOjEwOjE4XzAsMDBfMCwwMF8wLDAwXzAsMDBfMCwwMF80cjFpSWRaR2VBUT1fNjRhNjU5YTlfY2c4aE5VNWlodG89.yZ2_lSPPHEeracBFZt6YE2zEl8vdQfeqzwHh6amHYrXCdLfYFtbUrIgHSyYhjY_zLaUakIGhwv4ZsQvEdhuarA]

So, und jetzt stecke ich fest. Ich habe folgende Möglichkeiten im Visier :

Der Fehler ist in der Vorbereitung der zu Signierenden Daten Aber bis zu der Variable ZuSignieren bin ich noch identisch mit der funktionierenden Version ohne externem Signiermodul. Was mir hier einfallen würde - ich brauche die SHA256 Verschlüsselung ja nicht selber vorzunehmen - das wird ja vom Signiermodul gemacht ?

Der Fehler steckt in der Signierung Da weiss ich nicht, wie ich das anders machen kann oder wie ich das überprüfen kann. Habe einfach die Funktion der Testumgebung genommen, und hier die Daten übergeben. Aber das verwendete Zertifikat müsste stimmen, habe ja nur eines. Ich müsste irgendwie zur Kontrolle das Ergebnis wieder mit meinem öffentlichen Schlüssel dekodieren, und sollte dann die Eingabe bekommen. Da fehlt mir jetzt der Ansatz dazu, aber das macht ja eigentlich das Verifications-Tool. Was mir auffällt - in Eurem Mustercode sprecht Ihr von "SHA256withECDSA" In meinem Zertifikat liest man ein sha256RSA aber auch ein ECDSA_P256 womit es ja eigentlich wieder passen müsste?

Der Fehler steckt in der Nachbereitung Also was ich nicht mache (und auch einen Fehler bringt), ist die DER-Decodierung mit byte[] jwsSignature = CryptoUtil.convertDEREncodedSignatureToJWSConcatenated(signatureResult); So ist es bei Euch im Originalcode drin - aber brauchen wir lt. den Issues hier ja nicht ? Habe einfach direkt mit base64Encode umgewandelt.

Der Fehler ist bei der Überprüfung Würde bedeuten, dass das Zertifikat nicht stimmt? Also ich habe mal das Zertifikat als Base64 exportiert, und dann in den crypthographicMaterialContainer eingefügt. War identisch mit dem vom Programm generiertem, und erzeugte einen Fehler. Auf Wunsch kann ich gerne das Zertifikat nachreichen, weiss jetzt nicht, ob das so gut ist, es öffentlich dazuzuhängen? Obwohl es ja nur der öffentliche Schlüssel ist.

Sonst fällt mir derzeit nichts mehr ein, komme einfach nicht weiter. Vielleicht sehe ich den Wald vor lauter Bäumen nicht, könnt Ihr mir weiterhelfen ?

Noch eine Frage - die Kassen-ID fängt mit _R1-AT100 an. Das AT100 steht ja für den Verschlüsselungsprovider, wäre also A-Trust. Ist der Code richtig ? Oder sogar der Grund, warum die Verification einen Fehler produziert ?

Danke im Voraus für Eure Hilfe, LG Michael

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.

ErichFreitag commented 8 years ago

@WalMi und @MartinDomig - AT100 ist ein fiktiver Provider, genauso wie AT75 in den Beispielen. Bei geschlossenen Gesamtsystemen ist "AT0" anzugeben (RSKV Detailspez. Punkt 2). Was dort steht ist für die Signaturerstellung unerheblich.

@WalMi - mangels tief genug gehender Kenntnisse kann ich keine fertige Lösung anbieten, nur ein paar Denkansätze:

a) Was mir ebenso wie ihnen auffällt ist der Signaturalgorithmus im Zertifikat:

In den A-SIT-Beispielen enthält das Zertifikat den SigAlgName "SHA256withECDSA". In ihren Zertifikatsdaten steht jedoch beim SigAlgName "SHA256withRSA".

Das deutet - warum und woher auch immer - auf einen anderen Signaturalgorithums hin. Richtig ist "SHA256withECDSA".

b) wenn ich mit (m)einem Prüftool die Signaturprüfung durchführe, muss ich die Signatur auf DER umwandeln:

byte[] sigDecoded = Base64.decode( signatureValue ); byte[] sigConverted = org.jose4j.jws.EcdsaUsingShaAlgorithm.EcdsaP256UsingSha256.convertConcatenatedToDer(sigDecoded);

Das deutet darauf hin, dass die DER-Umwandlung erforderlich ist. Mit ihrem Signaturwert von yZ2/lSPPHEeracBFZt6YE2zEl8vdQfeqzwHh6amHYrXCdLfYFtbUrIgHSyYhjY/zLaUakIGhwv4ZsQvEdhuarA== ergibt sich

Sig decoded [-55, -99, -65, -107, 35, -49, 28, 71, ... Sig converted [48, 70, 2, 33, 0, -55, -99, -65, -107, 35, -49, 28, 71, ... sprich es kommen für die Verifikation 5 bytes im DER-Format dazu.

Hope this helps...

ErichFreitag commented 8 years ago

@WalMi - P.S. was ich auch gerade gelernt habe, ist, dass z.B. auf einer A-Trust-Karte ein Zertifikat mit RSA und eines mit ECC enthalten ist. Siehe https://www.a-trust.at/ATrust/Produkte/asignPremium.aspx#Produktinformationen und http://wiki.a-trust.at/wiki/Default.aspx?site=e-Card+FAQ.

Die Vermutung liegt daher nahe, dass sie das falsche Zertifikat verwenden. Dazu (und auch zum DER) können wohl Issues wie https://github.com/a-sit-plus/at-registrierkassen-mustercode/issues/105 oder https://github.com/a-sit-plus/at-registrierkassen-mustercode/issues/21 weiterhelfen.

asitplus-pteufl commented 8 years ago

@WalMi, bitte wie @ErichFreitag beschreibt unbedingt prüfen ob DER kodiert (Sie können ja einfach die Routinen aus dem Muster-Code nehmen und dann überprüfen ob die Sig-Prüfung funktioniert).

Aber mich stört noch etwas anderes in Ihrer Beschreibung: Sie schreiben: "Was mir hier einfallen würde - ich brauche die SHA256 Verschlüsselung ja nicht selber vorzunehmen - das wird ja vom Signiermodul gemacht", ich kenne jetzt den Mustercode für die Karte nicht, aber typischerweise erwarten Smartcards einen Hashwert, der dann signiert wird. D.h. Sie müssen den SHA256-Hashwert über die zu signierenden Daten rechnen (Aufbereitung so wie von Ihnen beschrieben) und dann an die Karte senden. Im Muster-Code ist das nicht explizit ersichtlich da das an sich der Signatur-Provider von Bouncycastle macht.

Bezüglich dem Zertifikat: Das Zertifikat selbst ist ja auch signiert, da kann durchaus sein dass sie wo einen "SHA256withRSA" sehen. Das tritt dann auf wenn die ausstellende Stelle diesen Algorithmus zum Signieren des Zertifikats verwendet.

WalMi commented 8 years ago

Guten Abend,

hurra, es funktioniert ...

Zuerst mal @MartinDomig und @ErichFreitag - Danke für die Klarstellung der Kassen-Id. Ändere das dann ab, so dass ich für A-Trust mit AT-1 als Provider-Kennzeichen arbeite. Danke.

@asitplus-pteufl - Sie hatten recht mit der SHA256 Aufbereitung der Daten. Hätte es eigentlich aus dem A-Trust Mustercode rauslesen müssen - aber da war ich falscher Meinung. Das mit SHA256 im Zertifikat hat mich irritiert. Also es mussten vor der Signierung noch die Daten mit SHA256 verhasht werden.

Eine DER-Transformation war nicht erforderlich - im Gegensatz zum Mustercode war als Eingabewert der Signierung eben dieses SHA256 notwendig. Dafür war aber die Ausgabeseite nicht DER-Codiert, so dass auch hier keine DeCodierung notwendig war. Und die Verification läuft so durch ...

@ErichFreitag - Ganz kurz noch zu A-Trust Karte und den Zertifikaten Es sind zwei Zertifikate drauf. Einmal das höherwertige Signaturzertifikat _(6-stellige PIN, Signaturalgorythmus das irritierende sha256RSA, Signaturhashalgorythmus sha256, Öffentlicher Schlüssel ECC (256 Bit), Parameter für öffentlichen Schlüssel ECDSAP256) Dann das niederwertigere Geheimhaltungszertifikat (4-stellige PIN, Signaturalgorythmus auch hier sha256RSA, Signaturhashalgorythmus sha256, Öffentlicher Schlüssel RSA 2048 bits) Verwendet wurde das Signaturzertifikat

Und hier noch der bei mir funktionierende Code

// in der Function   public String signMachineCodeRepOfReceipt
// ManualJWSModule - die ersten Zeilen sind wie im Original
byte[] ZuSignieren = jwsDataToBeSigned.getBytes();
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(ZuSignieren);
byte[] ZuSignierenSHA256 = md.digest();
byte[] signatureResult = MySigMod.signData(ZuSignierenSHA256);
String jwsSignatureBase64Url = CashBoxUtils.base64Encode(signatureResult, true);
return jwsHeaderBase64Url + "." + jwsPayloadBase64Url + "." + jwsSignatureBase64Url;
// Die Funktion signData stammt von den APDU-Beispielen, Java-Code von A-Trust
//[http://labs.a-trust.at/developer/ShowSource.aspx?id=114]

Herzlichen Dank allen für die schnelle und kompetente Hilfe,

LG Michael

ErichFreitag commented 8 years ago

Bestens und gerne.

P.S. "AT1" ohne Bindestrich.