Java 中的 AES 256 加密和解密
AES 256 encrypt and Decrypt in Java
您好,我在尝试解密一个 java 的 Ogg 文件时遇到了一个问题,该文件是用 PHP
加密的
这是创建加密文件的函数
PHP crypt function
我将此代码与 java 一起使用,但生成的文件已损坏
public static String decrypt(String input, String key){
byte[] bytes = Base64.getDecoder().decode(input);
if(bytes.length < 17) {
return null;
}
String result = null;
//byte[] ivBytes = Arrays.copyOfRange(bytes, 0, 16);
//byte[] contentBytes = Arrays.copyOfRange(bytes, 16, bytes.length);
try {
//Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
Key skey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
// Set the appropriate size for mInitVec by Generating a New One
AlgorithmParameters params = cipher.getParameters();
byte[] mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV();
cipher.init(Cipher.DECRYPT_MODE, skey, new IvParameterSpec(mInitVec));
byte[] decrypted = cipher.doFinal(bytes);
String uploadedFileLocation = "C:\test3.ogg";
FileOutputStream fs = new FileOutputStream(new File(uploadedFileLocation));
fs.write(decrypted);
fs.close();
result ="decrypted";
} catch (
Exception ex
) {
System.out.println(ex.toString());
}
return result;
}
TLDR:主要是 IV
对于像 CBC 这样使用 IV(初始化向量,有时是随机数)的加密模式,解密必须使用与加密相同的 IV( 和 密钥)。此外,像 CBC 这样的块模式通常需要填充和取消填充数据,而 PHP 使用的 OpenSSL 使用 Java 也支持的 'standard' PKCS5/7 填充来实现,但是必须 select.
$ cat 71865785.php
<?php
$data = "some test data";
$cipher = "aes-256-cbc";
$key = "reallyrealsekritreallyrealsekrit"; // for test only, real key should not be human-chosen
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
echo base64_encode($iv)."\n".openssl_encrypt($data,$cipher,$key,0,$iv)."\n";
?>
$ php 71865785.php
LGQXy7XF8DHBilN4Kb8Xrg==
DWI/F97O5e/NGD2czp2OUA==
$ cat SO71865785.java
//nopackage
import java.util.Base64;
import javax.crypto.*;
import javax.crypto.spec.*;
public class SO71865785 /* decrypt from PHP */ {
public static void main (String[] args) throws Exception {
byte[] key = "reallyrealsekritreallyrealsekrit".getBytes("ASCII"); // for test only
byte[] iv = Base64.getDecoder().decode("LGQXy7XF8DHBilN4Kb8Xrg==");
byte[] ctext = Base64.getDecoder().decode("DWI/F97O5e/NGD2czp2OUA==");
Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init (Cipher.DECRYPT_MODE, new SecretKeySpec(key,"AES"), new IvParameterSpec(iv));
byte[] plain = ciph.doFinal(ctext);
System.out.println ("len="+plain.length+":"+new String(plain,"ASCII"));
// if non-ASCII chars are present must use encoding to match encrypting system which is not known
}
}
$ javac SO71865785.java
$ java -cp . SO71865785
len=14:some test data
对于实际应用程序,您当然不会使用硬编码数据(通常也不应该对密钥进行硬编码)以及将 PHP 应用程序生成的数据传递给 Java 应用程序各不相同(很多!)。一种 not-uncommon 方法是连接 IV 和密文(最好是 MAC 或 auth 标签)用于传输 and/or 存储,然后用代码将它们分开 Arrays.copyOfRange
你有评论,但对于这个演示,我只是对每个片段进行了 base64 编码(openssl_encrypt 默认情况下已经使用 base64)。
注意 AES-256 密钥必须正好是 32 个字节(并且应该是任意字节,而不是人类可识别的字符)。在 PHP/OpenSSL 中,如果您提供的少于要求,它会默默地填充零字节(又名 NUL),但 Java 不会;如果你在 Java 中有一个 too-short byte[]
你可以方便地 zero-pad 它与例如Arrays.copyOf(poorkey,32)
.
您好,我在尝试解密一个 java 的 Ogg 文件时遇到了一个问题,该文件是用 PHP
加密的这是创建加密文件的函数
PHP crypt function
我将此代码与 java 一起使用,但生成的文件已损坏
public static String decrypt(String input, String key){
byte[] bytes = Base64.getDecoder().decode(input);
if(bytes.length < 17) {
return null;
}
String result = null;
//byte[] ivBytes = Arrays.copyOfRange(bytes, 0, 16);
//byte[] contentBytes = Arrays.copyOfRange(bytes, 16, bytes.length);
try {
//Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
Key skey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
// Set the appropriate size for mInitVec by Generating a New One
AlgorithmParameters params = cipher.getParameters();
byte[] mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV();
cipher.init(Cipher.DECRYPT_MODE, skey, new IvParameterSpec(mInitVec));
byte[] decrypted = cipher.doFinal(bytes);
String uploadedFileLocation = "C:\test3.ogg";
FileOutputStream fs = new FileOutputStream(new File(uploadedFileLocation));
fs.write(decrypted);
fs.close();
result ="decrypted";
} catch (
Exception ex
) {
System.out.println(ex.toString());
}
return result;
}
TLDR:主要是 IV
对于像 CBC 这样使用 IV(初始化向量,有时是随机数)的加密模式,解密必须使用与加密相同的 IV( 和 密钥)。此外,像 CBC 这样的块模式通常需要填充和取消填充数据,而 PHP 使用的 OpenSSL 使用 Java 也支持的 'standard' PKCS5/7 填充来实现,但是必须 select.
$ cat 71865785.php
<?php
$data = "some test data";
$cipher = "aes-256-cbc";
$key = "reallyrealsekritreallyrealsekrit"; // for test only, real key should not be human-chosen
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
echo base64_encode($iv)."\n".openssl_encrypt($data,$cipher,$key,0,$iv)."\n";
?>
$ php 71865785.php
LGQXy7XF8DHBilN4Kb8Xrg==
DWI/F97O5e/NGD2czp2OUA==
$ cat SO71865785.java
//nopackage
import java.util.Base64;
import javax.crypto.*;
import javax.crypto.spec.*;
public class SO71865785 /* decrypt from PHP */ {
public static void main (String[] args) throws Exception {
byte[] key = "reallyrealsekritreallyrealsekrit".getBytes("ASCII"); // for test only
byte[] iv = Base64.getDecoder().decode("LGQXy7XF8DHBilN4Kb8Xrg==");
byte[] ctext = Base64.getDecoder().decode("DWI/F97O5e/NGD2czp2OUA==");
Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init (Cipher.DECRYPT_MODE, new SecretKeySpec(key,"AES"), new IvParameterSpec(iv));
byte[] plain = ciph.doFinal(ctext);
System.out.println ("len="+plain.length+":"+new String(plain,"ASCII"));
// if non-ASCII chars are present must use encoding to match encrypting system which is not known
}
}
$ javac SO71865785.java
$ java -cp . SO71865785
len=14:some test data
对于实际应用程序,您当然不会使用硬编码数据(通常也不应该对密钥进行硬编码)以及将 PHP 应用程序生成的数据传递给 Java 应用程序各不相同(很多!)。一种 not-uncommon 方法是连接 IV 和密文(最好是 MAC 或 auth 标签)用于传输 and/or 存储,然后用代码将它们分开 Arrays.copyOfRange
你有评论,但对于这个演示,我只是对每个片段进行了 base64 编码(openssl_encrypt 默认情况下已经使用 base64)。
注意 AES-256 密钥必须正好是 32 个字节(并且应该是任意字节,而不是人类可识别的字符)。在 PHP/OpenSSL 中,如果您提供的少于要求,它会默默地填充零字节(又名 NUL),但 Java 不会;如果你在 Java 中有一个 too-short byte[]
你可以方便地 zero-pad 它与例如Arrays.copyOf(poorkey,32)
.