如何从 Java 中的 openssl_encrypt (PHP) 中解密字符串?
How to decrypt string from openssl_encrypt (PHP) in Java?
我正在尝试在 Java 中复制以下 PHP class 来解密在 PHP 中使用 openssl_encrypt 加密的字符串。
class Encryption {
/* Encryption method */
protected $method = 'aes-128-ctr';
/* Encryption key */
private $key;
/* Property constructor */
public function __construct($key = FALSE, $method = FALSE){
/* Check if custom key provided */
if(!$key) {
/* Set default encryption if none provided */
$key = '1234567891234567';
}
/* Check for control characters in key */
if(ctype_print($key)) {
/* Convert ASCII keys to binary format */
$this->key = openssl_digest($key, 'SHA256', true);
} else {
$this->key = $key;
}
$this->key = $key;
/* Check for custom method */
if($method) {
/* If it is a valid openssl cipher method, use it */
if(in_array(strtolower($method), openssl_get_cipher_methods())) {
$this->method = $method;
/* If it is not, unrecognised method */
} else {
die(__METHOD__ . ": unrecognised cipher method: {$method}");
}
}
}
/* Get iv bytes length */
protected function iv_bytes(){
/* Get length of encryption cipher initialisation vector */
return openssl_cipher_iv_length($this->method);
}
/* Encryption method */
public function encrypt($data) {
/* Get initialisation vector binary */
$iv = openssl_random_pseudo_bytes($this->iv_bytes());
/* Return IV hex & encryption string */
return bin2hex($iv) . openssl_encrypt($data, $this->method, $this->key, OPENSSL_ZERO_PADDING, $iv);
}
/* Decrypt encrypted string */
public function decrypt($data){
/* Get IV string length */
$iv_strlen = 2 * $this->iv_bytes();
/* Parse out the encryption string and unpack the IV and encrypted string. $regs is passed by reference in preg_match() */
if(preg_match("/^(.{" . $iv_strlen . "})(.+)$/", $data, $regs)) {
list(, $iv, $crypted_string) = $regs;
print_R($regs);
/* If there are character representing a hex and the IV string length is divisible by 2 */
if(ctype_xdigit($iv) && (strlen($iv) % 2 == 0)) {
/* Decrypt the unpacked encrypted string */
return openssl_decrypt($crypted_string, $this->method, $this->key, OPENSSL_ZERO_PADDING, hex2bin($iv));
}
}
return FALSE; // failed to decrypt
}
}
这是 Java
中解密器的当前沙箱 class
public class PHPDecryptor {
private static String removeIVHash(String data) {
/* IV string length is 128 bit (16 bytes) * 2 */
int iv_strlen = 2 * 16;
/* Remove IV hash */
String encrypted_string = data.substring(iv_strlen);
return encrypted_string;
}
public static String decrypt(String data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String encrypted_string = removeIVHash(data);
/* Get bytes for string and key */
byte[] text_to_decrypt = encrypted_string.getBytes(StandardCharsets.UTF_8);
String key_string = "1234567891234567";
byte[] key = key_string.getBytes(StandardCharsets.UTF_8);
/* Get secret key and iv */
SecretKeySpec secret_key = new SecretKeySpec(key, "AES");
IvParameterSpec iv = new IvParameterSpec(new byte[16]);
/* Init cipher */
Cipher cipher = Cipher.getInstance("AES/CTR/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret_key, iv);
/* Decrypt and cast to string */
byte[] decrypted = cipher.doFinal(text_to_decrypt);
String result = new String(decrypted, StandardCharsets.UTF_8);
return result;
}
public static void main(String[] arg) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String text_to_decrypt = "203790144a345320d98fb773795d518e/ioQTApeVMV/4g=="; // Encrypted by PHP
System.out.println(decrypt(text_to_decrypt));
}
}
但是,我没有得到准确的解密 - 事实上 Java class 的 return 结果完全是胡说八道,包含无效的 UTF-8 字符。
这里有没有明显的错误?
您似乎没有对 IV 做任何事情,只是将其删除。
从您删除的 IV 创建一个 IVParameterSpec 实例。
public static IvParameterSpec getIv(String iv) {
return new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
}
如果你使用一些分隔符(我喜欢用“:”)来分隔 iv 和其余的密文,你可以更好地从字符串中检索 IV,这样你就不需要担心十六进制 IV 长度并像这样检索它:
private static String decodeIvString(String data) {
String[] parts = data.split(":");
String iv = decodeHex(parts[0]);
return iv;
}
显然,您必须在 php 的加密函数中添加定界符。
然后您可以将 IV 传回 getIv()
函数。
/* Encryption method */
public function encrypt($data) {
/* Get initialisation vector binary */
$iv = openssl_random_pseudo_bytes($this->iv_bytes());
/* Return IV hex & encryption string */
return bin2hex($iv) . ":" . openssl_encrypt($data, $this->method, $this->key, OPENSSL_ZERO_PADDING, $iv);
}
并且正如上面提到的评论,您应该将 PKCS5Padding
替换为 NoPadding
这是我用来解密的一段实用代码:
public static byte[] decryptSymmetric(Algorithm algorithm, Key key, IvParameterSpec iv, byte[] data) throws CryptoFailure {
try {
Security.addProvider(new BouncyCastleProvider());
Cipher aes = Cipher.getInstance(algorithm.getMode(), BouncyCastleProvider.PROVIDER_NAME);
aes.init(Cipher.DECRYPT_MODE, key, iv);
return aes.doFinal(data);
} catch (BadPaddingException | IllegalBlockSizeException e) {
throw new CryptoFailure(e);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
throw new CryptoError(e);
}
}
我正在尝试在 Java 中复制以下 PHP class 来解密在 PHP 中使用 openssl_encrypt 加密的字符串。
class Encryption {
/* Encryption method */
protected $method = 'aes-128-ctr';
/* Encryption key */
private $key;
/* Property constructor */
public function __construct($key = FALSE, $method = FALSE){
/* Check if custom key provided */
if(!$key) {
/* Set default encryption if none provided */
$key = '1234567891234567';
}
/* Check for control characters in key */
if(ctype_print($key)) {
/* Convert ASCII keys to binary format */
$this->key = openssl_digest($key, 'SHA256', true);
} else {
$this->key = $key;
}
$this->key = $key;
/* Check for custom method */
if($method) {
/* If it is a valid openssl cipher method, use it */
if(in_array(strtolower($method), openssl_get_cipher_methods())) {
$this->method = $method;
/* If it is not, unrecognised method */
} else {
die(__METHOD__ . ": unrecognised cipher method: {$method}");
}
}
}
/* Get iv bytes length */
protected function iv_bytes(){
/* Get length of encryption cipher initialisation vector */
return openssl_cipher_iv_length($this->method);
}
/* Encryption method */
public function encrypt($data) {
/* Get initialisation vector binary */
$iv = openssl_random_pseudo_bytes($this->iv_bytes());
/* Return IV hex & encryption string */
return bin2hex($iv) . openssl_encrypt($data, $this->method, $this->key, OPENSSL_ZERO_PADDING, $iv);
}
/* Decrypt encrypted string */
public function decrypt($data){
/* Get IV string length */
$iv_strlen = 2 * $this->iv_bytes();
/* Parse out the encryption string and unpack the IV and encrypted string. $regs is passed by reference in preg_match() */
if(preg_match("/^(.{" . $iv_strlen . "})(.+)$/", $data, $regs)) {
list(, $iv, $crypted_string) = $regs;
print_R($regs);
/* If there are character representing a hex and the IV string length is divisible by 2 */
if(ctype_xdigit($iv) && (strlen($iv) % 2 == 0)) {
/* Decrypt the unpacked encrypted string */
return openssl_decrypt($crypted_string, $this->method, $this->key, OPENSSL_ZERO_PADDING, hex2bin($iv));
}
}
return FALSE; // failed to decrypt
}
}
这是 Java
中解密器的当前沙箱 classpublic class PHPDecryptor {
private static String removeIVHash(String data) {
/* IV string length is 128 bit (16 bytes) * 2 */
int iv_strlen = 2 * 16;
/* Remove IV hash */
String encrypted_string = data.substring(iv_strlen);
return encrypted_string;
}
public static String decrypt(String data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String encrypted_string = removeIVHash(data);
/* Get bytes for string and key */
byte[] text_to_decrypt = encrypted_string.getBytes(StandardCharsets.UTF_8);
String key_string = "1234567891234567";
byte[] key = key_string.getBytes(StandardCharsets.UTF_8);
/* Get secret key and iv */
SecretKeySpec secret_key = new SecretKeySpec(key, "AES");
IvParameterSpec iv = new IvParameterSpec(new byte[16]);
/* Init cipher */
Cipher cipher = Cipher.getInstance("AES/CTR/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret_key, iv);
/* Decrypt and cast to string */
byte[] decrypted = cipher.doFinal(text_to_decrypt);
String result = new String(decrypted, StandardCharsets.UTF_8);
return result;
}
public static void main(String[] arg) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String text_to_decrypt = "203790144a345320d98fb773795d518e/ioQTApeVMV/4g=="; // Encrypted by PHP
System.out.println(decrypt(text_to_decrypt));
}
}
但是,我没有得到准确的解密 - 事实上 Java class 的 return 结果完全是胡说八道,包含无效的 UTF-8 字符。
这里有没有明显的错误?
您似乎没有对 IV 做任何事情,只是将其删除。
从您删除的 IV 创建一个 IVParameterSpec 实例。
public static IvParameterSpec getIv(String iv) {
return new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
}
如果你使用一些分隔符(我喜欢用“:”)来分隔 iv 和其余的密文,你可以更好地从字符串中检索 IV,这样你就不需要担心十六进制 IV 长度并像这样检索它:
private static String decodeIvString(String data) {
String[] parts = data.split(":");
String iv = decodeHex(parts[0]);
return iv;
}
显然,您必须在 php 的加密函数中添加定界符。
然后您可以将 IV 传回 getIv()
函数。
/* Encryption method */
public function encrypt($data) {
/* Get initialisation vector binary */
$iv = openssl_random_pseudo_bytes($this->iv_bytes());
/* Return IV hex & encryption string */
return bin2hex($iv) . ":" . openssl_encrypt($data, $this->method, $this->key, OPENSSL_ZERO_PADDING, $iv);
}
并且正如上面提到的评论,您应该将 PKCS5Padding
替换为 NoPadding
这是我用来解密的一段实用代码:
public static byte[] decryptSymmetric(Algorithm algorithm, Key key, IvParameterSpec iv, byte[] data) throws CryptoFailure {
try {
Security.addProvider(new BouncyCastleProvider());
Cipher aes = Cipher.getInstance(algorithm.getMode(), BouncyCastleProvider.PROVIDER_NAME);
aes.init(Cipher.DECRYPT_MODE, key, iv);
return aes.doFinal(data);
} catch (BadPaddingException | IllegalBlockSizeException e) {
throw new CryptoFailure(e);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
throw new CryptoError(e);
}
}