Java - ハイブリッド暗号の例

Java –ハイブリッド暗号化の例

encrypted

ハイブリッド暗号は、安全であるがビッグデータ上の低速暗号(非対称暗号)と安全ではないが高速暗号(対称暗号)の間のシルバーライニングです。 ハイブリッド暗号化は、公開鍵と秘密鍵のペアが提供するセキュリティに加えて、ワンキー暗号化と復号化の速度を組み合わせており、非常に安全なタイプの暗号化と見なされます。

ハイブリッド暗号化の最も一般的な方法論は、対称キーを使用してデータを暗号化し、送信者の秘密キーまたは受信者の公開キーで暗号化することです。 受信者は、復号化するには、対応する非対称キーを使用して対称キーを復号化し、次にその対称キーを使用して受信したデータを復号化する必要があります。 この例は、次のことを明確にするのに役立ちます。

すでに公開鍵を交換している2人のユーザー、アリスとボブのシナリオを想像してください。 アリスはボブに「confidential.txt」というテキストファイルを送信したいと考えています。 彼女がメッセージを暗号化するために従う手順は次のとおりです。

  1. 強力なアルゴリズムを使用して新しい対称キーを生成します

  2. 手順1のキーを使用して「confidential.txt」を暗号化します

  3. ボブの公開鍵を使用して、ステップ1の鍵を暗号化します

  4. 暗号化されたテキストとキーの両方をボブに送信します

ボブは2つのファイルを受け取ります。 それらを解読するために彼が従うステップは次のとおりです。

  1. 自分の秘密鍵を使用して受け取った鍵を解読します

  2. 復号化後にステップ1で取得したキーを使用して、受信したテキストを復号化します

Note
対称暗号化と非対称暗号化について詳しく知りたい場合は、Symmetric-Key Cryptography exampleAsymmetric-Key Cryptography exampleを参照してください。

以下のコードのプロセスは、プロセスを理解して学習者を楽にするために、個別のファイルに分割されています。

1. プロジェクトディレクトリ

java-hybrid-cryptography-8f

2. アリスとボブのキーを生成する

プラットフォームに応じて、公開鍵と秘密鍵のペアを生成する方法はいくつかあります。 この例では、javaを使用して、アリス用とボブ用の2つのペアを作成します。 この例で使用する暗号化アルゴリズムはRSAです。

GenerateKeys.java

package com.example.keypair;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;

public class GenerateKeys {

    private KeyPairGenerator keyGen;
    private KeyPair pair;
    private PrivateKey privateKey;
    private PublicKey publicKey;

    public GenerateKeys(int keylength)
        throws NoSuchAlgorithmException, NoSuchProviderException {

        this.keyGen = KeyPairGenerator.getInstance("RSA");
        this.keyGen.initialize(keylength);

    }

    public void createKeys() {

        this.pair = this.keyGen.generateKeyPair();
        this.privateKey = pair.getPrivate();
        this.publicKey = pair.getPublic();

    }

    public PrivateKey getPrivateKey() {
        return this.privateKey;
    }

    public PublicKey getPublicKey() {
        return this.publicKey;
    }

    public void writeToFile(String path, byte[] key) throws IOException {

        File f = new File(path);
        f.getParentFile().mkdirs();

        FileOutputStream fos = new FileOutputStream(f);
        fos.write(key);
        fos.flush();
        fos.close();

    }

    public static void main(String[] args)
                throws NoSuchAlgorithmException, NoSuchProviderException, IOException {

        GenerateKeys gk_Alice;
        GenerateKeys gk_Bob;

        gk_Alice = new GenerateKeys(1024);
        gk_Alice.createKeys();
        gk_Alice.writeToFile("KeyPair/publicKey_Alice", gk_Alice.getPublicKey().getEncoded());
        gk_Alice.writeToFile("KeyPair/privateKey_Alice", gk_Alice.getPrivateKey().getEncoded());

        gk_Bob = new GenerateKeys(1024);
        gk_Bob.createKeys();
        gk_Bob.writeToFile("KeyPair/publicKey_Bob", gk_Bob.getPublicKey().getEncoded());
        gk_Bob.writeToFile("KeyPair/privateKey_Bob", gk_Bob.getPrivateKey().getEncoded());

    }

}

出力:

java-hybrid-cryptography-8a

3. アリスは対称鍵を生成します

以下のコードを使用すると、対称キーを生成できます。 コードはSecureRandomを使用して、キーにランダム性を追加します。 SecureRandomの詳細については、Java – How to create strong random numbersを参照してください。

コンストラクターは、キーが保存されるディレクトリへのパス、キーの長さ、およびキーの作成に使用されるアルゴリズムで初期化されます。 各アルゴリズムのキーの長さの詳細については、Import Limits on Cryptographic Algorithmsを参照してください。

この例では、AESを使用します。これは、速度とセキュリティの間のシルバーの裏打ちと考えられているためです。 暗号化アルゴリズムについて詳しく知りたい場合は、Performance Analysis of Data Encryption Algorithms: 2.5 Compared Algorithmsを参照してください。

GenerateSymmetricKey.java

package com.example.onekey;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class GenerateSymmetricKey {

    private SecretKeySpec secretKey;

    public GenerateSymmetricKey(int length, String algorithm)
        throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException {

        SecureRandom rnd = new SecureRandom();
        byte [] key = new byte [length];
        rnd.nextBytes(key);
        this.secretKey = new SecretKeySpec(key, algorithm);

    }

    public SecretKeySpec getKey(){
        return this.secretKey;
    }

    public void writeToFile(String path, byte[] key) throws IOException {

        File f = new File(path);
        f.getParentFile().mkdirs();

        FileOutputStream fos = new FileOutputStream(f);
        fos.write(key);
        fos.flush();
        fos.close();

    }

    public static void main(String[] args)
        throws NoSuchAlgorithmException, NoSuchPaddingException, IOException {

        GenerateSymmetricKey genSK = new GenerateSymmetricKey(16, "AES");
        genSK.writeToFile("OneKey/secretKey", genSK.getKey().getEncoded());

    }
}

出力:

java-hybrid-cryptography-8b

4. アリスはメッセージを書く

java-hybrid-cryptography-8c

5. アリスはデータを暗号化します

5.1 The StartEncryption class contains the three methods getPrivate(), getPublic(), getSecretKey() that are used to build the corresponding keys from files. mainメソッドでは、暗号化を呼び出す2つのクラスに使用する必要のあるFile objectsを作成します。 EncryptDataおよびEncryptKey

StartEncryption.java

package com.example.hybrid.encrypt;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.spec.SecretKeySpec;

public class StartEncryption {

    public PrivateKey getPrivate(String filename, String algorithm) throws Exception {

        byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance(algorithm);
        return kf.generatePrivate(spec);

    }

    public PublicKey getPublic(String filename, String algorithm) throws Exception {

        byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance(algorithm);
        return kf.generatePublic(spec);

    }

    public SecretKeySpec getSecretKey(String filename, String algorithm) throws IOException{

        byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
        return new SecretKeySpec(keyBytes, algorithm);

    }

    public static void main(String[] args)
        throws IOException, GeneralSecurityException, Exception{

        StartEncryption startEnc = new StartEncryption();

        File originalKeyFile = new File("OneKey/secretKey");
        File encryptedKeyFile = new File("EncryptedFiles/encryptedSecretKey");
        new EncryptKey(startEnc.getPublic("KeyPair/publicKey_Bob", "RSA"),
                originalKeyFile, encryptedKeyFile, "RSA");

        File originalFile = new File("confidential.txt");
        File encryptedFile = new File("EncryptedFiles/encryptedFile");
        new EncryptData(originalFile, encryptedFile,
                startEnc.getSecretKey("OneKey/secretKey", "AES"), "AES");

    }

}

5.2 EncryptData constructor receives as parameters two File Objects (the first is the file we want to encrypt and the second is the output file), a SecretKeySpec Object which is the Symmetric Key we want to use for the encryption and a String with the name of the algorithm that we will use for the Cipher.

EncryptData.java

package com.example.hybrid.encrypt;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.SecretKeySpec;

public class EncryptData {

    private Cipher cipher;

    public EncryptData(File originalFile, File encrypted, SecretKeySpec secretKey, String cipherAlgorithm)
        throws IOException, GeneralSecurityException{

        this.cipher = Cipher.getInstance(cipherAlgorithm);
        encryptFile(getFileInBytes(originalFile), encrypted, secretKey);

    }

    public void encryptFile(byte[] input, File output, SecretKeySpec key)
        throws IOException, GeneralSecurityException {

        this.cipher.init(Cipher.ENCRYPT_MODE, key);
        writeToFile(output, this.cipher.doFinal(input));

    }

    private void writeToFile(File output, byte[] toWrite)
        throws IllegalBlockSizeException, BadPaddingException, IOException{

        output.getParentFile().mkdirs();
        FileOutputStream fos = new FileOutputStream(output);
        fos.write(toWrite);
        fos.flush();
        fos.close();
        System.out.println("The file was successfully encrypted and stored in: " + output.getPath());

    }

    public byte[] getFileInBytes(File f) throws IOException{

        FileInputStream fis = new FileInputStream(f);
        byte[] fbytes = new byte[(int) f.length()];
        fis.read(fbytes);
        fis.close();
        return fbytes;

    }

}

5.3 EncryptKey constructor receives as parameters the PublicKey that will be used for the encryption, two File Objects (the first is the file that contains the Symmetric Key and the second is the output file), and a String with the name of the algorithm that we will use for the Cipher.

EncryptKey.java

package com.example.hybrid.encrypt;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PublicKey;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;

public class EncryptKey {

    private Cipher cipher;

    public EncryptKey(PublicKey key, File originalKeyFile, File encryptedKeyFile, String cipherAlgorithm)
        throws IOException, GeneralSecurityException{

        this.cipher = Cipher.getInstance(cipherAlgorithm);
        encryptFile(getFileInBytes(originalKeyFile), encryptedKeyFile, key);

    }

    public void encryptFile(byte[] input, File output, PublicKey key)
        throws IOException, GeneralSecurityException {

        this.cipher.init(Cipher.ENCRYPT_MODE, key);
        writeToFile(output, this.cipher.doFinal(input));

    }

    private void writeToFile(File output, byte[] toWrite)
        throws IllegalBlockSizeException, BadPaddingException, IOException{

        output.getParentFile().mkdirs();
        FileOutputStream fos = new FileOutputStream(output);
        fos.write(toWrite);
        fos.flush();
        fos.close();
        System.out.println("The key was successfully encrypted and stored in: " + output.getPath());

    }

    public byte[] getFileInBytes(File f) throws IOException{

        FileInputStream fis = new FileInputStream(f);
        byte[] fbytes = new byte[(int) f.length()];
        fis.read(fbytes);
        fis.close();
        return fbytes;

    }

}

出力:

The key was successfully encrypted and stored in: EncryptedFiles\encryptedSecretKey
The file was successfully encrypted and stored in: EncryptedFiles\encryptedFile

java-hybrid-cryptography-8d

6. ボブはデータを受信し、解読します

6.1 The StartDecryption class contains the three methods getPrivate(), getPublic(), getSecretKey() that are used to build the corresponding keys from files. mainメソッドでは、復号化のために呼び出す2つのクラスに使用する必要があるFileオブジェクトを作成します。 DecryptDataおよびDecryptKey

StartDecryption.java

package com.example.hybrid.decrypt;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.spec.SecretKeySpec;

public class StartDecryption {

    public PrivateKey getPrivate(String filename, String algorithm) throws Exception {

        byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance(algorithm);
        return kf.generatePrivate(spec);

    }

    public PublicKey getPublic(String filename, String algorithm) throws Exception {

        byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance(algorithm);
        return kf.generatePublic(spec);

    }

    public SecretKeySpec getSecretKey(String filename, String algorithm) throws IOException{

        byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
        return new SecretKeySpec(keyBytes, algorithm);

    }

    public static void main(String[] args) throws IOException, GeneralSecurityException, Exception{

        StartDecryption startEnc = new StartDecryption();

        File encryptedKeyReceived = new File("EncryptedFiles/encryptedSecretKey");
        File decreptedKeyFile = new File("DecryptedFiles/SecretKey");
        new DecryptKey(startEnc.getPrivate("KeyPair/privateKey_Bob", "RSA"),
                encryptedKeyReceived, decreptedKeyFile, "RSA");

        File encryptedFileReceived = new File("EncryptedFiles/encryptedFile");
        File decryptedFile = new File("DecryptedFiles/decryptedFile");
        new DecryptData(encryptedFileReceived, decryptedFile,
                startEnc.getSecretKey("DecryptedFiles/SecretKey", "AES"), "AES");

    }
}

6.2 DecryptKey constructor receives as parameters the PrivateKey that will be used for the decryption, two File Objects (the first is the encrypted key and the second is the output file) and a String with the name of the algorithm that we will use for the Cipher.

DecryptKey.java

package com.example.hybrid.decrypt;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;

public class DecryptKey {

    private Cipher cipher;

    public DecryptKey(PrivateKey privateKey, File encryptedKeyReceived, File decreptedKeyFile, String algorithm)
        throws IOException, GeneralSecurityException {

        this.cipher = Cipher.getInstance(algorithm);
        decryptFile(getFileInBytes(encryptedKeyReceived), decreptedKeyFile, privateKey);

    }

    public void decryptFile(byte[] input, File output, PrivateKey key)
        throws IOException, GeneralSecurityException {

        this.cipher.init(Cipher.DECRYPT_MODE, key);
        writeToFile(output, this.cipher.doFinal(input));

    }

    private void writeToFile(File output, byte[] toWrite)
        throws IllegalBlockSizeException, BadPaddingException, IOException{

        output.getParentFile().mkdirs();
        FileOutputStream fos = new FileOutputStream(output);
        fos.write(toWrite);
        fos.flush();
        fos.close();

    }

    public byte[] getFileInBytes(File f) throws IOException{

        FileInputStream fis = new FileInputStream(f);
        byte[] fbytes = new byte[(int) f.length()];
        fis.read(fbytes);
        fis.close();
        return fbytes;

    }

}

6.3 DecryptData constructor receives as parameters two File Objects (the first is the encrypted file and the second is the output file), a SecretKeySpec Object which is the Symmetric Key we want to use for the decryption and a String with the name of the algorithm that we will use for the Cipher.

DecryptData.java

package com.example.hybrid.decrypt;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.SecretKeySpec;

public class DecryptData {

    private Cipher cipher;

    public DecryptData(File encryptedFileReceived, File decryptedFile, SecretKeySpec secretKey, String algorithm)
        throws IOException, GeneralSecurityException {

        this.cipher = Cipher.getInstance(algorithm);
        decryptFile(getFileInBytes(encryptedFileReceived), decryptedFile, secretKey);

    }

    public void decryptFile(byte[] input, File output, SecretKeySpec key)
        throws IOException, GeneralSecurityException {

        this.cipher.init(Cipher.DECRYPT_MODE, key);
        writeToFile(output, this.cipher.doFinal(input));

    }

    private void writeToFile(File output, byte[] toWrite)
        throws IllegalBlockSizeException, BadPaddingException, IOException{

        output.getParentFile().mkdirs();
        FileOutputStream fos = new FileOutputStream(output);
        fos.write(toWrite);
        fos.flush();
        fos.close();
        System.out.println("The file was successfully decrypted. You can view it in: " + output.getPath());

    }

    public byte[] getFileInBytes(File f) throws IOException{

        FileInputStream fis = new FileInputStream(f);
        byte[] fbytes = new byte[(int) f.length()];
        fis.read(fbytes);
        fis.close();
        return fbytes;

    }

}

出力:

The file was successfully decrypted. You can view it in: DecryptedFiles\decryptedFile

java-hybrid-cryptography-8e

ソースコードをダウンロード

ダウンロード–HybridCryptographyExample.zip(11 KB)