加密/解密java到PHP

Posted

技术标签:

【中文标题】加密/解密java到PHP【英文标题】:Encrypt/Decrypt java to PHP 【发布时间】:2021-01-14 14:42:04 【问题描述】:

我试图了解加密/解密是如何工作的,所以我有这个加密 JAVA 函数以在 php 中转换(也是解密方法):

// JAVA
public byte[] encrypt( String text, String salt, String key, ecrypt enc, eprovider provider, ebit bit ) 
    byte[] iv =  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;
    IvParameterSpec ivspec = new IvParameterSpec( iv );
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
    KeySpec spec = new PBEKeySpec( key.toCharArray(), salt.getBytes(), 65536, bit.getValue() );
    SecretKey tmp = factory.generateSecret( spec );
    SecretKeySpec secretkey = new SecretKeySpec( tmp.getEncoded(), enc.getValue() );
    Cipher cipher = Cipher.getInstance( provider.getValue() );
    cipher.init( Cipher.ENCRYPT_MODE, secretkey, ivspec );
    return cipher.doFinal( text.getBytes( "UTF-8" ) );

我写了这样的东西来用 PHP 解密(我想我收到了一个由上面的 JAVA 加密的 base64 字符串):

// PHP
$IvLength = 16;
$password = '<PASSWD>';
$salt = "<SALT>";
$keyLength = 65536;
$iterations = 256;
$hash = hash_pbkdf2("sha256", $password, $salt, $iterations, $keyLength);
$ciphertext = base64_decode('<MY-BASE-64-CRYPT-STRING>');
$iv = substr($ciphertext, 0, $IvLength);
$ciphertext = substr($ciphertext, $IvLength);
$decryptedtext = openssl_decrypt($ciphertext, "aes-256-cbc", $hash, OPENSSL_RAW_DATA, $iv);

但我仍然缺少一些东西,因为 'openssl_decrypt' 返回 false。

我错过了什么?

【问题讨论】:

在 Java 代码中,IV 和密文没有连接(此外,静态 IV 是不安全的)并且密文不是 Base64 编码的(这可能发生在未发布的代码部分)。还缺少ecrypteproviderebit 的定义。在 PHP 代码中,密文经过 Base64 解码,然后将 IV 和实际密文分开(即两者都应连接)。请发布 Java 代码的示例数据(明文、盐、密码、enc.getValue()provider.getValue()bit.getValue()、密文)。 【参考方案1】:

正如您尝试学习的那样,我提供了一个 Java 和 PHP 完整解决方案,其工作方式相同并且结果是可交换的,因此可以使用 Java 加密并使用 PHP 解密,反之亦然。两个程序都使用 PBKDF2 从密码短语中派生加密密钥,加密后,将盐、iv 和密文连接起来。为了解密,完整的字符串被分成这些部分。

这是输出:

AES CBC 256 String encryption with PBKDF2 derived key
plaintext:  The quick brown fox jumps over the lazy dog
encryptionKey (Base64): sI5RagrofCpzDe1ucLI7cHb3GILAMsySr0A8CD04o0I=
* * * Encryption * * *
ciphertext (Base64): Vq5mhwLqgl2LoC/QNRKWA/5YpjrlZ1f3504NzpwEhUA=:QiEdP5gfXzNQwWcxNk4p1w==:Rwy3LC7SbeyDUREiitjjdtioQWkctU9H9OEvzv1ctWymzVh3A0SFQN4Ek/Ku4nVp
output is (Base64) salt : (Base64) iv : (Base64) ciphertext
* * * Decryption * * *
AES CBC 256 String decryption with PBKDF2 derived key
ciphertext (Base64): Vq5mhwLqgl2LoC/QNRKWA/5YpjrlZ1f3504NzpwEhUA=:QiEdP5gfXzNQwWcxNk4p1w==:Rwy3LC7SbeyDUREiitjjdtioQWkctU9H9OEvzv1ctWymzVh3A0SFQN4Ek/Ku4nVp
input is (Base64) salt : (Base64) iv : (Base64) ciphertext
plaintext:  The quick brown fox jumps over the lazy dog

安全警告:以下代码没有任何异常处理,仅用于教育目的。

Java:

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;

public class AesCbc256Pbkdf2StringEncryption_Full 
    public static void main(String[] args) throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException, InvalidKeySpecException 
        System.out.println("AES CBC 256 String encryption with PBKDF2 derived key");

        String plaintext = "The quick brown fox jumps over the lazy dog";
        System.out.println("plaintext:  " + plaintext);
        char[] password = "secret password".toCharArray();

        // encryption
        System.out.println("\n* * * Encryption * * *");
        String ciphertextBase64 = aesCbcPbkdf2EncryptToBase64(password, plaintext);
        System.out.println("ciphertext (Base64): " + ciphertextBase64);
        System.out.println("output is (Base64) salt : (Base64) iv : (Base64) ciphertext");

        // decryption
        System.out.println("\n* * * Decryption * * *");
        System.out.println("AES CBC 256 String decryption with PBKDF2 derived key");
        String ciphertextDecryptionBase64 = ciphertextBase64;
        System.out.println("ciphertext (Base64): " + ciphertextDecryptionBase64);
        System.out.println("input is (Base64) salt : (Base64) iv : (Base64) ciphertext");
        String decryptedtext = aesCbcPbkdf2DecryptFromBase64(password, ciphertextDecryptionBase64);
        System.out.println("plaintext:  " + decryptedtext);
    

    private static byte[] generateSalt32Byte() 
        SecureRandom secureRandom = new SecureRandom();
        byte[] salt = new byte[32];
        secureRandom.nextBytes(salt);
        return salt;
    

    private static byte[] generateRandomInitvector() 
        SecureRandom secureRandom = new SecureRandom();
        byte[] iv = new byte[16];
        secureRandom.nextBytes(iv);
        return iv;
    

    private static String aesCbcPbkdf2EncryptToBase64(char[] password, String data) throws NoSuchPaddingException, NoSuchAlgorithmException,
            InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeySpecException 
        int PBKDF2_ITERATIONS = 15000;
        byte[] salt = generateSalt32Byte();
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec keySpec = new PBEKeySpec(password, salt, PBKDF2_ITERATIONS, 32 * 8);
        SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyFactory.generateSecret(keySpec).getEncoded(), "AES");
        byte[] iv = generateRandomInitvector();
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        String ciphertextBase64 = Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)));
        String saltBase64 = Base64.getEncoder().encodeToString(salt);
        String ivBase64 = Base64.getEncoder().encodeToString(iv);
        return saltBase64 + ":" + ivBase64 + ":" + ciphertextBase64;
    

    private static String aesCbcPbkdf2DecryptFromBase64(char[] password, String data) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeySpecException 
        String[] parts = data.split(":", 0);
        byte[] salt = base64Decoding(parts[0]);
        byte[] iv = base64Decoding(parts[1]);
        byte[] encryptedData = base64Decoding(parts[2]);
        int PBKDF2_ITERATIONS = 15000;
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec keySpec = new PBEKeySpec(password, salt, PBKDF2_ITERATIONS, 32 * 8);
        SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyFactory.generateSecret(keySpec).getEncoded(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        return new String(cipher.doFinal(encryptedData));
    

    private static String base64Encoding(byte[] input) 
        return Base64.getEncoder().encodeToString(input);
    

    private static byte[] base64Decoding(String input) 
        return Base64.getDecoder().decode(input);
    

PHP:

<?php
function aesCbcPbkdf2EncryptToBase64($password, $data)

    $PBKDF2_ITERATIONS = 15000;
    $salt = generateSalt32Byte();
    $key = hash_pbkdf2("sha256", $password, $salt, $PBKDF2_ITERATIONS, 32, $raw_output = true);
    $iv = generateRandomInitvector();
    $ciphertext = openssl_encrypt($data, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
    return base64_encode($salt) . ':' . base64_encode($iv) . ':' . base64_encode($ciphertext);


function generateSalt32Byte()

    return openssl_random_pseudo_bytes(32, $crypto_strong);


function aesCbcPbkdf2DecryptFromBase64($password, $data)

    $PBKDF2_ITERATIONS = 15000;
    list($salt, $iv, $encryptedData) = explode(':', $data, 3);
    $key = hash_pbkdf2("sha256", $password, base64_decode($salt), $PBKDF2_ITERATIONS, 32, $raw_output = true);
    return openssl_decrypt(base64_decode($encryptedData), 'aes-256-cbc', $key, OPENSSL_RAW_DATA, base64_decode($iv));


function generateRandomInitvector()

    return openssl_random_pseudo_bytes(16, $crypto_strong);


echo 'AES CBC 256 String encryption with PBKDF2 derived key' . PHP_EOL;

$plaintext = 'The quick brown fox jumps over the lazy dog';
echo 'plaintext: ' . $plaintext . PHP_EOL;
$password = "secret password";

// encryption
echo PHP_EOL . '* * * Encryption * * *' . PHP_EOL;
$ciphertextBase64 = aesCbcPbkdf2EncryptToBase64($password, $plaintext);
echo 'ciphertext: ' . $ciphertextBase64 . PHP_EOL;
echo 'output is (Base64) salt : (Base64) iv : (Base64) ciphertext' .PHP_EOL;

// decryption
echo PHP_EOL;
echo PHP_EOL . '* * * Decryption * * *' . PHP_EOL;
$ciphertextDecryptionBase64 = $ciphertextBase64;
echo 'ciphertextDecryption (Base64): ' . $ciphertextDecryptionBase64 . PHP_EOL;
echo 'input is (Base64) salt : (Base64) iv : (Base64) ciphertext' .PHP_EOL;
$decryptedtext = aesCbcPbkdf2DecryptFromBase64($password, $ciphertextDecryptionBase64);
echo 'plaintext: ' . $decryptedtext . PHP_EOL;
?>

【讨论】:

感谢您的发帖!您帮助我了解 JAVA 以字节为单位工作,但 PHP 不是!我认为这是公认的答案。

以上是关于加密/解密java到PHP的主要内容,如果未能解决你的问题,请参考以下文章

php与java通用AES加密解密算法

Java 中的 AES 128 加密 PHP 中的解密

JAVA如何解密用PHP加密的base64编码和RIJNDAEL 256的数据?

Java & PHP & Javascript 通用 RSA 加密 解密 (长字符串)

网站前端JS加密方法RAS加密可以PHP解密

解密(使用 PHP)Java 加密(PBEWithMD5AndDES)