Шифрование/дешифрование на основе пароля AES-256 в Java

Я нашел руководство по внедрению шифрования/дешифрования AES в Java и попытался понять каждую строку, когда добавлял ее в свое собственное решение. Однако я не совсем понимаю это, и в результате у меня возникают проблемы. Конечная цель состоит в том, чтобы иметь шифрование/дешифрование на основе парольной фразы. Я читал об этом другие статьи/сообщения stackoverflow, но большинство из них не дают достаточного объяснения (я очень новичок в криптографии на Java)

Моя основная проблема сейчас заключается в том, что даже когда я устанавливаю byte[] saltBytes = "Hello".getBytes();, я все равно получаю другой результат Base64 в конце (char[] password каждый раз случайный, но я читал, что безопаснее оставлять пароли в форме char[]. Моя другая проблема заключается в том, что когда программа достигает decrypt(), я получаю NullPointerException в byte[] saltBytes = salt.getBytes("UTF-8");

Заранее благодарю вас за любую помощь/совет, который вы можете мне дать.

Рассматриваемый код:

import java.security.AlgorithmParameters;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

public class EncryptionDecryption {

    private static String salt;
    private static int iterations = 65536  ;
    private static int keySize = 256;
    private static byte[] ivBytes;

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

        char[] message = "PasswordToEncrypt".toCharArray();
        System.out.println("Message: " + message.toString());
        System.out.println("Encrypted: " + encrypt(message));
        System.out.println("Decrypted: " + decrypt(encrypt(message).toCharArray()));
    }

    public static String encrypt(char[] plaintext) throws Exception {

        salt = getSalt();
        byte[] saltBytes = salt.getBytes();

        SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec spec = new PBEKeySpec(plaintext, saltBytes, iterations, keySize);
        SecretKey secretKey = skf.generateSecret(spec);
        SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretSpec);
        AlgorithmParameters params = cipher.getParameters();
        ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
        byte[] encryptedTextBytes = cipher.doFinal(plaintext.toString().getBytes("UTF-8"));

        return DatatypeConverter.printBase64Binary(encryptedTextBytes);
    }

    public static String decrypt(char[] encryptedText) throws Exception {

        byte[] saltBytes = salt.getBytes("UTF-8");
        byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(encryptedText.toString());

        SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec spec = new PBEKeySpec(encryptedText, saltBytes, iterations, keySize);
        SecretKey secretkey = skf.generateSecret(spec);
        SecretKeySpec secretSpec = new SecretKeySpec(secretkey.getEncoded(), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretSpec, new IvParameterSpec(ivBytes));

        byte[] decryptedTextBytes = null;

        try {
            decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
        }   catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }   catch (BadPaddingException e) {
            e.printStackTrace();
        }

        return decryptedTextBytes.toString();

    }

    public static String getSalt() throws Exception {

        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[20];
        sr.nextBytes(salt);
        return salt.toString();
    }
}

person volo_java    schedule 20.02.2015    source источник


Ответы (1)


Я думаю, что вы делаете две ошибки :)

Я исправил ваш пример кода, чтобы он работал:

import java.security.AlgorithmParameters;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

public class EncryptionDecryption {

    private static String salt;
    private static int iterations = 65536  ;
    private static int keySize = 256;
    private static byte[] ivBytes;

    private static SecretKey secretKey;

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

        salt = getSalt();

        char[] message = "PasswordToEncrypt".toCharArray();
        System.out.println("Message: " + String.valueOf(message));
        System.out.println("Encrypted: " + encrypt(message));
        System.out.println("Decrypted: " + decrypt(encrypt(message).toCharArray()));
    }

    public static String encrypt(char[] plaintext) throws Exception {
        byte[] saltBytes = salt.getBytes();

        SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec spec = new PBEKeySpec(plaintext, saltBytes, iterations, keySize);
        secretKey = skf.generateSecret(spec);
        SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretSpec);
        AlgorithmParameters params = cipher.getParameters();
        ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
        byte[] encryptedTextBytes = cipher.doFinal(String.valueOf(plaintext).getBytes("UTF-8"));

        return DatatypeConverter.printBase64Binary(encryptedTextBytes);
    }

    public static String decrypt(char[] encryptedText) throws Exception {

        System.out.println(encryptedText);

        byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(new String(encryptedText));
        SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretSpec, new IvParameterSpec(ivBytes));

        byte[] decryptedTextBytes = null;

        try {
            decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
        }   catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }   catch (BadPaddingException e) {
            e.printStackTrace();
        }

        return new String(decryptedTextBytes);

    }

    public static String getSalt() throws Exception {

        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[20];
        sr.nextBytes(salt);
        return new String(salt);
    }
}

Первая ошибка заключается в том, что вы создаете 2 разные соли (при использовании метода шифрования), поэтому зашифрованные/расшифрованные журналы были разными (логично, но расшифровка все равно будет работать, потому что вы вызываете расшифровку сразу после шифрования).

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

Я бы посоветовал вам генерировать каждый случайный материал (например, закрытый ключ, соль и т. д.) при запуске. Но имейте в виду, что когда вы остановите свое приложение, вы не сможете расшифровать старые вещи, если не получите точно такие же случайные вещи.

Надеюсь помог :)

С уважением,

person Community    schedule 20.02.2015
comment
DatatypeConverter больше не поддерживается? - person Ghanshyam Nayma; 07.03.2017
comment
Можете ли вы помочь мне изменить ваш код в соответствии с этой командой openssl aes-128-cbc -a -e -in hello.txt -md sha256 . Я использую пароль как «проход». Эта команда работает нормально, но с кодом я получаю Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 4 bytes at com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:87) - person QubesX; 25.01.2019