Java 中基于 AES-256 密码的加密/解密 [英] AES-256 Password Based Encryption/Decryption in Java

查看:34
本文介绍了Java 中基于 AES-256 密码的加密/解密的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我找到了一份在 Java 中实现 AES 加密/解密的指南,并试图理解每一行,因为我将其放入我自己的解决方案中.但是,我并不完全理解它,因此遇到了问题.最终目标是拥有基于密码的加密/解密.我已经阅读了关于此的其他文章/stackoverflow 帖子,但大多数都没有提供足够的解释(我对 Java 中的加密非常陌生)

我现在的主要问题是即使我设置了 byte[] saltBytes = "Hello".getBytes();最后我仍然得到不同的 Base64 结果(char[] password 每次都是随机的,但我读到将密码保留为 char[] 形式更安全.我的另一个问题是,当程序到达 decrypt() 时,我在byte[] saltBytes = salt.getBytes("UTF-8");

预先感谢您给我的任何帮助/建议.

有问题的代码:

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;public static void main(String []args) 抛出异常 {char[] message = "PasswordToEncrypt".toCharArray();System.out.println("消息:" + message.toString());System.out.println("加密:" + encrypt(message));System.out.println("解密:" + 解密(加密(message).toCharArray()));}公共静态字符串加密(字符 [] 明文)抛出异常 {盐 = 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"));返回 DatatypeConverter.printBase64Binary(encryptedTextBytes);}公共静态字符串解密(char [] encryptedText)抛出异常{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));字节 [] 解密文本字节 = 空;尝试 {解密文本字节 = cipher.doFinal(encryptedTextBytes);} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();}返回decryptedTextBytes.toString();}公共静态字符串 getSalt() 抛出异常 {SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");字节[]盐=新字节[20];sr.nextBytes(盐);返回 salt.toString();}}

解决方案

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

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

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/stackoverflow 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,

这篇关于Java 中基于 AES-256 密码的加密/解密的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆