且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Java 中基于 AES-256 密码的加密/解密

更新时间:2023-02-17 18:32:53

我认为你犯了两个错误 :)

我已经更正了您的示例代码以使其正常工作:

import java.security.AlgorithmParameters;导入 java.security.NoSuchAlgorithmException;导入 java.security.SecureRandom;导入 javax.crypto.BadPaddingException;导入 javax.crypto.Cipher;导入 javax.crypto.IllegalBlockSizeException;导入 javax.crypto.SecretKey;导入 javax.crypto.SecretKeyFactory;导入 javax.crypto.spec.IvParameterSpec;导入 javax.crypto.spec.PBEKeySpec;导入 javax.crypto.spec.SecretKeySpec;导入 javax.xml.bind.DatatypeConverter;公共类加密解密{私有静态字符串盐;私有静态整数迭代 = 65536 ;私有静态 int keySize = 256;私有静态字节[] ivBytes;私有静态 SecretKey secretKey;public static void main(String []args) 抛出异常 {盐 = getSalt();char[] message = "PasswordToEncrypt".toCharArray();System.out.println("消息:" + String.valueOf(message));System.out.println("加密:" + encrypt(message));System.out.println("解密:" + 解密(加密(message).toCharArray()));}公共静态字符串加密(字符 [] 明文)抛出异常 {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"));返回 DatatypeConverter.printBase64Binary(encryptedTextBytes);}公共静态字符串解密(char [] encryptedText)抛出异常{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));字节 [] 解密文本字节 = 空;尝试 {解密文本字节 = cipher.doFinal(encryptedTextBytes);} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();}返回新字符串(解密文本字节);}公共静态字符串 getSalt() 抛出异常 {SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");字节[]盐=新字节[20];sr.nextBytes(盐);返回新字符串(盐);}}

第一个错误是您生成了 2 个不同的盐(使用加密方法时),因此加密/解密日志是不同的(合乎逻辑,但解密仍然有效,因为您在加密后直接调用解密).>

第二个错误是密钥错误.加密时需要生成密钥,但不解密时需要生成密钥.更简单地说,就好像我用密码encrypt"加密,而你试图用密码decrypt"解密.

我建议您在启动时生成所有随机内容(例如私钥、盐等).但请注意,当您停止应用时,除非获得完全相同的随机内容,否则您将无法解密旧内容.

希望我有所帮助:)

问候,

I found a guide for implementing AES encryption/decryption in Java and tried to understand each line as I put it into my own solution. However, I don't fully understand it and am having issues as a result. The end goal is to have passphrase based encryption/decryption. I've read other articles/*** posts about this, but most do not provide enough explanation (I am very new to crypto in Java)

My main issues right now are that even when I set byte[] saltBytes = "Hello".getBytes(); I still get a different Base64 result in the end (char[] password is random each time, but I read that it is safer to leave passwords in char[] form. My other problem is that when the program gets to decrypt(), I get a NullPointerException at byte[] saltBytes = salt.getBytes("UTF-8");

Thank you in advance for any help/advice you can give me.

The code in question:

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();
    }
}

I think that you are making two mistakes :)

I've corrected your sample code to make it work :

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);
    }
}

The first mistake is that you generate 2 different salts (when using the encrypt method), so encrypted/decrypted logs were differents (logical, but the decryption would still work because you are calling the decryption directly after encryption).

The second mistake was for the secret key. You need to generate a secret key when you are encrypting, but not decrypting. To put it more simply, it is as if i was encrypting with the password "encrypt" and that you are trying to decrypt it with the password "decrypt".

I would advise you to generate every random stuff (such as private key, salt etc on startup). But beware that when you'll stop your app, you won't be able to decrypt old stuff unless getting the exact same random stuff.

Hope I helped :)

Regards,