BMF-RKSV-Technik / at-registrierkassen-mustercode

111 stars 39 forks source link

C# Fehlermeldung das der verschlüsselte Umsatzzähler falsch wäre!? #541

Open BRRIT2017 opened 7 years ago

BRRIT2017 commented 7 years ago

Hallo!

Ich bekomme bei der Prüf APP für den QRCode vom BMF folgende Meldung:

Bezeichnung: Prüfergebnis - Kasse Beschreibung: Bei der Belegprüfung wird untersucht, ob die Vorgaben der RKSV in Bezug auf den maschinenlesbaren Code am Beleg und auf die meldepflichtigen Metadaten der Sicherheitseinrichtung befolgt werden. Im Fehlerfall sind die genauen Fehlerinformationen nachfolgend angeführt. Meldung: Der vorliegende Beleg weist Fehler im maschinenlesbaren Code auf. Die Fehlerursachen sind nachfolgend angeführt: Bezeichnung: Detailprüfung des maschinenlesbaren Codes Beschreibung: Dieses Modul und die dazugehörigen Submodule überprüfen die Gültigkeit des Belegs. Dabei werden sowohl Formatprüfungen, kryptographische Prüfungen (verschlüsselter Umsatzzähler, Signatur) als auch Prüfungen im Zusammenhang mit dem Status der Kasse bzw. der Signatur-/Siegelerstellungseinheit durchgeführt. Bezeichnung: Detailformatprüfung des maschinenlesbaren Codes Beschreibung: Dieses Modul und die dazugehörigen Submodule überprüfen die Korrektheit des Formats der Elemente des maschinenlesbaren Codes. Bezeichnung: Formatprüfung: Verkettungswert Beschreibung: In diesem Modul und den dazugehörigen Submodulen werden unterschiedliche Prüfungen im Zusammenhang mit der BASE64-Kodierung (gültige Zeichen, Länge) des Verkettungswerts durchgeführt.

==================================================

Es wird via C# der Code erzeugt, was ist da bloß falsch? Ich benutze den orginal Beispiel Code:

public class RGIVSModule
{
    public static byte[] GenerateKey()
    {
        AesManaged aesKey = new AesManaged();
        aesKey.KeySize = 256;
        aesKey.GenerateKey();

        return aesKey.Key;
    }

    public static byte[] Encrypt(long umsatz, string kassenID, string belegnummer, byte[] aesKey)
    {
        byte[] data = EncodeUmsatz(umsatz);
        byte[] iv = GenerateIV(kassenID, belegnummer);

        return Encrypt(data, iv, aesKey);
    }

    public static long Decrypt(byte[] data, string kassenID, string belegnummer, byte[] aesKey)
    {
        long umsatz = -1;
        byte[] iv = GenerateIV(kassenID, belegnummer);
        byte[] decrypted = Decrypt(data, iv, aesKey);
        if (decrypted != null)
        {
            umsatz = DecodeUmsatz(decrypted);
        }

        return umsatz;
    }

    private static byte[] Encrypt(byte[] data, byte[] IV, byte[] aesKey)
    {
        byte[] encrypted = null;
        IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CTR/NoPadding");

        try
        {
            KeyParameter skey = new KeyParameter(aesKey);
            ParametersWithIV aesIVKeyParam = new ParametersWithIV(skey, IV);
            cipher.Init(true, aesIVKeyParam);
        }
        catch (Exception e)
        {
            Console.WriteLine("Encrypt exception (init): " + e.Message);
            return null;
        }

        try
        {
            MemoryStream bOut = new MemoryStream();
            CipherStream cOut = new CipherStream(bOut, null, cipher);

            cOut.Write(data, 0, data.Length);
            cOut.Close();
            encrypted = bOut.ToArray();
        }
        catch (Exception e)
        {
            Console.WriteLine("Encrypt exception: " + e.Message);
            encrypted = null;
        }

        return encrypted;

    }

    private static byte[] Decrypt(byte[] data, byte[] iv, byte[] aesKey)
    {
        byte[] output = null;

        IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CTR/NoPadding");

        try
        {
            KeyParameter skey = new KeyParameter(aesKey);
            ParametersWithIV aesIVKeyParam = new ParametersWithIV(skey, iv);
            cipher.Init(false, aesIVKeyParam);
        }
        catch (Exception e)
        {
            Console.WriteLine("Decrypt exception (init): " + e.Message);
            return output;
        }

        try
        {
            MemoryStream bIn = new MemoryStream(data, false);
            CipherStream cIn = new CipherStream(bIn, cipher, null);
            BinaryReader dIn = new BinaryReader(cIn);

            output = new byte[data.Length];
            Array.Clear(output, 0, output.Length);
            output = dIn.ReadBytes(output.Length);
        }
        catch (Exception e)
        {
            Console.WriteLine("Decrypt exception : " + e.Message);
            return output;
        }

        return output;
    }

    private static byte[] GenerateIV(string kassenId, string belegNummer)
    {
        byte[] inBytes = System.Text.Encoding.UTF8.GetBytes(kassenId + belegNummer);
        SHA256 hasher = SHA256Managed.Create();
        byte[] hashBytes = hasher.ComputeHash(inBytes);

        byte[] iv = new List<byte>(hashBytes).GetRange(0, 16).ToArray();

        return iv;
    }

    private static long DecodeUmsatz(byte[] umsatzBytes)
    {
        // reverse to get little-endian array
        Array.Reverse(umsatzBytes, 0, umsatzBytes.Length);
        return BitConverter.ToInt64(umsatzBytes, 0);
    }

    private static byte[] EncodeUmsatz(long umsatz)
    {
        // This gives an 8-byte array
        byte[] umsatzBytes = BitConverter.GetBytes(umsatz);
        // Pad with zeroes to get 16 bytes
        int length = 16 * ((umsatzBytes.Length + 15) / 16);
        Array.Resize(ref umsatzBytes, length);
        // reverse to get big-endian array
        Array.Reverse(umsatzBytes, 0, umsatzBytes.Length);
        return umsatzBytes;
    }

}

==================================================================

Hoffentlich sieht irgend jemand den Fehler!?

Viele Grüße

Bernd

ErichFreitag commented 7 years ago

Ich kann aus der Fehlermeldung nicht herauslesen, was eigentlich der Fehler ist - da fehlt noch ein Teil der Fehlermeldung.

BRRIT2017 commented 7 years ago

Einen Fehler habe ich bereits gefunden und abgeändert... Wenn ich von 16 auf 8 Bit den AES Key anpasse bekomme ich nur noch einen Fehler :-(

Hier ein aktuelle DEP Satz und die Fehlermeldung dazu...

Fehlermeldung(en): Der vorliegende Beleg weist Fehler im maschinenlesbaren Code auf. Die Fehlerursachen sind nachfolgend angeführt: Bei der Überprüfung des maschinenlesbaren Codes wurde ein Fehler beim Abgleich des Signaturwertes mit dem öffentlichen Schlüssel festgestellt. Bitte überprüfen Sie, ob die verwendete Signatur- bzw. Siegelerstellungseinheit korrekt in FinanzOnline registriert wurde. maschinenlesbarer Code: _R1-AT1_rgiKa2_8584_2017-04-07T05:20:11_0,00_3,49_0,00_0,00_0,00_GfZiaHJBvio=_44FCC78C_h95RBz+bl7Y=_JdZeLnK3ElGMjPTZN9aSv0jl8Z8vNmBetVNitIFeEXA=

Was ist da falsch?

ErichFreitag commented 7 years ago

Die Signatur ist falsch weil zu kurz - die muss codiert 88 Zeichen lang sein.

BRRIT2017 commented 7 years ago

Hätten Sie ein Codebeispiel wie der Key berechnet wird? C# vielleicht? Wäre super, dann wäre ich endlich damit durch :-)

ErichFreitag commented 7 years ago

Siehe Detailspezifikation https://github.com/a-sit-plus/at-registrierkassen-mustercode/releases/download/1.2-DOK/2016-09-05-Detailfragen-RKSV-V1.2.pdf. Es wird ihnen nicht erspart bleiben sich damit auseinanderzusetzen.

BRRIT2017 commented 7 years ago

Genau dieses Dokument ist von NICHT Programmieren geschrieben worden. Da steht alles au der Sicht eines Java Programmierers. Es gibt aber auch andere als Java auf dieser Welt. Diese und andere Dokumentationen vom BMF wiedersprechen sich insich. Es gibt neuer in denen der Code 16 Bit lang sein darf was aber das Prüftool nicht annimt. Ich brauche doch nur eine Übersetzung bzw. ein funktionierenden Code schnipsel...

ErichFreitag commented 7 years ago

Diesbezüglich kann ich ihnen nicht weiterhelfen.

Wenn sie nicht verstehen was sie tun werden sie auch keine rechtskonforme Umsetzung zustande bringen. Beispielsweise mischen sie bunt Key und Umsatzzähler und Signatur oder 16 Bit und 16 Bytes.

Am Ende sind sie für die korrekte Umsetzung letztverantwortlich. Bei konkreten Problemen und Fragestellungen kann geholfen werden. "Wie mache ich das" steht in diesem Dokument. "Was ist zu tun" steht in der RKSV und im Erlass. Ohne das zu lesen wird es nicht funktionieren.

Flanelli commented 7 years ago

Ich kann die Ausführungen von Herrn Ing. Freitag zu 100% unterstützen. Wenn man die Detailspezifikationen und den Erlass mit Sorgfalt und einem Minimum an logischem Hausverstand durchliest weiß man jedenfalls was zu tun ist. Um das zu lesen braucht man kein Java :-) Man braucht auch für die Umsetzung nicht zwingend Java sondern sollte wohl in seiner eigenen Entwicklungsumgebung damit zurande kommen wenn es doch auch noch was anders als java auf dieser Welt gibt! Es steht natürlich außer Frage dass so mancher Passus in den Dokumentationen alles andere als preisverdächtig ausgefallen ist aber es war doch auch mehr als genug Zeit um bis zur Deadline jedenfalls eine funktionierende RKSV-Lösung zu erarbeiten.

PS.: Sogar für den Einsatz des Prüftool braucht man keinerlei Java-Kenntnisse. Sogar ich als bekennender Anti-Javajaner :-) habe es geschafft das Ding zum Laufen zu bringen nachdem ich halt auch einmal genauer gelesen habe und dann den Kniff mit der Policy auch erkannt habe.

PPS.: Ich kann es mir aber nicht verkneifen noch hinzuzufügen dass ich wohl nicht der einzige bin der recht gerne irgendwann einmal auch eine komplette Prüfung des JWS-DEP machen würde wenn sich ein Nullbeleg mit Ausfall als "Spaßbremse" etabliert hat. So gesehen sind wir halt noch nicht ganz Up-to-Date aber es läuft und läuft und läuft ....

PPS.: Dank der Kompetenz und Hilfsbereitschaft von Herrn Ing. Freitag haben wir uns allerdings auch einiges an Kopfzerbechen erspart. DANKE!!!