Java - Exemple de cryptographie hybride

Java - Exemple de cryptographie hybride

encrypted

La cryptographie hybride est la doublure argentée entre la cryptographie sûre mais lente sur les mégadonnées (cryptographie asymétrique) et la cryptographie dangereuse mais rapide (cryptographie symétrique). La cryptographie hybride combine la vitesse de chiffrement et de déchiffrement à une clé avec la sécurité fournie par la paire de clés publique-privée et est donc considérée comme un type de chiffrement hautement sécurisé.

La méthodologie la plus courante pour la cryptographie hybride consiste à chiffrer les données à l'aide d'une clé symétrique qui sera ensuite chiffrée avec la clé privée de l'expéditeur ou la clé publique du récepteur. Pour déchiffrer, le récepteur devra d'abord déchiffrer la clé symétrique à l'aide de la clé asymétrique correspondante, puis utiliser cette clé symétrique pour déchiffrer les données qu'il a reçues. Cet exemple pourrait aider à clarifier que:

Imaginez un scénario avec deux utilisateurs, Alice et Bob, qui ont déjà échangé des clés publiques. Alice veut envoyer à Bob un fichier texte appelé "confidential.txt". Les étapes qu'elle suivra pour crypter son message sont les suivantes:

  1. Génère une nouvelle clé symétrique à l'aide d'un algorithme puissant

  2. Crypte «confidential.txt» à l'aide de la clé de l'étape 1

  3. Chiffre la clé de l'étape 1 à l'aide de la clé publique de Bob

  4. Envoie à la fois le texte crypté et la clé à Bob

Bob reçoit les deux fichiers. Les étapes qu'il suivra pour les décrypter sont les suivantes:

  1. Décrypte la clé qu'il a reçue en utilisant sa propre clé privée

  2. Décrypte le texte qu'il a reçu à l'aide de la clé qu'il a obtenue à l'étape 1 après le décryptage

Note
Si vous voulez en savoir plus sur le cryptage symétrique et asymétrique, reportez-vous àSymmetric-Key Cryptography example etAsymmetric-Key Cryptography example

Les processus dans les codes ci-dessous sont divisés en fichiers séparés pour aider à comprendre le processus et à faciliter l'apprenant.

1. Répertoire des projets

java-hybrid-cryptography-8f

2. Générer des clés pour Alice et Bob

Il existe plusieurs façons de générer une paire de clés publique-privée en fonction de votre plate-forme. Dans cet exemple, nous allons créer deux paires, une pour Alice et une pour Bob en utilisant Java. L'algorithme cryptographique que nous utiliserons dans cet exemple est 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());

    }

}

Sortie:

java-hybrid-cryptography-8a

3. Alice génère une clé symétrique

Avec le code ci-dessous, vous pouvez générer une clé symétrique. Le code utiliseSecureRandom pour ajouter du caractère aléatoire à la clé. Pour plus d'informations surSecureRandom, reportez-vous àJava – How to create strong random numbers.

Le constructeur est initialisé avec le chemin d'accès au répertoire où la clé sera enregistrée, la longueur de la clé et l'algorithme qui sera utilisé pour la créer. Pour en savoir plus sur les longueurs de clé de chaque algorithme, reportez-vous àImport Limits on Cryptographic Algorithms.

Dans cet exemple, nous utilisons AES car il est considéré comme la doublure argentée entre vitesse et sécurité. Si vous souhaitez en savoir plus sur les algorithmes de chiffrement, reportez-vous à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());

    }
}

Sortie:

java-hybrid-cryptography-8b

4. Alice écrit son message

java-hybrid-cryptography-8c

5. Alice crypte les données

5.1 The StartEncryption class contains the three methods getPrivate(), getPublic(), getSecretKey() that are used to build the corresponding keys from files. Dans la méthode principale, nous construisons lesFile objects que nous devons utiliser pour les deux classes que nous appellerons pour le chiffrement; EncryptData etEncryptKey.

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;

    }

}

Sortie:

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. Bob reçoit les données et les déchiffre

6.1 The StartDecryption class contains the three methods getPrivate(), getPublic(), getSecretKey() that are used to build the corresponding keys from files. Dans la méthode principale, nous construisons les objetsFile que nous devons utiliser pour les deux classes que nous appellerons pour le déchiffrement; DecryptData etDecryptKey.

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;

    }

}

Sortie:

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

java-hybrid-cryptography-8e

Télécharger le code source

Téléchargez-le -HybridCryptographyExample.zip (11 Ko)