Java - пример гибридной криптографии

Java - пример гибридной криптографии

encrypted

Гибридная криптография - это серебряная линия между безопасной, но медленной криптографией больших данных (асимметричная криптография) и небезопасной, но быстрой криптографией (симметричная криптография). Гибридная криптография сочетает в себе скорость шифрования и дешифрования одним ключом вместе с безопасностью, которую обеспечивает пара «открытый-закрытый ключ», и, таким образом, считается очень безопасным типом шифрования.

Наиболее распространенной методологией гибридной криптографии является шифрование данных с использованием симметричного ключа, который затем будет зашифрован с помощью закрытого ключа отправителя или открытого ключа получателя. Чтобы расшифровать, получатель должен будет сначала расшифровать Симметричный ключ, используя соответствующий Асимметричный Ключ, а затем использовать этот Симметричный Ключ для расшифровки полученных данных. Этот пример может помочь уточнить, что:

Представьте себе сценарий с двумя пользователями, Алисой и Бобом, которые уже обменялись открытыми ключами. Алиса хочет отправить Бобу текстовый файл с именем «confidential.txt». Шаги, которые она будет выполнять для шифрования своего сообщения, следующие:

  1. Создает новый симметричный ключ, используя сильный алгоритм

  2. Зашифровывает «confidential.txt», используя ключ из шага 1

  3. Зашифровывает ключ из шага 1, используя открытый ключ Боба

  4. Посылает зашифрованный текст и ключ Бобу

Боб получает два файла. Шаги, которые он будет выполнять, чтобы расшифровать их, следующие:

  1. Расшифровывает ключ, который он получил, используя собственный закрытый ключ

  2. Расшифровывает полученный текст с помощью ключа, полученного на шаге 1 после расшифровки

Note
Если вы хотите узнать больше о симметричном и асимметричном шифровании, обратитесь кSymmetric-Key Cryptography example иAsymmetric-Key Cryptography example

Процессы в кодах ниже разбиты на отдельные файлы, чтобы помочь понять процесс и облегчить ученику.

1. Каталог проектов

java-hybrid-cryptography-8f

2. Создать ключи для Алисы и Боба

Существует несколько способов создания пары «открытый-закрытый ключ» в зависимости от вашей платформы. В этом примере мы создадим две пары, одну для Алисы и одну для Боба, используя java. Криптографический алгоритм, который мы будем использовать в этом примере, - это 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. В основном методе мы создаем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. В основном методе мы создаем объекты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 КБ)