我们正在使用以下代码在Java中加密数据,并尝试将逻辑转换为PHP。用一种语言加密的数据不能用另一种语言解密。有什么区别吗? 我的Java代码
public class EncYes {
private static final char[] hexDigits = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
public static void main(String[] args) {
try {
String encString=null;
EncYes enc = new EncYes();
switch(args[0]){
case "e":
System.out.println(enc.encrypt(args[1],args[2]));
break;
case "d":
System.out.println(enc.decrypt(args[1],args[2]));
break;
}
} catch (Exception e) {
System.out.println(e);
}
}
public String encrypt(String json, String key) throws NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
Cipher cipher = null;
EncYes enc = new EncYes();
//byte[] keyBytes = new byte[16];
SecretKeySpec skeySpec = new SecretKeySpec(enc.hexfromString(key), "AES");
byte[] ivSrc = new byte[12];
GCMParameterSpec ivSpec = new GCMParameterSpec(128, ivSrc);
cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(1, skeySpec, ivSpec);
byte[] encstr = cipher.doFinal(json.getBytes());
return enc.hextoString(encstr);
}
public String decrypt(String json, String key) throws NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
Cipher cipher = null;
EncYes enc = new EncYes();
//byte[] keyBytes = new byte[16];
SecretKeySpec skeySpec = new SecretKeySpec(enc.hexfromString(key), "AES");
byte[] ivSrc = new byte[12];
GCMParameterSpec ivSpec = new GCMParameterSpec(128, ivSrc);
cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(2, skeySpec, ivSpec);
byte[] encstr = cipher.doFinal(enc.hexfromString(json));
return new String(encstr);
}
public static byte[] hexfromString(String s) {
int i = s.length();
byte[] abyte0 = new byte[(i + 1) / 2];
int j = 0;
int k = 0;
if (i % 2 == 1) {
abyte0[k++] = (byte)HexfromDigit(s.charAt(j++));
}
while(j < i) {
abyte0[k++] = (byte)(HexfromDigit(s.charAt(j++)) << 4 | HexfromDigit(s.charAt(j++)));
}
return abyte0;
}
public static int HexfromDigit(char c) {
if (c >= '0' && c <= '9')
return c - 48;
if (c >= 'A' && c <= 'F')
return (c - 65) + 10;
if (c >= 'a' && c <= 'f')
return (c - 97) + 10;
else
throw new IllegalArgumentException("invalid hex digit: ");
}
public static String asHex(byte[] buf) {
StringBuffer strbuf = new StringBuffer(buf.length * 2);
for(int i = 0; i < buf.length; ++i) {
if ((buf[i] & 255) < 16) {
strbuf.append("0");
}
strbuf.append(Long.toString((long)(buf[i] & 255), 16));
}
return strbuf.toString();
}
public static String HextoString(byte abyte0[], int i, int j) {
char ac[] = new char[j * 2];
int k = 0;
for (int l = i; l < i + j; l++) {
byte byte0 = abyte0[l];
ac[k++] = hexDigits[byte0 >>> 4 & 0xf];
ac[k++] = hexDigits[byte0 & 0xf];
}
return new String(ac);
}
public static String hextoString(byte[] abyte0) {
return HextoString(abyte0, 0, abyte0.length);
}
public static String generateIv() {
UUID uId = UUID.randomUUID();
return uId.toString().replace("-", "");
}
}
class EncYes {
private static $hexDigits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
public static function main($args) {
try {
$enc = new EncYes();
switch ($args[0]) {
case "e":
return $enc->encrypt($args[1], $args[2]) . "\n";
break;
case "d":
return $enc->decrypt($args[1], $args[2]) . "\n";
break;
}
} catch (Exception $e) {
echo $e->getMessage() . "\n";
}
}
public function encrypt($json, $key) {
$iv = str_repeat("\0", 12);
$cipher = "aes-128-gcm";
$tag = "";
$encrypted = openssl_encrypt($json, $cipher, $this->hexfromString($key), OPENSSL_RAW_DATA, $iv, $tag,"",12);
if ($encrypted === false) {
throw new Exception("Encryption failed");
}
return $this->hextoString($encrypted . $tag);
}
public function decrypt($json, $key) {
$iv = str_repeat("\0", 12); // 12-byte IV filled with zeros
//$iv = base64_decode('AAAAAAAAAAAAAAAA');
$cipher = "aes-128-gcm";
$data = $this->hexfromString($json);
$encrypted = substr($data, 0, -12);
$tag = substr($data, -12);
//echo base64_encode($tag);
$decrypted = openssl_decrypt($encrypted, $cipher, $this->hexfromString($key), OPENSSL_RAW_DATA, $iv, $tag);
if ($decrypted === false) {
throw new Exception("Decryption failed");
}
return $decrypted;
}
public static function hexfromString($s) {
$i = strlen($s);
$abyte0 = array_fill(0, (int)(($i + 1) / 2), 0);
$j = 0;
$k = 0;
if ($i % 2 == 1) {
$abyte0[$k++] = self::HexfromDigit($s[$j++]);
}
while ($j < $i) {
$abyte0[$k++] = (self::HexfromDigit($s[$j++]) << 4) | self::HexfromDigit($s[$j++]);
}
return implode(array_map("chr", $abyte0));
}
public static function HexfromDigit($c) {
if ($c >= '0' && $c <= '9') {
return ord($c) - ord('0');
}
if ($c >= 'A' && $c <= 'F') {
return (ord($c) - ord('A')) + 10;
}
if ($c >= 'a' && $c <= 'f') {
return (ord($c) - ord('a')) + 10;
}
throw new InvalidArgumentException("invalid hex digit: " . $c);
}
public static function asHex($buf) {
$strbuf = "";
for ($i = 0; $i < strlen($buf); $i++) {
$byte = ord($buf[$i]);
if (($byte & 255) < 16) {
$strbuf .= "0";
}
$strbuf .= dechex($byte & 255);
}
return $strbuf;
}
public static function HextoString2($abyte0, $i, $j) {
$ac = array_fill(0, $j * 2, '0');
$k = 0;
for ($l = $i; $l < $i + $j; $l++) {
$byte0 = ord($abyte0[$l]);
$ac[$k++] = self::$hexDigits[$byte0 >> 4 & 0xf];
$ac[$k++] = self::$hexDigits[$byte0 & 0xf];
}
return implode("", $ac);
}
public static function hextoString($abyte0) {
return self::HextoString2($abyte0, 0, strlen($abyte0));
}
public static function generateIv() {
return str_replace("-", "", uuid_create());
}
}
主要问题是Java代码使用的标签长度为128位(第1个参数,
openssl_encrypt($json, $cipher, $this->hexfromString($key), OPENSSL_RAW_DATA, $iv, $tag,"",12)
中的最后一个参数以及在解密期间分开ciphertext和tag时)128位保证了最大的安全性,因此应使用此标签长度(除非有较小标签长度的有效理由)。
其他可能的问题,漏洞或效率低下是:
使用PHP代码中指定的算法是
aes-128-gcm
使用零IV,这是基于CTR的模式(如GCM)(至少使用固定键)等基于CTR的模式的关键漏洞,请参见
HERHE在Java代码中,应在
getBytes()
new String()
中明确指定一个字符集编码,因为其他依赖平台依赖的默认值(至少在较旧的Java版本中)。此外,自Java 17,s以来,已经支持了十六进制编码。 HexFormat