수색…


비고

최신 암호화는 컴퓨터 및 통신 보안의 초석입니다. 그 기초는 수 이론, 계산 복잡성 이론, 확률 이론과 같은 수학 개념에 기초합니다.

암호는 디지털 데이터의 보안을 처리합니다. 이것은 수학적 알고리즘을 기반으로 한 메커니즘의 설계를 의미합니다. 암호 사용의 주된 목적은 네 가지 기본 정보 보안 서비스를 제공하는 것입니다. 기밀성, 부인 방지, 인증 및 데이터 무결성 등이 있습니다.

무결성 검증 - 대칭 키 - Java를 사용한 암호화 및 암호 해독 예제

암호화는 데이터를 형식으로 변환하는 데 사용됩니다 (예 : 문자의 내용, 금융 거래를 인증하는 자격 증명 부분). 대화에 참여하지 않으려는 사람이 쉽게 재구성 할 수없는 데이터로 변환합니다.

기본적으로 암호화는 두 엔티티 (개인 또는 그룹) 사이의 도청을 방지하기 위해 사용됩니다.

대칭 암호화의 경우 발신자와 수신자 (예 : Alice, Bob)는 동일한 암호화 알고리즘 (일반적으로 표준화 된 알고리즘)과 동일한 암호화 키 (두 개만 알고 있음)를 사용해야합니다.

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

관련된 링크들

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

}

소개

암호는 통신을 안전하게하기 위해 수학적 구조 (코드)를 사용하는 과학입니다. 암호학 분야는 정보 보안 분야의 일부입니다.

가능한 많은 암호화 작업이 있습니다. 몇 가지 가장 잘 알려진 예는 다음과 같습니다.

  • 암호화 : 메시지가 기밀로 유지 될 수 있도록 일반 텍스트 메시지를 암호 텍스트 메시지로 변환
  • 암호 해독 : 암호문 메시지를 다시 일반 텍스트 메시지로 변환
  • 보안 해싱 : 비가역 (단방향) 압축을 수행하여 특정 메시지에 대해 정적으로 크기가 지정되고 계산 상으로 다른 표현을 생성합니다.

암호는 수학을 기반으로하며 산술은 암호화와 관련된 알고리즘에서 자주 사용됩니다. 개발자가 사용하는 프리미티브, 스키마 및 프로토콜의 작은 하위 집합이 있습니다. 개발자는 일반적으로 알고리즘 자체를 구현하지 않지만 암호화 API 및 런타임에서 제공하는 스키마와 프로토콜을 사용합니다.

프리미티브 는 AES와 같은 블록 암호 일 수 있습니다. 프리미티브 (primitive)는 암호화 체계를위한 빌딩 블록으로 사용되는 알고리즘입니다. 스키마 는 예를 들어 CBC 또는 GCM과 같은 블록 암호 연산 모드입니다 . 하나 이상의 암호화 체계가 암호화 프로토콜을 구성 할 수 있습니다. TLS와 같은 프로토콜 은 많은 암호화 기법뿐만 아니라 메시지 인코딩 / 디코딩 기법, 메시지 순서, 사용 조건 등을 사용합니다. 낮은 수준의 암호화 API는 프리미엄에 대한 직접 액세스를 제공하는 반면 상위 수준의 API는 전체 프로토콜 구현에 대한 액세스를 제공 할 수 있습니다.

서면으로 작성된 이후 메시지는 암호화되어 해독되었습니다. 기계 장치는 적어도 고대 그리스 사회에서 사용되었습니다. 이러한 종류의 암호화는 고전적인 암호화 라고합니다. 암호화에 대한 많은 소개는 분석하기가 상대적으로 쉽기 때문에 고전적인 암호화로 시작됩니다. 그러나 클래식 알고리즘은 최신 구조에서 요구되는 보안을 준수하지 않으며 쉽게 손상되기 쉽습니다. 전형적인 계획의 예는 Caesar와 Vigenère입니다. 가장 잘 알려진 기계 장치는 의심의 여지없이 Enigma 코딩 기계입니다.

현대의 암호학은 주로 수학과 숫자 / 그룹 이론에 기초합니다. 훨씬 복잡한 알고리즘과 키 크기가 필요합니다. 이들은 컴퓨팅 장치에 의해서만 효율적으로 처리 될 수 있습니다. 이러한 이유로 현대 암호는 주로 바이트 지향 입출력을 사용합니다. 즉, 메시지를 암호화 알고리즘 구현으로 변환하려면 먼저 이진 및 백으로 변환해야합니다. 즉, (텍스트) 메시지는 암호화되기 전에 문자 인코딩을 사용하여 변환되어야합니다.

텍스트 메시지의 문자 인코딩 형식은 UTF-8입니다. 구조화 된 메시지는 ASN.1 / DER 또는 정식 XML 표현 또는 임의의 수의 독점 기술을 사용하여 인코딩 할 수 있습니다. 때로는 바이너리 출력을 텍스트로 다시 변환해야합니다. 이 경우 기본 64 또는 16 진수와 같은 인코딩 체계를 사용하여 텍스트 내의 이진 데이터를 나타낼 수 있습니다.

암호화가 옳은 것으로 악명 높게 어렵습니다. 개발자는 완전히 이해할 수있는 구조 만 사용해야합니다. 가능한 경우 개발자는 TLS와 같은 상위 수준 프로토콜을 사용하여 전송 보안을 만들어야합니다. 정식 교육이나 광범위한 경험없이 안전한 알고리즘, 스키마 또는 프로토콜을 만들 수있는 실질적인 기회는 없습니다. 인터넷에서 예제를 복사 / 붙여 넣기해도 보안 솔루션으로 이어지지 않으며 데이터 손실이 발생할 수 있습니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow