Skip to main content

Symmetrische Verschlüsselungsalgorithmen in Java absichern

Artikel von
blog feature pypi spoof

18. Oktober 2023

0 Min. Lesezeit

In unserer vernetzten Welt hat der Schutz digitaler Daten höchste Priorität. Da Java-Anwendungen in vielen Bereichen weit verbreitet sind – vom Bankwesen bis zum Gesundheitswesen –, müssen wir die Bedeutung der Verschlüsselung hervorheben. Bei der Verschlüsselung werden lesbare Daten, also Klartext, in unlesbare Daten, den Chiffretext, umgewandelt. So bleiben verschlüsselte Daten auch dann für Unbefugte unzugänglich, wenn sie abgefangen werden.

Als Entwickler müssen Sie zunächst entscheiden, ob Sie Verschlüsselung benötigen. Auch wenn das zunächst ungewöhnlich klingt: Das entscheidende Prinzip der Verschlüsselung ist, dass sich der Chiffretext wieder in den ursprünglichen Text zurückverwandeln lässt. Bei Passwörtern möchten wir beispielsweise keine Verschlüsselung verwenden, da wir den ursprünglichen Text nicht „entschlüsseln“ können möchten. Daher setzen wir auf Hashing für Passwörter statt auf Verschlüsselung.

Verschlüsselung in Java

Java-Anwendungen verarbeiten häufig sensible Daten, darunter Kundeninformationen, Finanzdaten und Transaktionsdatensätze. Als Java-Entwickler müssen Sie robuste Verschlüsselungsalgorithmen einsetzen, um die Integrität und Vertraulichkeit sensibler Daten zu gewährleisten. So schützen Sie Ihre Nutzer und wichtige Daten in Ihrem System.

Symmetrische und asymmetrische Verschlüsselung

Grundsätzlich lassen sich zwei Arten der Verschlüsselung unterscheiden: symmetrische und asymmetrische. Beide funktionieren auf grundlegend unterschiedliche Weise und eignen sich für unterschiedliche Anwendungsfälle.

Bei der symmetrischen Verschlüsselung wird für Ver- und Entschlüsselung derselbe Schlüssel verwendet. Sie ist dadurch schneller, aber weniger sicher, wenn der Schlüssel weitergegeben werden muss. Bei der asymmetrischen Verschlüsselung kommt dagegen ein Schlüsselpaar aus öffentlichem und privatem Schlüssel zum Einsatz. Der öffentliche Schlüssel dient der Verschlüsselung, der private der Entschlüsselung. Symmetrische Verschlüsselung eignet sich für Szenarien, in denen große Datenmengen verschlüsselt werden müssen. Asymmetrische Verschlüsselung ist ideal, um Schlüssel sicher über ein Netzwerk auszutauschen. Obwohl wir zwei Arten der Verschlüsselung unterscheiden, konzentrieren wir uns in diesem Artikel ausschließlich auf symmetrische Verschlüsselung in Java-Anwendungen.

Bei symmetrischer Verschlüsselung empfiehlt es sich, Dienste zu nutzen, die Hardware-Sicherheitsmodule (HSMs) für die Schlüsselverwaltung und Envelope-Encryption-Muster verwenden. So lässt sich die Schlüsselverwaltung vereinfachen, die den schwierigsten Teil einer Verschlüsselungslösung darstellt. Wenn Sie jedoch keinen Dienst wie Amazon Key Management Service (KMS) nutzen können und die Schlüssel dennoch verwalten möchten, erfahren Sie im Folgenden, wie Sie dabei sicher vorgehen. Im restlichen Artikel zeigen wir, wie Sie symmetrische Verschlüsselung in Ihrer Java-Anwendung mit den Paketen javax.crypto implementieren.

Veraltete Verschlüsselungsalgorithmen und ihre Risiken verstehen

Trotz der Bedeutung von Verschlüsselung sind nicht alle Verschlüsselungsalgorithmen gleich sicher. Bei einigen älteren oder „veralteten“ Verschlüsselungsalgorithmen wie DES (Data Encryption Standard) oder 3DES wurden Schwachstellen entdeckt, die Angreifer ausnutzen können. Diese Algorithmen galten einst als sicher. Fortschritte bei der Rechenleistung und den Methoden der Kryptoanalyse haben sie jedoch unsicher gemacht.

Die Verwendung unsicherer Verschlüsselungsalgorithmen in Ihren Java-Anwendungen kann Risiken bergen. Ein Angreifer, der Zugriff auf Ihre verschlüsselten Daten erlangt, kann sie möglicherweise entschlüsseln, wenn der Verschlüsselungsalgorithmus veraltet ist und bekannte Schwachstellen aufweist. Das kann zu einer Datenschutzverletzung führen, die für Ihre Nutzer und Ihr Unternehmen potenziell verheerende Folgen hat.

Empfehlungen zu Algorithmen und Verschlüsselungsmodi

Für symmetrische Verschlüsselungsalgorithmen empfiehlt die OWASP Foundation derzeit AES (Advanced Encryption Standard) mit einer Schlüssellänge von mindestens 128 Bit, vorzugsweise jedoch 256 Bit. Außerdem sollten Sie den Algorithmus mit einem sicheren Modus verwenden. Auch die kryptografischen Standards und Richtlinien des NIST sehen die AES-Verschlüsselung als Blockchiffre vor.

Für eine Chiffre wie AES stehen mehrere Modi zur Verfügung. Sie unterscheiden sich in ihren Sicherheits- und Leistungseigenschaften. Bei den Modi sollten Sie ECB (Electronic Codebook) vermeiden, da dieser Modus keine zuverlässige Vertraulichkeit der Nachrichten gewährleistet. Verwenden Sie stattdessen Modi wie CCM (Counter with CBC-MAC) oder GCM (Galois/Counter Mode). Beide sind authentifizierte Modi und gewährleisten Integrität, Vertraulichkeit und Authentizität der Daten.

Schwache Verschlüsselungsalgorithmen in Java erkennen

Mit den javax.crypto-APIs lässt sich relativ einfach ein kleiner Verschlüsselungsdienst in Java implementieren. Das folgende Beispiel zeigt einen EncryptionService in Java, der auf dem veralteten DES-Algorithmus mit dem ECB-Modus basiert.

public class EncryptionServiceDes {

    private SecretKey secretKey;
    private Cipher cipher;

    public EncryptionServiceDes(String key) throws GeneralSecurityException {
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        DESKeySpec desKeySpec = new DESKeySpec(keyBytes);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        secretKey = keyFactory.generateSecret(desKeySpec);
        cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
    }

    public String encrypt(String original) throws GeneralSecurityException {
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        // Encrypt the original data
        byte[] encryptedData = cipher.doFinal(original.getBytes(StandardCharsets.UTF_8));
        // Encode the encrypted data in base64 for better handling
        return Base64.getEncoder().encodeToString(encryptedData);
    }
    public String decrypt(String cypher) throws GeneralSecurityException{
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        // Decode the base64-encoded ciphertext
        byte[] encryptedData = Base64.getDecoder().decode(cypher);
        // Decrypt the data
        byte[] decryptedData = cipher.doFinal(encryptedData);
        return new String(decryptedData, StandardCharsets.UTF_8);
    }
}

Nachdem wir einen bekanntermaßen unsicheren Verschlüsselungsalgorithmus in ein Projekt eingebaut haben, verwenden wir den kostenlosen Snyk-Dienst, um diese unsichere Verwendung zu erkennen und uns eine bessere Lösung zu empfehlen.

Erstellen Sie zunächst ein kostenloses Snyk-Konto. Sie können ganz einfach mit Ihrem GitHub-, Bitbucket-, Azure AD- oder Docker-Konto loslegen.

Anschließend habe ich mein GitHub-Repository mit Snyk verbunden. Die statische Analyse von Snyk Code weist mich sofort darauf hin, dass meine Anwendung einen fehlerhaften oder riskanten kryptografischen Algorithmus verwendet. Das ist absolut zutreffend, denn der obige Dienst enthält mehrere Verweise auf DES. Die Snyk Code-Engine empfiehlt außerdem, AES in Betracht zu ziehen – ganz im Einklang mit den Empfehlungen von OWASP.

Der Sicherheitscode-Scan stuft DES/ECB/PKCS5Padding als fehlerhaften kryptografischen Algorithmus ein – mit CWE-327 und einem Score von 828.

Obwohl ich im kurzen Codeausschnitt unten AES verwende, kommt der CBC-Modus zum Einsatz, der nicht als sicher gilt.

…
    public EncryptionServiceAES(String key) throws GeneralSecurityException {
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        secretKey = new SecretKeySpec(keyBytes, "AES");
        cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    }
…

Snyk erkennt unter anderem auch die Verwendung unsicherer Modi. Der Screenshot unten stammt aus dem Snyk-Plugin, das meinen Code in IntelliJ IDEA scannt und nützliche Informationen wie Beispiele für externe Korrekturen bereitstellt.

Snyk-Schwachstellenbericht mit einer Warnung vor einem fehlerhaften kryptografischen Algorithmus für AES/CBC und einer Codekorrektur mit AES/GCM.

Die Umstellung des EncryptionService auf eine Version mit AES/GCM/NoPadding wie im folgenden Beispiel ist eine deutlich bessere Lösung als meine vorherige und entspricht den aktuellen Empfehlungen von OWASP.

public class EncryptionServiceAESGCM {
    private SecretKey secretKey;
    private Cipher cipher;

    public EncryptionServiceAESGCM(String key) throws GeneralSecurityException {
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        secretKey = new SecretKeySpec(keyBytes, "AES");
        cipher = Cipher.getInstance("AES/GCM/NoPadding");
    }

    public String encrypt(String original) throws GeneralSecurityException{
        byte[] iv = new byte[16]; // Initialization Vector
        SecureRandom random = new SecureRandom();
        random.nextBytes(iv); // Generate a random IV
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(128, iv));
        byte[] encryptedData = cipher.doFinal(original.getBytes());
        var encoder = Base64.getEncoder();
        var encrypt64 = encoder.encode(encryptedData);
        var iv64 = encoder.encode(iv);
        return new String(encrypt64) + "#" + new String(iv64);
    }

    public String decrypt(String cypher) throws GeneralSecurityException{
        var split = cypher.split("#");
        var decoder = Base64.getDecoder();
        var cypherText = decoder.decode(split[0]);
        var iv = decoder.decode(split[1]);
        var paraSpec = new GCMParameterSpec(128, iv);
        cipher.init(Cipher.DECRYPT_MODE, secretKey, paraSpec);
        byte[] decryptedData = cipher.doFinal(cypherText);
        return new String(decryptedData);
    }
}

Verschlüsselungsalgorithmen in Ihren Java-Anwendungen kontinuierlich überprüfen

Veraltete Verschlüsselungsalgorithmen zu aktualisieren ist entscheidend. Genauso wichtig ist es, Ihre Codebasis kontinuierlich auf neue Sicherheitsprobleme zu überprüfen. Verschlüsselungsalgorithmen, die zum Zeitpunkt der Erstellung als sicher gelten, werden in wenigen Jahren mit Sicherheit veraltet sein.

Sie sollten daher proaktiv und kontinuierlich nach solchen Problemen suchen. Das gilt nicht nur für Verschlüsselungsalgorithmen, sondern selbstverständlich auch für andere Probleme im Code, den Sie pflegen. Mit einem Tool für statische Anwendungssicherheitstests (SAST) wie Snyk Code können Sie solche Probleme einfach erkennen und beheben, bevor Schaden entsteht.

Snyk Code für Java kostenlos nutzen

Snyk Code ist ein hochmodernes Tool für statische Anwendungssicherheitstests (SAST), das Sicherheitslücken in Ihrer Codebasis erkennt. Es unterstützt mehrere Programmiersprachen, darunter Java, analysiert Code und gibt Entwicklern Feedback in Echtzeit. Snyk Code lässt sich in Ihre CI/CD-Pipeline integrieren, um Ihren Code bei jedem Commit automatisch zu scannen. So können Sie Sicherheitsprobleme früh im Entwicklungszyklus erkennen und beheben. Außerdem können Sie Snyk Code mit Ihrem Git-Repository verbinden, über unsere CLI auf der Kommandozeile verwenden und in alle gängigen Java-IDEs integrieren.

So können Sie Probleme in Ihrem Code frühzeitig und in jedem Schritt des Entwicklungszyklus erkennen. Vor allem können Sie Snyk Code kostenlos nutzen, indem Sie sich unten bei Snyk registrieren.

Starten Sie mit Capture the Flag

Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.

Die in diesem Blogbeitrag verwendeten Codebeispiele sind auf GitHub veröffentlicht, damit Sie selbst damit experimentieren können. Wenn Sie weitere gute Ressourcen zur Sicherheit für Java-Entwickler suchen, sehen Sie sich Folgendes an:

Gepostet in: