サーチ…


備考

現代の暗号は、コンピュータと通信のセキュリティの基盤です。その基礎は、数論、計算複雑性理論、確率論などの数学の概念に基づいています。

暗号は、デジタルデータのセキュリティを扱います。これは、数学的アルゴリズムに基づくメカニズムの設計を指します。暗号を使用する主な目的は、4つの基本的な情報セキュリティサービスを提供することです。機密性、否認防止、認証、データの完全性などを保証します。

整合性検証済み - 対称鍵 - Javaを使用した暗号化と復号化の例

暗号化は、会話の一部ではないことが意図されていない誰かによって簡単に再構築できないものに、そのオリジン形式のデータ(例:文字の内容、金融取引を認証する信用証明部分)を変換するために使用されます。

基本的に暗号化は、任意の2つのエンティティ(個人またはグループ)間の盗聴を防止するために使用されます。

対称暗号化の場合、送信者と受信者の両方(例:Alice、Bob)は同じ暗号化アルゴリズム(一般に標準化された暗号化アルゴリズム)と同じ暗号化鍵(それらの2つにのみ知られています)を使用する必要があります。

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のようなブロック暗号であってもよい。プリミティブとは、暗号方式のビルディングブロックとして使用されるアルゴリズムのことです。 スキームは、例えば、CBCまたはGCMのようなブロック暗号モードの動作である 。 1つ以上の暗号スキームが暗号プロトコルを構成することができる。 TLSなどのプロトコルは、多くの暗号スキームを使用しますが、メッセージのエンコード/デコード技術、メッセージの順序、使用条件なども使用します。低レベル暗号APIは、プリミティブに直接アクセスするだけで、高水準APIは完全なプロトコル実装にアクセスできます。

書かれた単語が発明されたので、メッセージは暗号化され、手で解読されました。機械装置は、少なくとも古代ギリシア社会以来使用されてきた。この種の暗号は古典暗号と呼ばれています。暗号の紹介は、分析が比較的容易な古典的な暗号技術から始まります。しかし、古典的なアルゴリズムは、現代の構築物から要求されるセキュリティを遵守せず、しばしば壊れやすい。古典的なスキームの例は、シーザーとビゲールです。最もよく知られている機械的装置は間違いなくエニグマ符号化装置である。

現代の暗号は、数学や数学/グループ理論を中心に科学に基づいています。はるかに複雑なアルゴリズムとキーサイズが必要です。これらは、コンピューティングデバイスによってのみ効率的に処理できます。このため、現代の暗号は主にバイト指向の入出力を使用します。これは、メッセージが暗号アルゴリズムの実装によって変換される前に、バイナリおよびバックに変換する必要があることを意味します。これは、暗号化される前に、文字エンコーディングを使用して(テキスト形式の)メッセージを変換する必要があることを意味します。

テキストメッセージの文字エンコーディングの形式は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