cryptographyErste Schritte mit der Kryptographie


Bemerkungen

Die moderne Kryptographie ist der Eckpfeiler der Computer- und Kommunikationssicherheit. Ihre Grundlage basiert auf mathematischen Konzepten wie Zahlentheorie, Rechenkomplexitätstheorie und Wahrscheinlichkeitstheorie.

Kryptographie befasst sich mit der Sicherung digitaler Daten. Es bezieht sich auf das Design von Mechanismen, die auf mathematischen Algorithmen basieren. Das Hauptziel der Verwendung von Kryptographie besteht darin, die vier grundlegenden Informationssicherheitsdienste bereitzustellen. Vertraulichkeit, Rückweisung, Authentifizierung und Datenintegrität.

Integrity Validated - Symmetric Key - Beispiel für Verschlüsselung und Entschlüsselung mit Java

Die Verschlüsselung wird verwendet, um Daten in ihrem ursprünglichen Format (z. B. Inhalt eines Briefs, Berechtigungsnachweis für die Genehmigung einer Finanztransaktion) in etwas umzuwandeln, das von niemandem, der nicht Teil der Konversation sein soll, einfach rekonstruiert werden kann.

Grundsätzlich wird die Verschlüsselung verwendet, um das Abhören zwischen zwei Entitäten (Einzelpersonen oder einer Gruppe) zu verhindern.

Bei symmetrischer Verschlüsselung müssen sowohl Sender als auch Empfänger (z. B. Alice, Bob) denselben Verschlüsselungsalgorithmus (im Allgemeinen ein standardisierter) und denselben Verschlüsselungsschlüssel (nur den beiden bekannt) verwenden.

http://docs.oracle.com/javase/1.5.0/docs/guide/security/jce/JCERefGuide.html#Examples

ähnliche Links

  • https://en.wikipedia.org/wiki/History_of_cryptography
  • https://en.wikipedia.org/wiki/Cryptography
package com.example.so.documentation.cryptography;

import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.StringTokenizer;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;


/**
 * 
 * <p> Encryption is used to transform data in its orignal format (Eg: The contents of a letter, Credentials part of authorizing a financial transaction) to something that 
 * cannot be easily reconstructed by anyone who is not intended to be part of the conversation.  </p>
 * <p> Basically encryption is used to prevent eavesdropping between any two entities 
 * (individuals or a group). </p> 
 * 
 * <p> In case of symmetric encryption, both the sender and receiver (Eg: Alice, Bob) must use the same encryption algorithm (generally a standardised one) 
 * and the same encryption key (known only to the two of them). </p>
 * 
 * <p> http://docs.oracle.com/javase/1.5.0/docs/guide/security/jce/JCERefGuide.html#Examples </p>
 * 
 * <p> Related Links </p>
 * <ul>
 *     <li>https://en.wikipedia.org/wiki/History_of_cryptography</li>
 *     <li>https://en.wikipedia.org/wiki/Cryptography</li>
 * </ul>
 * 
 * <pre>
 *         ChangeLog : 2016-09-24
 *         1. The modified encrypted text is now reflected correctly in the log and also updated same in javadoc comment.
 * </pre>
 * @author Ravindra HV (with inputs w.r.t integrity check from ArtjomB[http://stackoverflow.com/users/1816580/artjom-b])
 * @since (30 July 2016)
 * @version 0.3
 *
 */
public class IntegrityValidatedSymmetricCipherExample {
    
    /**
     * <p>https://en.wikipedia.org/wiki/Advanced_Encryption_Standard</p>
     */
    private static final String SYMMETRIC_ENCRYPTION_ALGORITHM_NAME = "AES"; // The current standard encryption algorithm (as of writing)
    
    /**
     * <p>Higher the number, the better</p>
     * <p>Encryption is performed on chunks of data defined by the key size</p>
     * <p>Higher key sizes may require modification to the JDK (Unlimited Strength Cryptography)</p>
     *         
     */
    private static final int SYMMETRIC_ENCRYPTION_KEY_SIZE = 128; // lengths can be 128, 192 and 256
    
    /**
     * <p> 
     *         A transformation defines in what manner the encryption should be performed.
     * </p>
     * <p>
            Eg: Whether there is any link between two chunks of encrypted data (CBC) or what should happen 
     *         if there is a mismatch between the key-size and the data length.       * 
     * </p>
     * 
     * <p> https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation </p>
     */
    private static final String SYMMETRIC_ENCRYPTION_TRANSFORMATION = "AES/CBC/PKCS5Padding";
    private static final Charset CHARSET_INSTANCE_UTF8 = Charset.forName("UTF-8");
    
    
    private static final int AES_IV_KEY_SIZE = 128; // for AES, iv key size is fixed at 128 independent of key-size
    
    private static final String MAC_ALGORITHM_NAME__HMAC_SHA256 = "HmacSHA256";
    private static final String HASH_FIELDS_SEPARATOR = "|" ;
    
    
    

    /**
     * @param args
     * <p>Sample output.</p>
     * <pre>
Encrypted, Base64 encoded text :W1DePjeYMlI6xmyq9jr+cw==|55F80F4C2987CC143C69563025FACE22|GLR3T8GdcocpsTM1qSXp5jLsNx6QRK880BtgnV1jFg0=
Decrypted text :helloworld
Encrypted, Base64 encoded text - v2:1XX/A9BO1Cp8mK+SHh9iHA==|B8294AC9967BB57D714ACCB3EE5710BD|TnjdaWbvp+H6yCbAAQFMkWNixeW8VwmW48YlKA/AAyw=
Decrypted text  - v2:helloworld
Encrypted, Base64 encoded text - v3 (original):EU4+rAZ2vOKtoSDiDPcO+A==|AEEB8DD341D8D9CD2EDFA05A4595EBD2|7anESSSJf1dHobS5tDdQ1mCNkFcIgCvtNC/p79xJi5U=
Encrypted, Base64 encoded text - v3 (modified):FU4+rAZ2vOKtoSDiDPcO+A==|AEEB8DD341D8D9CD2EDFA05A4595EBD2|7anESSSJf1dHobS5tDdQ1mCNkFcIgCvtNC/p79xJi5U=
Error : Integrity check failed
Exception in thread "main" java.lang.RuntimeException: Error : Integrity check failed
    at com.example.so.documentation.cryptography.IntegrityValidatedSymmetricCipherExampleThree.decrypt(IntegrityValidatedSymmetricCipherExampleThree.java:165)
    at com.example.so.documentation.cryptography.IntegrityValidatedSymmetricCipherExampleThree.main(IntegrityValidatedSymmetricCipherExampleThree.java:126)
     * </pre>
     */
    public static void main(String[] args) {
        
        /*
         * EncryptionKey : Shared secret between receiver and sender (who generates the password and how its shared depends on the purpose)
         * This program generates a new one every time its run ! 
         * Normally it would be generated once and then be stored somewhere (Eg: In a JCEKS keystore file).
         */
        byte[] generatedSharedSecret = secretKeyGeneratorUtility();
        byte[] generatedSharedHMACKey = secretKeyGeneratorUtility();
        String plainText = "helloworld";
        
        String encryptedText = encrypt(plainText, generatedSharedSecret, generatedSharedHMACKey);
        System.out.println("Encrypted, Base64 encoded text :"+encryptedText);
        String decryptedText = decrypt(encryptedText, generatedSharedSecret, generatedSharedHMACKey);
        System.out.println("Decrypted text :"+decryptedText);
        
        String encryptedTextTwo = encrypt(plainText, generatedSharedSecret, generatedSharedHMACKey);
        System.out.println("Encrypted, Base64 encoded text - v2:"+encryptedTextTwo);
        String decryptedTextTwo = decrypt(encryptedTextTwo, generatedSharedSecret, generatedSharedHMACKey);
        System.out.println("Decrypted text  - v2:"+decryptedTextTwo);
        
        String encryptedTextThree = encrypt(plainText, generatedSharedSecret, generatedSharedHMACKey);
        System.out.println("Encrypted, Base64 encoded text - v3 (original):"+encryptedTextThree);
        char[] encryptedTextThreeChars = encryptedTextThree.toCharArray();
        encryptedTextThreeChars[0] = (char) ((encryptedTextThreeChars[0])+1);
        String encryptedTextThreeModified = new String(encryptedTextThreeChars);
        System.out.println("Encrypted, Base64 encoded text - v3 (modified):"+encryptedTextThreeModified);
        
        String decryptedTextThree = decrypt(encryptedTextThreeModified, generatedSharedSecret, generatedSharedHMACKey);
        System.out.println("Decrypted text  - v3:"+decryptedTextThree);

    }
    
    
    public static String encrypt(String plainText, byte[] key, byte[] hmacKey) {
        
        byte[] plainDataBytes = plainText.getBytes(CHARSET_INSTANCE_UTF8);
        byte[] iv = initializationVectorGeneratorUtility();
        byte[] encryptedDataBytes = encrypt(plainDataBytes, key, iv);
        
        String initializationVectorHex = DatatypeConverter.printHexBinary(iv);
        String encryptedBase64EncodedString = DatatypeConverter.printBase64Binary(encryptedDataBytes); // Generally the encrypted data is encoded in Base64 or hexadecimal encoding for ease of handling.
        String hashInputString = encryptedBase64EncodedString + HASH_FIELDS_SEPARATOR + initializationVectorHex + HASH_FIELDS_SEPARATOR;
        String hashedOutputString =  DatatypeConverter.printBase64Binary(messageHashWithKey(hmacKey, hashInputString.getBytes(CHARSET_INSTANCE_UTF8)));
        String encryptionResult = hashInputString + hashedOutputString;  
        return encryptionResult;
    }
    
    public static byte[] encrypt(byte[] plainDataBytes, byte[] key, byte[] iv) {
        byte[] encryptedDataBytes = encryptOrDecrypt(plainDataBytes, key, iv, true);
        return encryptedDataBytes;
    }

    
    public static String decrypt(String cipherInput, byte[] key, byte[] hmacKey) {
        StringTokenizer stringTokenizer = new StringTokenizer(cipherInput, HASH_FIELDS_SEPARATOR);
        
        String encryptedString = stringTokenizer.nextToken();
        String initializationVectorHex = stringTokenizer.nextToken();
        String hashedString = stringTokenizer.nextToken();

        String hashInputString = encryptedString + HASH_FIELDS_SEPARATOR + initializationVectorHex + HASH_FIELDS_SEPARATOR;
        String hashedOutputString =  DatatypeConverter.printBase64Binary(messageHashWithKey(hmacKey, hashInputString.getBytes(CHARSET_INSTANCE_UTF8)));

        if( hashedString.equals(hashedOutputString) == false ) {
            String message = "Error : Integrity check failed";
            System.out.println(message);
            throw new RuntimeException(message);
        }
        
        byte[] encryptedDataBytes = DatatypeConverter.parseBase64Binary(encryptedString); // The Base64 encoding must be reversed so as to reconstruct the raw bytes.
        byte[] iv = DatatypeConverter.parseHexBinary(initializationVectorHex);
        byte[] plainDataBytes = decrypt(encryptedDataBytes, key, iv);
        String plainText = new String(plainDataBytes, CHARSET_INSTANCE_UTF8);
        return plainText;
    }

    public static byte[] decrypt(byte[] encryptedDataBytes, byte[] key, byte[] iv) {
        byte[] decryptedDataBytes = encryptOrDecrypt(encryptedDataBytes, key, iv, false);
        return decryptedDataBytes;
    }
    

    public static byte[] encryptOrDecrypt(byte[] inputDataBytes, byte[] key, byte[] iv, boolean encrypt) {
        byte[] resultDataBytes = null;
        
        // Exceptions, if any, are just logged to console for this example.
        try {
            Cipher cipher = Cipher.getInstance(SYMMETRIC_ENCRYPTION_TRANSFORMATION);
            SecretKey secretKey = new SecretKeySpec(key, SYMMETRIC_ENCRYPTION_ALGORITHM_NAME);
            AlgorithmParameterSpec algorithmParameterSpec = new IvParameterSpec(iv);
            if(encrypt) {
                cipher.init(Cipher.ENCRYPT_MODE, secretKey, algorithmParameterSpec);    
            }
            else {
                cipher.init(Cipher.DECRYPT_MODE, secretKey, algorithmParameterSpec);
            }
            
            resultDataBytes = cipher.doFinal(inputDataBytes); // In relative terms, invoking do-final in one go is fine as long as the input size is small.
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        
        return resultDataBytes;
    }

    
    private static byte[] secretKeyGeneratorUtility() {
        byte[] keyBytes = null;
        
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(SYMMETRIC_ENCRYPTION_ALGORITHM_NAME);
            keyGenerator.init(SYMMETRIC_ENCRYPTION_KEY_SIZE);
            SecretKey secretKey = keyGenerator.generateKey();
            keyBytes = secretKey.getEncoded();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        
        return keyBytes;
        
    }
    
    
    /**
     * <p> InitialVector : Helps in avoiding generating the same encrypted result, even when the same encryption - algorithm and key are used. </p>
     * <p> Since this is also required to be known to both sender and receiver, its either based on some convention or is part of the cipher-text transmitted.</p>  
     * <p> https://en.wikipedia.org/wiki/Initialization_vector </p>
     * @return
     */
    private static byte[] initializationVectorGeneratorUtility() {
        byte[] initialVectorResult = null;
        
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(SYMMETRIC_ENCRYPTION_ALGORITHM_NAME);
            keyGenerator.init(AES_IV_KEY_SIZE);
            SecretKey secretKey = keyGenerator.generateKey();
            initialVectorResult = secretKey.getEncoded();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        
        return initialVectorResult;
    }
    
    
    private static byte[] messageHashWithKey(byte[] key, byte[] data) { // byte[] iv, 
        byte[] hmac = null;
        
        try {
            Mac mac = Mac.getInstance(MAC_ALGORITHM_NAME__HMAC_SHA256);
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, MAC_ALGORITHM_NAME__HMAC_SHA256);
            //AlgorithmParameterSpec algorithmParameterSpec = new IvParameterSpec(iv);
            mac.init(secretKeySpec); // algorithmParameterSpec
            hmac = mac.doFinal(data);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } /*catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }*/
        
        return hmac;
        
    }

}
 

Einführung

Kryptographie ist die Wissenschaft, mathematische Konstrukte (Codes) zu verwenden, um die Kommunikation sicherer zu machen. Das Gebiet der Kryptographie ist eine Teilmenge des Gebiets der Informationssicherheit.

Es gibt viele kryptographische Operationen. Einige der bekanntesten Beispiele sind:

  • Verschlüsselung: Umwandlung einer Klartextnachricht in eine Geheimtextnachricht, sodass die Nachricht vertraulich bleibt
  • Entschlüsselung: Umwandlung einer Geheimtextnachricht in eine Klartextnachricht
  • Sicheres Hashing: irreversible (unidirektionale) Komprimierung, um eine rechnerisch unterschiedliche Repräsentation für eine bestimmte Nachricht zu erstellen.

Kryptographie basiert auf Mathematik, und Arithmetik wird häufig in Algorithmen verwendet, die sich auf Kryptographie beziehen. Es gibt eine kleine Untergruppe von Grundelementen, Schemata und Protokollen, die von Entwicklern verwendet werden. Entwickler implementieren die Algorithmen normalerweise nicht selbst, sondern verwenden die Schemata und Protokolle, die von kryptografischen APIs und Laufzeiten bereitgestellt werden.

Ein Primitiv könnte eine Blockchiffre wie AES sein. Ein Primitiv ist jeder Algorithmus, der als Baustein für ein kryptografisches Schema verwendet wird. Ein Schema ist beispielsweise ein Betriebsmodus , wie beispielsweise CBC oder GCM. Ein oder mehrere kryptographische Schemata können ein kryptografisches Protokoll bilden. Ein Protokoll wie TLS verwendet viele kryptografische Schemata, aber auch Verfahren zur Nachrichtenkodierung / -decodierung, Reihenfolge der Nachrichten, Verwendungsbedingungen usw. Low-Level-Verschlüsselungs-APIs bieten nur direkten Zugriff auf Grundelemente, während High-Level-APIs Zugriff auf vollständige Protokollimplementierungen bieten.

Nachrichten wurden von Hand verschlüsselt und entschlüsselt, seit das geschriebene Wort erfunden wurde. Mechanische Geräte wurden zumindest seit der antiken griechischen Gesellschaft verwendet. Diese Art der Kryptographie wird als klassische Kryptographie bezeichnet . Viele Einführungen in die Kryptographie beginnen mit der klassischen Kryptographie, da sie relativ einfach zu analysieren ist. Klassische Algorithmen halten sich jedoch nicht an die Sicherheitsanforderungen moderner Konstrukte und sind oft leicht zu brechen. Beispiele für klassische Konzepte sind der Caesar und der Vigenère. Das bekannteste mechanische Gerät ist zweifellos die Enigma-Codiermaschine.

Die moderne Kryptographie basiert auf Wissenschaft - hauptsächlich Mathematik und Zahlen- / Gruppentheorie. Es beinhaltet viel kompliziertere Algorithmen und Schlüsselgrößen. Diese können nur von Computergeräten effizient gehandhabt werden. Aus diesem Grund verwendet die moderne Kryptographie hauptsächlich byteorientierte Eingabe und Ausgabe. Das bedeutet, dass Nachrichten in binär und zurück konvertiert werden müssen, bevor sie durch eine Implementierung eines kryptographischen Algorithmus transformiert werden können. Dies bedeutet, dass (Text-) Nachrichten vor der Verschlüsselung mithilfe der Zeichencodierung umgewandelt werden müssen.

Formen der Zeichenkodierung von Textnachrichten sind UTF-8. Strukturierte Nachrichten können mit ASN.1 / DER oder kanonischen XML-Darstellungen oder einer beliebigen Anzahl proprietärer Techniken codiert werden. Manchmal muss die binäre Ausgabe auch wieder in Text umgewandelt werden. In diesem Fall kann ein Codierschema wie Basis 64 oder Hexadezimalzahlen verwendet werden, um die binären Daten im Text darzustellen.

Kryptographie ist bekanntlich schwer richtig zu machen. Entwickler sollten nur Konstrukte verwenden, die sie vollständig verstehen. Wenn möglich, sollte ein Entwickler ein höheres Protokoll wie TLS verwenden, um Transportsicherheit zu schaffen. Es gibt keine praktische Chance, einen sicheren Algorithmus, ein Schema oder ein Protokoll ohne formale Schulung oder umfangreiche Erfahrung zu erstellen. Das Kopieren / Einfügen von Beispielen aus dem Internet führt wahrscheinlich nicht zu sicheren Lösungen und kann sogar zu Datenverlust führen.