Szukaj…


Uwagi

Współczesna kryptografia jest podstawą bezpieczeństwa komputerów i komunikacji. Jego podstawa opiera się na pojęciach matematyki, takich jak teoria liczb, teoria złożoności obliczeniowej i teoria prawdopodobieństwa.

Kryptografia zajmuje się zabezpieczaniem danych cyfrowych. Odnosi się do projektowania mechanizmów opartych na algorytmach matematycznych. Podstawowym celem korzystania z kryptografii jest zapewnienie czterech podstawowych usług bezpieczeństwa informacji; poufność, niezaprzeczalność, uwierzytelnianie i integralność danych.

Integrity Validated - Symmetric Key - Przykład szyfrowania i deszyfrowania przy użyciu Java

Szyfrowanie służy do przekształcania danych w ich oryginalnym formacie (np. Zawartość listu, poświadczenie autoryzacji transakcji finansowej) w coś, co nie może być łatwo odtworzone przez nikogo, kto nie jest przeznaczony do rozmowy.

Zasadniczo stosuje się szyfrowanie, aby zapobiec podsłuchowi między dowolnymi dwoma podmiotami (osobami lub grupą).

W przypadku szyfrowania symetrycznego, zarówno nadawca, jak i odbiorca (np. Alice, Bob) muszą stosować ten sam algorytm szyfrowania (zazwyczaj znormalizowany) i ten sam klucz szyfrowania (znany tylko dwóm z nich).

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

powiązane linki

  • 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;
        
    }

}

Wprowadzenie

Kryptografia to nauka wykorzystywania konstrukcji matematycznych (kodów) w celu zapewnienia bezpieczeństwa komunikacji. Dziedzina kryptografii jest podzbiorem dziedziny bezpieczeństwa informacji.

Możliwych jest wiele operacji kryptograficznych; niektóre najbardziej znane przykłady to:

  • Szyfrowanie: przekształcanie wiadomości w postaci zwykłego tekstu w wiadomość w postaci zaszyfrowanej, dzięki czemu wiadomość pozostaje poufna
  • Deszyfrowanie: przekształcanie wiadomości tekstowej zaszyfrowanej z powrotem w wiadomość tekstową
  • Bezpieczne mieszanie: wykonywanie nieodwracalnej (jednokierunkowej) kompresji w celu utworzenia statycznie wielkości, odrębnej obliczeniowo reprezentacji dla określonego komunikatu.

Kryptografia oparta jest na matematyce, a arytmetyka jest często stosowana w algorytmach związanych z kryptografią. Istnieje niewielki podzbiór prymitywów, schematów i protokołów używanych przez programistów. Programiści zwykle nie implementują samych algorytmów, ale używają schematów i protokołów udostępnianych przez kryptograficzne interfejsy API i środowiska wykonawcze.

Prymitywem może być szyfr blokowy, taki jak AES. Prymityw to dowolny algorytm wykorzystywany jako element składowy schematu kryptograficznego. Schemat to na przykład tryb działania szyfru blokowego , taki jak CBC lub GCM. Jeden lub więcej schematów kryptograficznych może stanowić protokół kryptograficzny. Protokół taki jak TLS wykorzystuje wiele schematów kryptograficznych, ale także techniki kodowania / dekodowania wiadomości, porządkowania wiadomości, warunki użytkowania itp. Niskopoziomowe kryptograficzne interfejsy API zapewniają po prostu bezpośredni dostęp do operacji podstawowych, podczas gdy interfejsy API wysokiego poziomu mogą zapewniać dostęp do pełnych implementacji protokołów.

Wiadomości zostały zaszyfrowane i odszyfrowane ręcznie od momentu wynalezienia słowa pisanego. Urządzenia mechaniczne były używane przynajmniej od starożytnego społeczeństwa greckiego. Ten rodzaj kryptografii nazywany jest kryptografią klasyczną . Wiele wstępów do kryptografii rozpoczyna się od klasycznej kryptografii, ponieważ jest stosunkowo łatwa do analizy. Klasyczne algorytmy nie są jednak zgodne z wymogami bezpieczeństwa wymaganymi od nowoczesnych konstrukcji i często są łatwe do złamania. Przykładami klasycznych schematów są Cezar i Vigenère. Najbardziej znanym urządzeniem mechanicznym jest bez wątpienia maszyna do kodowania Enigma.

Współczesna kryptografia oparta jest na nauce - głównie matematyce i teorii liczb / grup. Wymaga znacznie bardziej skomplikowanych algorytmów i rozmiarów kluczy. Można je skutecznie obsłużyć tylko za pomocą urządzeń komputerowych. Z tego powodu współczesna kryptografia wykorzystuje głównie bajtowe dane wejściowe i wyjściowe. Oznacza to, że wiadomości muszą zostać przekonwertowane na dane binarne iz powrotem, zanim będą mogły zostać przetworzone przez dowolną implementację algorytmu kryptograficznego. Oznacza to, że (tekstowe) wiadomości muszą zostać przekształcone za pomocą kodowania znaków przed ich zaszyfrowaniem.

Formy kodowania znaków wiadomości tekstowych to UTF-8. Wiadomości strukturalne mogą być kodowane przy użyciu ASN.1 / DER lub kanonicznych reprezentacji XML - lub dowolnej liczby zastrzeżonych technik. Czasami wyjście binarne również musi zostać przekształcone z powrotem w tekst. W takim przypadku do reprezentowania danych binarnych w tekście można zastosować schemat kodowania , taki jak podstawa 64 lub dane szesnastkowe.

Kryptografia jest bardzo trudna do osiągnięcia. Programiści powinni używać tylko takich konstrukcji, które w pełni rozumieją. Jeśli to możliwe, programista powinien użyć protokołu wyższego poziomu, takiego jak TLS, aby stworzyć bezpieczeństwo transportu. Nie ma praktycznej szansy na stworzenie bezpiecznego algorytmu, schematu lub protokołu bez formalnego wykształcenia lub dużego doświadczenia. Przykłady kopiowania / wklejania z Internetu prawdopodobnie nie prowadzą do bezpiecznych rozwiązań, a nawet mogą prowadzić do utraty danych.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow