【问题标题】:AES-256 Password Based Encryption/Decryption in JavaJava中基于AES-256密码的加密/解密
【发布时间】:2015-04-21 17:54:02
【问题描述】:

我找到了在 Java 中实现 AES 加密/解密的指南,并在我将其放入自己的解决方案时尝试理解每一行。但是,我并不完全理解它,因此遇到了问题。最终目标是拥有基于密码的加密/解密。我已阅读有关此的其他文章/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();
    }
}

【问题讨论】:

    标签: java encryption passwords aes password-encryption


    【解决方案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 个不同的盐(使用 encrypt 方法时),因此加密/解密的日志是不同的(合乎逻辑,但解密仍然有效,因为您在加密后直接调用解密)。

    第二个错误是密钥。加密时需要生成密钥,但不解密时需要生成密钥。更简单地说,就好像我正在使用密码“encrypt”进行加密,而您正在尝试使用密码“decrypt”对其进行解密。

    我建议您在启动时生成每个随机的东西(例如私钥、盐等)。但请注意,当您停止应用时,您将无法解密旧内容,除非得到完全相同的随机内容。

    希望我能帮上忙:)

    问候,

    【讨论】:

    • DatatypeConverter 现在不支持了?
    • 你能帮我按照这个命令修改你的代码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)
    猜你喜欢
    • 2010-11-02
    • 1970-01-01
    • 2022-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多