当前位置: 首页 > 知识库问答 >
问题:

使用PBKDF2和AES256进行加密和解密-需要实际示例-如何获取派生密钥

吕德惠
2023-03-14

我试图了解如何通过使用PBKDF2和SHA256获得派生密钥。

我很纠结,需要一个清晰易懂的例子。

到目前为止我所拥有的:

>

  • 我找到了https://en.wikipedia.org/wiki/PBKDF2其中有一个示例,但使用SHA1,具有以下值:

    密码plnlrtfpijpuhqylxbgqiyipieyxvfsavzgxbbcfusqkozwpngsyjqlmjsytrmd UTF8

    盐A009C1A485912C6AE630D3E744240B04 HEX

    散列函数SHA1

    密钥大小128

    迭代1000次

    我一直在使用https://gchq.github.io/CyberChef,可以得到输出17EB4014C8C461C300E9B61518B9A18B它与维基百科示例中派生的关键字节相匹配。

    我一直在和你一起工作https://mkyong.com/java/java-aes-encryption-and-decryption/其中有一个名为getAESKeyFromPassword的方法,如下所示:

    // Password derived AES 256 bits secret key
    public static SecretKey getAESKeyFromPassword(char[] password, byte[] salt)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
    
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        // iterationCount = 65536
        // keyLength = 256
        KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
        SecretKey secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
        return secret;
    
    }
    

    我想进行同样的“调查”,就像我对Wikipedia页面、SHA1和CyberChef所做的那样,但使用SHA256(替换Java代码中的值,以匹配示例中的salt、密码和迭代)。

    这就是我困惑的开始:

    密码plnlrtfpijpuhqylxbgqiyipieyxvfsavzgxbbcfusqkozwpngsyjqlmjsytrmd UTF8

    盐A009C1A485912C6AE630D3E744240B04 HEX

    散列函数SHA256

    密钥大小128

    我希望派生密钥在CyberChef中与https://mkyong.com/java/java-aes-encryption-and-decryption/实例

    不是。

    我不禁认为我的理解有缺陷。

    有人可以提供一个简单的(经过处理的)带有SHA256的PBKDF2示例,这样我就可以理解发生了什么。如果派生密钥不是相同的(与SHA1示例一样,请解释原因)。

    是Java SecretKey:

    SecretKey secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
    

    与派生密钥相同?

    似乎缺少易于理解的例子。

    谢谢

    英里。

  • 共有1个答案

    曹伟泽
    2023-03-14

    感谢大家的投入,特别是Topaco:)

    我要回答我的问题,因为我花了一些时间在MCVE上工作,并设法获得了与网络厨师相同的密钥。

    密钥值为:28869b5f31ae29236f164c5cb33e2e3bb46f483867a15f8e7208e1836070f64a

    以下是cyberChef的输出:

    下面是Java代码,以及运行它的输出:

    package crypto;
    
    import javax.crypto.Cipher;
    import javax.crypto.SecretKey;
    import javax.crypto.SecretKeyFactory;
    import javax.crypto.spec.GCMParameterSpec;
    import javax.crypto.spec.PBEKeySpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.nio.ByteBuffer;
    import java.nio.charset.Charset;
    import java.nio.charset.StandardCharsets;
    import java.security.NoSuchAlgorithmException;
    import java.security.SecureRandom;
    import java.security.spec.InvalidKeySpecException;
    import java.security.spec.KeySpec;
    import java.util.Base64;
    
    public class EncryptDecryptAesGcmPassword {
    
        private static final String ENCRYPT_ALGO = "AES/GCM/NoPadding";
    
        private static final int TAG_LENGTH_BIT = 128; // must be one of {128, 120,     112, 104, 96}
        private static final int IV_LENGTH_BYTE = 12;
        private static final int SALT_LENGTH_BYTE = 16;
        public static final int ITERATION_COUNT = 1000;
        public static final int KEY_LENGTH = 256;
    
        private static final Charset UTF_8 = StandardCharsets.UTF_8;
    
        // return a base64 encoded AES encrypted text
        public static String encrypt(byte[] salt, byte[] pText, String password) throws Exception {
            // GCM recommended 12 bytes iv?
            byte[] iv = getRandomNonce(IV_LENGTH_BYTE);
    
            // secret key from password
            SecretKey aesKeyFromPassword = getAESKeyFromPassword(password.toCharArray(), salt);
    
            Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
    
            // ASE-GCM needs GCMParameterSpec
            cipher.init(Cipher.ENCRYPT_MODE, aesKeyFromPassword, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
    
            byte[] cipherText = cipher.doFinal(pText);
    
            // prefix IV and Salt to cipher text
            byte[] cipherTextWithIvSalt = ByteBuffer.allocate(iv.length + salt.length + cipherText.length)
                .put(iv)
                .put(salt)
                .put(cipherText)
                .array();
    
            // string representation, base64, send this string to other for decryption.
            return Base64.getEncoder().encodeToString(cipherTextWithIvSalt);
    
        }
    
        // we need the same password, salt and iv to decrypt it
        private static String decrypt(String cText, String password) throws Exception {
            byte[] decode = Base64.getDecoder().decode(cText.getBytes(UTF_8));
    
            // get back the iv and salt from the cipher text
            ByteBuffer bb = ByteBuffer.wrap(decode);
    
            byte[] iv = new byte[IV_LENGTH_BYTE];
            bb.get(iv);
    
            byte[] salt = new byte[SALT_LENGTH_BYTE];
            bb.get(salt);
    
            byte[] cipherText = new byte[bb.remaining()];
            bb.get(cipherText);
    
            // get back the aes key from the same password and salt
            SecretKey aesKeyFromPassword = getAESKeyFromPassword(password.toCharArray(), salt);
    
            Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
    
            cipher.init(Cipher.DECRYPT_MODE, aesKeyFromPassword, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
    
            byte[] plainText = cipher.doFinal(cipherText);
    
            return new String(plainText, UTF_8);
    
        }
    
    
        public static byte hexToByte(String hexString) {
            int firstDigit = toDigit(hexString.charAt(0));
            int secondDigit = toDigit(hexString.charAt(1));
            return (byte) ((firstDigit << 4) + secondDigit);
        }
    
        public static byte[] decodeHexString(String hexString) {
            if (hexString.length() % 2 == 1) {
                throw new IllegalArgumentException(
                    "Invalid hexadecimal String supplied.");
            }
    
            byte[] bytes = new byte[hexString.length() / 2];
            for (int i = 0; i < hexString.length(); i += 2) {
                bytes[i / 2] = hexToByte(hexString.substring(i, i + 2));
            }  
            return bytes;
        }
    
        private static int toDigit(char hexChar) {
            int digit = Character.digit(hexChar, 16);
            if (digit == -1) {
                throw new IllegalArgumentException(
                    "Invalid Hexadecimal Character: "+ hexChar);
            }
            return digit;
        }
    
        // Random byte[] with length numBytes
        public static byte[] getRandomNonce(int numBytes) {
            byte[] nonce = new byte[numBytes];
            new SecureRandom().nextBytes(nonce);
            return nonce;
        }
    
        // Password derived AES 256 bits secret key
        public static SecretKey getAESKeyFromPassword(char[] password, byte[] salt)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
    
            SecretKeyFactory factory =    SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
            // iterationCount = 1000
            // keyLength = 256
            KeySpec spec = new PBEKeySpec(password, salt, ITERATION_COUNT,
                KEY_LENGTH);
            SecretKey secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
    
            String encodedKey = hex(secret.getEncoded());
    
            // print SecretKey as hex
            System.out.println("SecretKey: " + encodedKey);
    
            return secret;
    
        }
    
        // hex representation
        public static String hex(byte[] bytes) {
            StringBuilder result = new StringBuilder();
            for (byte b : bytes) {
                result.append(String.format("%02x", b));
            }
            return result.toString();
        }
    
    
    
        public static void main(String[] args) throws Exception {
            String OUTPUT_FORMAT = "%-30s:%s";
            String PASSWORD = "plnlrtfpijpuhqylxbgqiiyipieyxvfsavzgxbbcfusqkozwpngsyejqlmjsytrmd";
    
            // plain text
            String pText = "AES-GSM Password-Bases encryption!";
    
            // convert hex string to byte[]
            byte[] salt = decodeHexString("A009C1A485912C6AE630D3E744240B04");
    
    
            String encryptedTextBase64 = EncryptDecryptAesGcmPassword.encrypt(salt, pText.getBytes(UTF_8), PASSWORD);
    
            System.out.println("\n------ AES GCM Password-based Encryption ------");
            System.out.println(String.format(OUTPUT_FORMAT, "Input (plain text)", pText));
            System.out.println(String.format(OUTPUT_FORMAT, "Encrypted (base64) ", encryptedTextBase64));
    
            System.out.println("\n------ AES GCM Password-based Decryption ------");
            System.out.println(String.format(OUTPUT_FORMAT, "Input (base64)", encryptedTextBase64));
    
            String decryptedText = EncryptDecryptAesGcmPassword.decrypt(encryptedTextBase64, PASSWORD);
            System.out.println(String.format(OUTPUT_FORMAT, "Decrypted (plain text)", decryptedText));
        }
    }
    

    运行此代码,生成以下内容:

    SecretKey: 28869b5f31ae29236f164c5cb33e2e3bb46f483867a15f8e7208e1836070f64a
    
    ------ AES GCM Password-based Encryption ------
    Input (plain text)            :AES-GSM Password-Bases encryption!
    Encrypted (base64)            :/PuTLBTKVWgJB2iMoAnBpIWRLGrmMNPnRCQLBABOkwNeY8BrrdtoRNVFqZ+xmUjvF2PET6Ne2+PAp34QLCUFjQodTMdmzaNAfzcLWOf4
    
    ------ AES GCM Password-based Decryption ------
    Input (base64)               :/PuTLBTKVWgJB2iMoAnBpIWRLGrmMNPnRCQLBABOkwNeY8BrrdtoRNVFqZ+xmUjvF2PET6Ne2+PAp34QLCUFjQodTMdmzaNAfzcLWOf4
    SecretKey: 28869b5f31ae29236f164c5cb33e2e3bb46f483867a15f8e7208e1836070f64a
    Decrypted (plain text)        :AES-GSM Password-Bases encryption!
    

    谢谢

    英里。

     类似资料:
    • 问题内容: 有没有一种方法可以解密Java中的密码。Java将算法实现为。我得到了创建密码哈希的代码。我在下面提到了哈希技术的链接: http://howtodoinjava.com/security/how-to-generate-secure-password-hash- md5-sha-pbkdf2-bcrypt-examples/ 我的要求是以加密格式存储第三方FTP服务器密码,并在需要登

    • 我将AES与salt和IV一起用于加密和解密一个唯一的ID,但它给出了javax。加密。解密时出现BadPaddingException。 每次解密数据时给出的完整错误堆栈跟踪 加密方法- 解密方法 我是JCA的新手。

    • 我已经看到了其他一些关于创建加密的初始化向量(IV)的问题,似乎使用随机值是一种选择。然而,我需要生成用于解密的IV,所以我必须使用基于一些salt的数据加密的相同的数据。 node.js加密函数createDecipher表示: crypto.createdecipher()的实现使用OpenSSL函数EVP_BytesToKey派生密钥,摘要算法设置为MD5,一次迭代,没有salt。 好的,听

    • 问题内容: 我正在尝试在Python中加密某些内容,并在nodejs应用程序中对其进行解密。 我正在努力使这两个AES实现一起工作。这是我的位置。 在节点中: 产生输出: 在python中 这产生输出 显然,它们非常接近,但是node似乎在用某些内容填充输出。有什么想法可以使两者互操作吗? 问题答案: 好的,我已经弄清楚了,节点使用OpenSSL,后者使用PKCS5进行填充。PyCrypto不处理

    • 本文向大家介绍Python和Java进行DES加密和解密的实例,包括了Python和Java进行DES加密和解密的实例的使用技巧和注意事项,需要的朋友参考一下 DES 为 Data Encryption Standard (数据加密标准)的缩写,是一种常见的对称加密算法。有关对称加密与非对称加密的特点及其应用场景,本文就不描述了,读者可以自行 google 。本文说明如何使用 Java 和 Pyt

    • null 下面是我当前的代码: 以下是我的结果: 原文: 正如您所看到的,加密中缺少几个字符,这也影响了解密。缺的是2号线的v和3号线的v 你知道为什么吗?