PBKDF2WithHmacSHA512 Java 相当于 PHP 用于 AES 加密

PBKDF2WithHmacSHA512 Java equivalent in PHP for AES encryption

我正在尝试在 PHP 中将 PBKDF2WithHmacSHA512 与 OpenSSL 一起使用。 但是我无法创建与 Java 创建的加密字符串相同的字符串。我要加密的数据是 Atom Insta Pay。使用下面提到的 java 代码我得到 vneGN6vnDJ3Z4wj4u3+z1g== 但是在 PHP 我得到 JQubY9xCf+g9yASdNkq7cQ== 这是完全不同的。

// Java code
    import java.util.logging.Logger;
    import javax.crypto.Cipher;
    import javax.crypto.SecretKey;
    import javax.crypto.SecretKeyFactory;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.PBEKeySpec;
    import javax.crypto.spec.SecretKeySpec;
    
    public class myEncryption {
        static Logger log = Logger.getLogger(myEncryption.class.getName());
        private static int pswdIterations = 65536;
        private static int keySize = 256;
        private static final byte[] ivBytes = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
    
        public static String encrypt(String plainText, String key) {
            try {
                byte[] saltBytes = key.getBytes("UTF-8");
                SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
                PBEKeySpec spec = new PBEKeySpec(key.toCharArray(), saltBytes, pswdIterations, keySize);
    
                SecretKey secretKey = factory.generateSecret(spec);     
                SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");  
    
                IvParameterSpec localIvParameterSpec = new IvParameterSpec(ivBytes);
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                cipher.init(1, secret, localIvParameterSpec);
    
                byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
    
                return byteToHex(encryptedTextBytes);
    
             }catch (Exception e) {
                log.info("Exception while encrypting data:" + e.toString());
            }
    
            return null;
        }
    
        public static String decrypt(String encryptedText, String key) {
    
            try {
    
                byte[] saltBytes = key.getBytes("UTF-8");
                byte[] encryptedTextBytes = hex2ByteArray(encryptedText);
    
                SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
                PBEKeySpec spec = new PBEKeySpec(key.toCharArray(), saltBytes, pswdIterations, keySize);
    
                SecretKey secretKey = factory.generateSecret(spec);
                SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
    
                IvParameterSpec localIvParameterSpec = new IvParameterSpec(ivBytes);
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                cipher.init(2, secret, localIvParameterSpec);
    
                byte[] decryptedTextBytes = (byte[]) null;
                decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
    
                return new String(decryptedTextBytes);
    
            }catch (Exception e) {
                log.info("Exception while decrypting data:" + e.toString());
            }
    
            return null;
        }
    
        private static String byteToHex(byte[] byData) {
            StringBuffer sb = new StringBuffer(byData.length * 2);
    
            for (int i = 0; i < byData.length; ++i) {
                int v = byData[i] & 0xFF;
                if (v < 16)
                    sb.append('0');
                sb.append(Integer.toHexString(v));
            }
    
            return sb.toString().toUpperCase();
        }
    
        private static byte[] hex2ByteArray(String sHexData) {
            byte[] rawData = new byte[sHexData.length() / 2];
            for (int i = 0; i < rawData.length; ++i) {
                int index = i * 2;
                int v = Integer.parseInt(sHexData.substring(index, index + 2), 16);
                rawData[i] = (byte) v;
            }
    
            return rawData;
        }
    }

//PHP code
$method = "AES-256-CBC";
$salt = 'A4476C2062FFA58980DC8F79EB6A799E';
$key = 'A4476C2062FFA58980DC8F79EB6A799E';
$data = 'Demo String';

//Converting Array to bytes
$iv = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
$chars = array_map("chr", $iv);
$IVbytes = join($chars);

$salt1 = mb_convert_encoding($salt, "UTF-8"); //Encoding to UTF-8
$key1 = mb_convert_encoding($key, "UTF-8"); //Encoding to UTF-8

//SecretKeyFactory Instance of PBKDF2WithHmacSHA512 Java Equivalent
$hash = openssl_pbkdf2($key1,$salt1,'256','65536', 'sha512'); 

$encrypted = openssl_encrypt($data, $method, $hash, OPENSSL_RAW_DATA, $IVbytes);

echo base64_encode($encrypted);

PHP-解决方案比 Java-方更“简单”,并且可以将密钥和盐作为直接输入使用(无需转换)。

因为你喜欢比较 base64 编码的密文示例代码 returns base64 编码的密文而不是十六进制字符串 因为 Java-prog 正在放弃。

这是具有相同结果的输出:

expected:  JQubY9xCf+g9yASdNkq7cQ==
encrypted: JQubY9xCf+g9yASdNkq7cQ==

安全警告:您的代码使用了静态盐和 iv,这使得加密不安全(我希望这只是为了演示)。

这是代码:

<?php
//PHP code
$method = "AES-256-CBC";
$salt = 'A4476C2062FFA58980DC8F79EB6A799E';
$key =  'A4476C2062FFA58980DC8F79EB6A799E';
$data = 'Atom Insta Pay';

//Converting Array to bytes
$iv = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
$chars = array_map("chr", $iv);
$IVbytes = join($chars);

$hash = openssl_pbkdf2($key,$salt,32,65536, 'sha512');
$encrypted = openssl_encrypt($data, $method, $hash, OPENSSL_RAW_DATA, $IVbytes);
echo 'expected:  JQubY9xCf+g9yASdNkq7cQ==' . PHP_EOL;
echo 'encrypted: ' . base64_encode($encrypted);
?>