Utilisation du chiffrement par mot de passe sur un fichier en Java


J'essaie de chiffrer le contenu d'un fichier dans un autre fichier en utilisant une phrase de passe en Java. Le fichier est lu à un tableau d'octets, chiffré à un autre tableau d'octets, et puis écrit dans le nouveau fichier. Malheureusement, lorsque j'essaie d'inverser le cryptage, le fichier de sortie est déchiffré comme une poubelle.

Je soupçonne fortement que le problème a à voir avec la génération d'une clé identique à chaque fois que la même phrase de passe est utilisée. J'ai écrit une méthode de test qui vide la clé dans un fichier chaque fois que l'on est généré. La clé est enregistrée à la fois directement et sous forme codée. Le premier est identique à chaque fois, mais le second est toujours différent pour une raison quelconque.

En toute honnêteté, je ne connais pas beaucoup les méthodes de cryptage, en particulier en Java. J'ai seulement besoin que les données soient modérément sécurisées, et le cryptage n'a pas à résister à une attaque de quiconque ayant beaucoup de temps et de compétences. Merci d'avance à tous ceux qui ont des conseils à ce sujet.

Modifier: Esailija a eu la gentillesse de souligner que je définissais toujours le chiffrement avec ENCRYPT_MODE. J'ai corrigé le problème en utilisant un argument booléen, mais maintenant j'obtiens l'exception suivante:

Javax.cryptographique.IllegalBlockSizeException: La longueur d'entrée doit être multiple de 8 lors du déchiffrement avec un chiffrement rembourré

Cela me semble que la phrase secrète n'est pas utilisée correctement. J'avais l'impression que "PBEWithMD5AndDES" le hacherait en un code de 16 octets, ce qui est très certainement un multiple de 8. Je me demande pourquoi la clé génère et s'utilise très bien pour le mode de cryptage, mais elle se plaint ensuite en essayant de déchiffrer exactement dans les mêmes conditions.

import java.various.stuff;

/**Utility class to encrypt and decrypt files**/
public class FileEncryptor {
    //Arbitrarily selected 8-byte salt sequence:
    private static final byte[] salt = {
        (byte) 0x43, (byte) 0x76, (byte) 0x95, (byte) 0xc7,
        (byte) 0x5b, (byte) 0xd7, (byte) 0x45, (byte) 0x17 
    };

    private static Cipher makeCipher(String pass, Boolean decryptMode) throws GeneralSecurityException{

        //Use a KeyFactory to derive the corresponding key from the passphrase:
        PBEKeySpec keySpec = new PBEKeySpec(pass.toCharArray());
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey key = keyFactory.generateSecret(keySpec);

        //Create parameters from the salt and an arbitrary number of iterations:
        PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 42);

        /*Dump the key to a file for testing: */
        FileEncryptor.keyToFile(key);

        //Set up the cipher:
        Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");

        //Set the cipher mode to decryption or encryption:
        if(decryptMode){
            cipher.init(Cipher.ENCRYPT_MODE, key, pbeParamSpec);
        } else {
            cipher.init(Cipher.DECRYPT_MODE, key, pbeParamSpec);
        }

        return cipher;
    }


    /**Encrypts one file to a second file using a key derived from a passphrase:**/
    public static void encryptFile(String fileName, String pass)
                                throws IOException, GeneralSecurityException{
        byte[] decData;
        byte[] encData;
        File inFile = new File(fileName);

        //Generate the cipher using pass:
        Cipher cipher = FileEncryptor.makeCipher(pass, false);

        //Read in the file:
        FileInputStream inStream = new FileInputStream(inFile);
        decData = new byte[(int)inFile.length()];
        inStream.read(decData);
        inStream.close();

        //Encrypt the file data:
        encData = cipher.doFinal(decData);


        //Write the encrypted data to a new file:
        FileOutputStream outStream = new FileOutputStream(new File(fileName + ".encrypted"));
        outStream.write(encData);
        outStream.close();
    }


    /**Decrypts one file to a second file using a key derived from a passphrase:**/
    public static void decryptFile(String fileName, String pass)
                            throws GeneralSecurityException, IOException{
        byte[] encData;
        byte[] decData;
        File inFile = new File(fileName);

        //Generate the cipher using pass:
        Cipher cipher = FileEncryptor.makeCipher(pass, true);

        //Read in the file:
        FileInputStream inStream = new FileInputStream(inFile);
        encData = new byte[(int)inFile.length()];
        inStream.read(encData);
        inStream.close();

        //Decrypt the file data:
        decData = cipher.doFinal(encData);

        //Write the decrypted data to a new file:
        FileOutputStream target = new FileOutputStream(new File(fileName + ".decrypted.txt"));
        target.write(decData);
        target.close();
    }

    /**Record the key to a text file for testing:**/
    private static void keyToFile(SecretKey key){
        try {
            File keyFile = new File("C:\\keyfile.txt");
            FileWriter keyStream = new FileWriter(keyFile);
            String encodedKey = "\n" + "Encoded version of key:  " + key.getEncoded().toString();
            keyStream.write(key.toString());
            keyStream.write(encodedKey);
            keyStream.close();
        } catch (IOException e) {
            System.err.println("Failure writing key to file");
            e.printStackTrace();
        }

    }

}
Author: user1870901, 2012-12-03

2 answers

Vous utilisez le Cipher.ENCRYPT_MODE pour le déchiffrement et le chiffrement. Vous devez utiliser Cipher.DECRYPT_MODE pour déchiffrer le fichier.

Qui a été corrigé, mais votre booléen est faux. Il devrait être vrai pour crypter et faux pour décrypter. Je recommanderais fortement de ne pas utiliser false/true comme arguments de fonction et d'utiliser toujours enum comme Cipher.ENCRYPT... passer à autre chose

Alors vous chiffrez à .fichier crypté, mais en essayant de déchiffrer le fichier texte brut d'origine.

Alors vous n'appliquez pas de remplissage à cryptage. Je suis surpris que cela doive être fait manuellement, mais le remplissage est expliqué ici . Le schéma de remplissage PKCS5 semblait être implicitement utilisé ici.

Il s'agit d'un code de travail complet, écrivant un fichier crypté sur test.txt.encrypted et un fichier déchiffré sur test.txt.decrypted.txt. L'ajout de remplissage dans le cryptage et sa suppression dans le décryptage sont expliqués dans les commentaires.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

public class FileEncryptor {

    public static void main( String[] args ) {

        try {
            encryptFile( "C:\\test.txt", "password" );
            decryptFile( "C:\\test.txt", "password" );
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (GeneralSecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    //Arbitrarily selected 8-byte salt sequence:
    private static final byte[] salt = {
        (byte) 0x43, (byte) 0x76, (byte) 0x95, (byte) 0xc7,
        (byte) 0x5b, (byte) 0xd7, (byte) 0x45, (byte) 0x17 
    };

    private static Cipher makeCipher(String pass, Boolean decryptMode) throws GeneralSecurityException{

        //Use a KeyFactory to derive the corresponding key from the passphrase:
        PBEKeySpec keySpec = new PBEKeySpec(pass.toCharArray());
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey key = keyFactory.generateSecret(keySpec);

        //Create parameters from the salt and an arbitrary number of iterations:
        PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 42);

        /*Dump the key to a file for testing: */
        FileEncryptor.keyToFile(key);

        //Set up the cipher:
        Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");

        //Set the cipher mode to decryption or encryption:
        if(decryptMode){
            cipher.init(Cipher.ENCRYPT_MODE, key, pbeParamSpec);
        } else {
            cipher.init(Cipher.DECRYPT_MODE, key, pbeParamSpec);
        }

        return cipher;
    }


    /**Encrypts one file to a second file using a key derived from a passphrase:**/
    public static void encryptFile(String fileName, String pass)
                                throws IOException, GeneralSecurityException{
        byte[] decData;
        byte[] encData;
        File inFile = new File(fileName);
        //Generate the cipher using pass:
        Cipher cipher = FileEncryptor.makeCipher(pass, true);

        //Read in the file:
        FileInputStream inStream = new FileInputStream(inFile);

        int blockSize = 8;
        //Figure out how many bytes are padded
        int paddedCount = blockSize - ((int)inFile.length()  % blockSize );

        //Figure out full size including padding
        int padded = (int)inFile.length() + paddedCount;

        decData = new byte[padded];


        inStream.read(decData);

        inStream.close();

        //Write out padding bytes as per PKCS5 algorithm
        for( int i = (int)inFile.length(); i < padded; ++i ) {
            decData[i] = (byte)paddedCount;
        }

        //Encrypt the file data:
        encData = cipher.doFinal(decData);


        //Write the encrypted data to a new file:
        FileOutputStream outStream = new FileOutputStream(new File(fileName + ".encrypted"));
        outStream.write(encData);
        outStream.close();
    }


    /**Decrypts one file to a second file using a key derived from a passphrase:**/
    public static void decryptFile(String fileName, String pass)
                            throws GeneralSecurityException, IOException{
        byte[] encData;
        byte[] decData;
        File inFile = new File(fileName+ ".encrypted");

        //Generate the cipher using pass:
        Cipher cipher = FileEncryptor.makeCipher(pass, false);

        //Read in the file:
        FileInputStream inStream = new FileInputStream(inFile );
        encData = new byte[(int)inFile.length()];
        inStream.read(encData);
        inStream.close();
        //Decrypt the file data:
        decData = cipher.doFinal(encData);

        //Figure out how much padding to remove

        int padCount = (int)decData[decData.length - 1];

        //Naive check, will fail if plaintext file actually contained
        //this at the end
        //For robust check, check that padCount bytes at the end have same value
        if( padCount >= 1 && padCount <= 8 ) {
            decData = Arrays.copyOfRange( decData , 0, decData.length - padCount);
        }

        //Write the decrypted data to a new file:



        FileOutputStream target = new FileOutputStream(new File(fileName + ".decrypted.txt"));
        target.write(decData);
        target.close();
    }

    /**Record the key to a text file for testing:**/
    private static void keyToFile(SecretKey key){
        try {
            File keyFile = new File("C:\\keyfile.txt");
            FileWriter keyStream = new FileWriter(keyFile);
            String encodedKey = "\n" + "Encoded version of key:  " + key.getEncoded().toString();
            keyStream.write(key.toString());
            keyStream.write(encodedKey);
            keyStream.close();
        } catch (IOException e) {
            System.err.println("Failure writing key to file");
            e.printStackTrace();
        }

    }
}
 6
Author: Esailija, 2012-12-02 23:05:28

Ce sont quelques améliorations à la réponse de @Esailija compte tenu de nouvelles fonctionnalités en Java.

Mon utilisation des classes CipherInputStream et CipherOutputStream, la longueur et la complexité du code sont considérablement réduites.

J'utilise également char [] au lieu de String pour le mot de passe.

Vous pouvez utiliser le système.console().readPassword ("input password:") pour obtenir le mot de passe en tant que char[] afin qu'il ne soit jamais une chaîne.

public static void encryptFile(String inFileName, String outFileName, char[] pass) throws IOException, GeneralSecurityException {
    Cipher cipher = PasswordProtectFile.makeCipher(pass, true);
    try (CipherOutputStream cipherOutputStream = new CipherOutputStream(new FileOutputStream(outFileName), cipher);
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inFileName))) {
        int i;
        while ((i = bis.read()) != -1) {
            cipherOutputStream.write(i);
        }
    }
}

public static void decryptFile(String inFileName, String outFileName, char[] pass) throws GeneralSecurityException, IOException {
    Cipher cipher = PasswordProtectFile.makeCipher(pass, false);
    try (CipherInputStream cipherInputStream = new CipherInputStream(new FileInputStream(inFileName), cipher);
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outFileName))) {
        int i;
        while ((i = cipherInputStream.read()) != -1) {
            bos.write(i);
        }
    }
}

private static Cipher makeCipher(char[] pass, Boolean decryptMode) throws GeneralSecurityException {

    // Use a KeyFactory to derive the corresponding key from the passphrase:
    PBEKeySpec keySpec = new PBEKeySpec(pass);
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
    SecretKey key = keyFactory.generateSecret(keySpec);

    // Create parameters from the salt and an arbitrary number of iterations:
    PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 43);

    // Set up the cipher:
    Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");

    // Set the cipher mode to decryption or encryption:
    if (decryptMode) {
        cipher.init(Cipher.ENCRYPT_MODE, key, pbeParamSpec);
    } else {
        cipher.init(Cipher.DECRYPT_MODE, key, pbeParamSpec);
    }

    return cipher;
}
 1
Author: BPS, 2016-08-29 16:23:08