我一直在寻找在PHP服务器和Java客户端之间加密数据的方法。单独地,代码工作正常,我想在PHP服务器上坚持使用OpenSSL。
在尝试解码PHP加密字符串时遇到错误时,您是否看到了我所缺少的任何内容:
PHP:
<?php $iv = 'fedcba9876543210'; #Same as in JAVA $key = '0123456789abcdef'; #Same as in JAVA $ciphers = openssl_get_cipher_methods(FALSE); $ciphers_and_aliases = openssl_get_cipher_methods(true); $cipher_aliases = array_diff($ciphers_and_aliases, $ciphers); print_r($ciphers); //print_r($cipher_aliases); // DEFINE our cipher define('AES_CBC', 'aes-128-cbc'); // Generate a 256-bit encryption key // This should be stored somewhere instead of recreating it each time $encryption_key = "test_key"; // Generate an initialization vector // This *MUST* be available for decryption as well //$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length(AES_CBC)); // Create some data to encrypt $data = "Hello World!!!"; $data_b64= base64_encode($data); echo "Before encryption: $data<br><br>Before Base64: $data_b64<br><br>"; // Encrypt $data using aes-256-cbc cipher with the given encryption key and // our initialization vector. The 0 gives us the default options, but can // be changed to OPENSSL_RAW_DATA or OPENSSL_ZERO_PADDING $encrypted = openssl_encrypt($data_b64, AES_CBC, $encryption_key, 0, $iv); $len = strlen($encrypted); echo "Encrypted Len: $len <br><br>"; $encrypted64 = base64_encode($encrypted); echo "Encrypted b64: $encrypted64<br><br>"; // If we lose the $iv variable, we can't decrypt this, so: // - $encrypted is already base64-encoded from openssl_encrypt // - Append a separator that we know won't exist in base64, ":" // - And then append a base64-encoded $iv $encrypted = $encrypted64 . ':' . base64_encode($iv); echo "Encrypted: $encrypted<br><br>"; // To decrypt, separate the encrypted data from the initialization vector ($iv). $parts = explode(':', $encrypted); // $parts[0] = encrypted data // $parts[1] = base-64 encoded initialization vector // Don't forget to base64-decode the $iv before feeding it back to //openssl_decrypt $decrypted64 = openssl_decrypt(base64_decode($parts[0]), AES_CBC, $encryption_key, 0, base64_decode($parts[1])); $decrypted = base64_decode($decrypted64); echo "Decrypted: $decrypted\n"; ?>
PHP输出:
加密之前:Hello World !!! 在Base64之前:SGVsbG8gV29ybGQhISE = 加密镜头:44 加密的b64:U21yMVRGQTdROVc3TWJ1Wm1HUTBhMmZmenlIN2tvdWQ5SHA5ekVxUmp5az0 = 加密:U21yMVRGQTdROVc3TWJ1Wm1HUTBhMmZmenlIN2tvdWQ5SHA5ekVxUmp5az0 =:ZmVkY2JhOTg3NjU0MzIxMA == 解密:世界你好!
加密之前:Hello World !!!
在Base64之前:SGVsbG8gV29ybGQhISE =
加密镜头:44
加密的b64:U21yMVRGQTdROVc3TWJ1Wm1HUTBhMmZmenlIN2tvdWQ5SHA5ekVxUmp5az0 =
加密:U21yMVRGQTdROVc3TWJ1Wm1HUTBhMmZmenlIN2tvdWQ5SHA5ekVxUmp5az0 =:ZmVkY2JhOTg3NjU0MzIxMA ==
解密:世界你好!
Java代码:
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; import java.security.*; public class Sandbox { public static String encrypt(String key, String initVector, String value) { try { IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8")); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); byte[] encrypted = cipher.doFinal(Base64.getEncoder().encode(value.getBytes())); System.out.println("encrypted string: " + Base64.getEncoder().encodeToString(encrypted)); return Base64.getEncoder().encodeToString(encrypted); } catch (Exception ex) { ex.printStackTrace(); } return null; } public static String decrypt(String key, String initVector, String encrypted) { try { IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8")); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); byte[] temp = Base64.getDecoder().decode(encrypted); System.out.println((new String(temp)).length()); byte[] original = cipher.doFinal(temp); original = Base64.getDecoder().decode(original); return new String(original); } catch (Exception ex) { ex.printStackTrace(); } return null; } public static void main(String[] args) { String key = "0123456789abcdef"; // 128 bit key String initVector = "fedcba9876543210"; // 16 bytes IV // for (Provider provider : Security.getProviders()) { // System.out.println(provider.getName()); // for (String key2 : provider.stringPropertyNames()) { // System.out.println("\t" + key2 + "\t" + provider.getProperty(key2)); // } // } System.out.println(decrypt(key, initVector, encrypt(key, initVector, "Hello World!!!"))); System.out.println(decrypt(key, initVector, "R090NDcvclAyY2E1cmxLWG9kSGlnUktHdEI5U05sRGxNdWF4NFFjUUV0OD0=")); } }
Java输出:
> 30 Hello World!!! 44 null javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:913) at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824) at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436) at javax.crypto.Cipher.doFinal(Cipher.java:2165) at Sandbox.decrypt(Sandbox.java:43) at Sandbox.main(Sandbox.java:67) BUILD SUCCESSFUL (total time: 1 second)
可以找到工作版本-https: //github.com/chaudhuri- ab/CrossPlatformCiphers
需要记住的一点是,如果未在PHP中指定OPENSSL_RAW_DATA,则数据将被加密为base64。那让我失望了。
class PHP_AES_Cipher { private static $OPENSSL_CIPHER_NAME = "aes-128-cbc"; //Name of OpenSSL Cipher private static $CIPHER_KEY_LEN = 16; //128 bits /** * Encrypt data using AES Cipher (CBC) with 128 bit key * * @param type $key - key to use should be 16 bytes long (128 bits) * @param type $iv - initialization vector * @param type $data - data to encrypt * @return encrypted data in base64 encoding with iv attached at end after a : */ static function encrypt($key, $iv, $data) { if (strlen($key) < PHP_AES_Cipher::$CIPHER_KEY_LEN) { $key = str_pad("$key", PHP_AES_Cipher::$CIPHER_KEY_LEN, "0"); //0 pad to len 16 } else if (strlen($key) > PHP_AES_Cipher::$CIPHER_KEY_LEN) { $key = substr($str, 0, PHP_AES_Cipher::$CIPHER_KEY_LEN); //truncate to 16 bytes } $encodedEncryptedData = base64_encode(openssl_encrypt($data, PHP_AES_Cipher::$OPENSSL_CIPHER_NAME, $key, OPENSSL_RAW_DATA, $iv)); $encodedIV = base64_encode($iv); $encryptedPayload = $encodedEncryptedData.":".$encodedIV; return $encryptedPayload; } /** * Decrypt data using AES Cipher (CBC) with 128 bit key * * @param type $key - key to use should be 16 bytes long (128 bits) * @param type $data - data to be decrypted in base64 encoding with iv attached at the end after a : * @return decrypted data */ static function decrypt($key, $data) { if (strlen($key) < PHP_AES_Cipher::$CIPHER_KEY_LEN) { $key = str_pad("$key", PHP_AES_Cipher::$CIPHER_KEY_LEN, "0"); //0 pad to len 16 } else if (strlen($key) > PHP_AES_Cipher::$CIPHER_KEY_LEN) { $key = substr($str, 0, PHP_AES_Cipher::$CIPHER_KEY_LEN); //truncate to 16 bytes } $parts = explode(':', $data); //Separate Encrypted data from iv. $decryptedData = openssl_decrypt(base64_decode($parts[0]), PHP_AES_Cipher::$OPENSSL_CIPHER_NAME, $key, OPENSSL_RAW_DATA, base64_decode($parts[1])); return $decryptedData; } }
Java:
package ciphers; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; public class Java_AES_Cipher { private static String CIPHER_NAME = "AES/CBC/PKCS5PADDING"; private static int CIPHER_KEY_LEN = 16; //128 bits /** * Encrypt data using AES Cipher (CBC) with 128 bit key * * * @param key - key to use should be 16 bytes long (128 bits) * @param iv - initialization vector * @param data - data to encrypt * @return encryptedData data in base64 encoding with iv attached at end after a : */ public static String encrypt(String key, String iv, String data) { try { if (key.length() < Java_AES_Cipher.CIPHER_KEY_LEN) { int numPad = Java_AES_Cipher.CIPHER_KEY_LEN - key.length(); for(int i = 0; i < numPad; i++){ key += "0"; //0 pad to len 16 bytes } } else if (key.length() > Java_AES_Cipher.CIPHER_KEY_LEN) { key = key.substring(0, CIPHER_KEY_LEN); //truncate to 16 bytes } IvParameterSpec initVector = new IvParameterSpec(iv.getBytes("UTF-8")); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance(Java_AES_Cipher.CIPHER_NAME); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, initVector); byte[] encryptedData = cipher.doFinal((data.getBytes())); String base64_EncryptedData = Base64.getEncoder().encodeToString(encryptedData); String base64_IV = Base64.getEncoder().encodeToString(iv.getBytes("UTF-8")); return base64_EncryptedData + ":" + base64_IV; } catch (Exception ex) { ex.printStackTrace(); } return null; } /** * Decrypt data using AES Cipher (CBC) with 128 bit key * * @param key - key to use should be 16 bytes long (128 bits) * @param data - encrypted data with iv at the end separate by : * @return decrypted data string */ public static String decrypt(String key, String data) { try { String[] parts = data.split(":"); IvParameterSpec iv = new IvParameterSpec(Base64.getDecoder().decode(parts[1])); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance(Java_AES_Cipher.CIPHER_NAME); cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); byte[] decodedEncryptedData = Base64.getDecoder().decode(parts[0]); byte[] original = cipher.doFinal(decodedEncryptedData); return new String(original); } catch (Exception ex) { ex.printStackTrace(); } return null; } }