PHP & Java 之间的加密
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/44234719/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Encryption Between PHP & Java
提问by Arnab C.
I was looking to encrypt data between a PHP server and a Java Client. Individually the code works fine and I would like to stick with OpenSSL on the PHP server.
我希望在 PHP 服务器和 Java 客户端之间加密数据。单独的代码工作正常,我想在 PHP 服务器上坚持使用 OpenSSL。
Do any of you see anything that I am missing here as I get an error when trying to decode the PHP encrypted string:
当我尝试解码 PHP 加密字符串时出现错误时,你们有没有看到我在这里遗漏的任何内容:
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 output:
PHP输出:
Before encryption: Hello World!!!
Before Base64: SGVsbG8gV29ybGQhISE=
Encrypted Len: 44
Encrypted b64: U21yMVRGQTdROVc3TWJ1Wm1HUTBhMmZmenlIN2tvdWQ5SHA5ekVxUmp5az0=
Encrypted: U21yMVRGQTdROVc3TWJ1Wm1HUTBhMmZmenlIN2tvdWQ5SHA5ekVxUmp5az0=:ZmVkY2JhOTg3NjU0MzIxMA==
Decrypted: Hello World!!!
加密前:Hello World!!!
Base64 之前:SGVsbG8gV29ybGQhISE=
加密长度:44
加密的 b64:U21yMVRGQTdROVc3TWJ1Wm1HUTBhMmZmenlIN2tvdWQ5SHA5ekVxUmp5az0=
加密:U21yMVRGQTdROVc3TWJ1Wm1HUTBhMmZmenlIN2tvdWQ5SHA5ekVxUmp5az0=:ZmVkY2JhOTg3NjU0MzIxMA==
解密:你好世界!!!
Java Code:
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 Output:
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)
回答by Arnab C.
A working version can be found - https://github.com/chaudhuri-ab/CrossPlatformCiphers
可以找到工作版本 - https://github.com/chaudhuri-ab/CrossPlatformCiphers
Some things to keep in mind is the that if you do not specify OPENSSL_RAW_DATA in PHP the data will be encrypted as base64. That was throwing me off.
要记住的一些事情是,如果您没有在 PHP 中指定 OPENSSL_RAW_DATA,数据将被加密为 base64。那让我失望了。
PHP:
PHP:
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;
}
}
回答by theapache64
I can't figure out why your method fails. By the way, Here's how i did it,
我不明白为什么你的方法失败了。顺便说一句,这是我如何做到的,
Java
爪哇
import com.sun.org.apache.xml.internal.security.utils.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
public class MyClass {
public static void main(String[] args) {
String data = "Arnab C";
final String enc = DarKnight.getEncrypted(data);
System.out.println("Encrypted : " + enc);
System.out.println("Decrypted : " + DarKnight.getDecrypted(enc));
}
static class DarKnight {
private static final String ALGORITHM = "AES";
private static final byte[] SALT = "tHeApAcHe6410111".getBytes();// THE KEY MUST BE SAME
private static final String X = DarKnight.class.getSimpleName();
static String getEncrypted(String plainText) {
if (plainText == null) {
return null;
}
Key salt = getSalt();
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, salt);
byte[] encodedValue = cipher.doFinal(plainText.getBytes());
return Base64.encode(encodedValue);
} catch (Exception e) {
e.printStackTrace();
}
throw new IllegalArgumentException("Failed to encrypt data");
}
public static String getDecrypted(String encodedText) {
if (encodedText == null) {
return null;
}
Key salt = getSalt();
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, salt);
byte[] decodedValue = Base64.decode(encodedText);
byte[] decValue = cipher.doFinal(decodedValue);
return new String(decValue);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
static Key getSalt() {
return new SecretKeySpec(SALT, ALGORITHM);
}
}
}
PHP
PHP
<?php
$key = "tHeApAcHe6410111";
function encrypt($text,$key){
$block = mcrypt_get_block_size('rijndael_128', 'ecb');
$pad = $block - (strlen($text) % $block);
$text .= str_repeat(chr($pad), $pad);
return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_ECB));
}
function decrypt($str, $key){
$str = base64_decode($str);
$str = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_ECB);
$block = mcrypt_get_block_size('rijndael_128', 'ecb');
$pad = ord($str[($len = strlen($str)) - 1]);
$len = strlen($str);
$pad = ord($str[$len-1]);
return substr($str, 0, strlen($str) - $pad);
}
$enc = encrypt("Arnab C",$GLOBALS['key']);
echo "Encrypted : ".$enc."</br>";
$dec = decrypt($enc,$GLOBALS['key']);
echo "Decrypted : ".$dec;
Java Output
Java 输出
Encrypted : PJG1Uu6SjJuuVGf7ApuHAw==
加密:PJG1Uu6SjJuuVGf7ApuHAw==
Decrypted : Arnab C
解密:Arnab C
PHP Output
PHP输出
Encrypted : PJG1Uu6SjJuuVGf7ApuHAw==
加密:PJG1Uu6SjJuuVGf7ApuHAw==
Decrypted : Arnab C
解密:Arnab C
回答by petrnohejl
I used the solution from theapache64, but in PHP 7.2 it stopped working since Mcrypt has been deprecated and later removed. So I changed the code and it works:
我使用了 theapache64 中的解决方案,但在 PHP 7.2 中它停止工作,因为 Mcrypt 已被弃用并随后被删除。所以我改变了代码,它的工作原理:
function encrypt($data, $key) {
return base64_encode(openssl_encrypt($data, "aes-128-ecb", $key, OPENSSL_RAW_DATA));
}
function decrypt($data, $key) {
return openssl_decrypt(base64_decode($data), "aes-128-ecb", $key, OPENSSL_RAW_DATA);
}
回答by Kairos
In Java 8, you cannot use
在 Java 8 中,您不能使用
import com.sun.org.apache.xml.internal.security.utils.Base64;
Instead, you can use
相反,您可以使用
import android.util.Base64;
Then you also need to change the Base64.decode and Base64.encode lines.
然后您还需要更改 Base64.decode 和 Base64.encode 行。
The complete code will be as follows (the comment from petrnohejl, about MCRYPT that has been deprecated in PHP7, taken into account):
完整的代码如下(来自petrnohejl的评论,关于在PHP7中已被弃用的MCRYPT,考虑在内):
Java:
爪哇:
import android.util.Base64;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class MyClass {
public static void main(String[] args) {
String data = "Arnab C";
final String enc = DarKnight.getEncrypted(data);
System.out.println("Encrypted : " + enc);
System.out.println("Decrypted : " + DarKnight.getDecrypted(enc));
}
static class DarKnight {
private static final String ALGORITHM = "AES";
private static final byte[] SALT = "tHeApAcHe6410111".getBytes();// THE KEY MUST BE SAME
private static final String X = DarKnight.class.getSimpleName();
static String getEncrypted(String plainText) {
if (plainText == null) {
return null;
}
Key salt = getSalt();
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, salt);
byte[] encodedValue = cipher.doFinal(plainText.getBytes());
return Base64.encodeToString(encodedValue,Base64.DEFAULT);
} catch (Exception e) {
e.printStackTrace();
}
throw new IllegalArgumentException("Failed to encrypt data");
}
public static String getDecrypted(String encodedText) {
if (encodedText == null) {
return null;
}
Key salt = getSalt();
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, salt);
byte[] decodedValue = Base64.decode(encodedText, Base64.DEFAULT);
byte[] decValue = cipher.doFinal(decodedValue);
return new String(decValue);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
static Key getSalt() {
return new SecretKeySpec(SALT, ALGORITHM);
}
}
}
PHP:
PHP:
<?php
$key = "tHeApAcHe6410111";
function encrypt($data, $key) {
return base64_encode(openssl_encrypt($data, "aes-128-ecb", $key, OPENSSL_RAW_DATA));
}
function decrypt($data, $key) {
return openssl_decrypt(base64_decode($data), "aes-128-ecb", $key, OPENSSL_RAW_DATA);
}
$enc = encrypt("Arnab C",$GLOBALS['key']);
echo "Encrypted : ".$enc."</br>";
$dec = decrypt($enc,$GLOBALS['key']);
echo "Decrypted : ".$dec;
?>
Don't give me credits, I am just the messenger, who combined a few posts.
不要给我信用,我只是信使,合并了几个帖子。