PHP/Java 中的 AES GCM
AES GCM in PHP/Java
我必须使用 GCM 块模式在 AES-256 中编写 encryption/decryption 库。
我在 java 中写了同样的东西,它工作正常。
这是代码:
private static final int GCM_IV_SIZE_BYTES = 12;
private static final int GCM_TAG_SIZE_BYTES = 16;
private static final int GCM_SALT_SIZE_BYTES = 16;
public static byte[] encrypt(byte[] plaintext, byte[] dataKey, String version) throws Exception
{
long startTime = System.currentTimeMillis();
// Generate Initialization Vector
byte[] IV = generateIV();
// Get Cipher Instance
Cipher cipher = getCipher();
// Get Salt
byte[] salt = generateSalt();
// Store Version
byte[] versionArr = new byte[3];
versionArr = version.getBytes();
// Generate Key
SecretKeySpec keySpec = new SecretKeySpec(dataKey, "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_SIZE_BYTES * 8, IV);
// Initialize Cipher for ENCRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Encryption
byte[] cipherText = cipher.doFinal(plaintext);
int capacity = 3 + GCM_SALT_SIZE_BYTES + GCM_IV_SIZE_BYTES + plaintext.length + GCM_TAG_SIZE_BYTES;
// Create ByteBuffer & add SALT, IV & CipherText
ByteBuffer buffer = ByteBuffer.allocate(capacity);
buffer.put(versionArr);
buffer.put(salt);
buffer.put(IV);
buffer.put(cipherText);
long endTime = System.currentTimeMillis();
System.out.println("Encryption Time : "+(endTime - startTime)+"ms");
// return the final encrypted cipher txt
return buffer.array();
}
public static String decrypt(byte[] cipherText, byte[] dataKey) throws Exception
{
long startTime = System.currentTimeMillis();
if (cipherText.length < GCM_IV_SIZE_BYTES + GCM_TAG_SIZE_BYTES + GCM_SALT_SIZE_BYTES) throw new IllegalArgumentException();
ByteBuffer buffer = ByteBuffer.wrap(cipherText);
byte[]version = new byte[3];
buffer.get(version, 0, version.length);
System.out.println(new String(version));
// Get Salt from Cipher
byte[] salt = new byte[GCM_SALT_SIZE_BYTES];
buffer.get(salt, 0, salt.length);
System.out.println(new String(salt));
// GET IV from cipher
byte[] ivBytes1 = new byte[GCM_IV_SIZE_BYTES];
buffer.get(ivBytes1, 0, ivBytes1.length);
System.out.println(new String(ivBytes1));
byte[] encryptedTextBytes = new byte[buffer.capacity() - salt.length - ivBytes1.length- 3];
buffer.get(encryptedTextBytes);
System.out.println("enc tect bytes");
System.out.println(new String(encryptedTextBytes));
// Get Cipher Instance
Cipher cipher = getCipher();
// Generate Key
SecretKeySpec keySpec = new SecretKeySpec(dataKey, "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_SIZE_BYTES * 8, ivBytes1);
// Initialize Cipher for DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Decryption
byte[] decryptedText = cipher.doFinal(encryptedTextBytes);
long endTime = System.currentTimeMillis();
System.out.println("Decryption Time : "+(endTime - startTime)+"ms");
return new String(decryptedText);
}
现在的问题是我必须在 PHP 中编写相同的库,然后我必须使用 PHP 库加密并使用 Java 库解密,反之亦然
这是我的 PHP 加密代码:
function encrypt($key, $textToEncrypt){
$cipher = 'aes-256-gcm';
$iv_len = 12;
$tag_length = 16;
$version_length = 3;
$salt_length = 16;
$version = "v01";
$iv = openssl_random_pseudo_bytes($iv_len);
$salt = openssl_random_pseudo_bytes($salt_length);
$tag = ""; // will be filled by openssl_encrypt
$ciphertext = openssl_encrypt($textToEncrypt, $cipher, $key, 0, $iv, $tag, "", $tag_length);
$encrypted = base64_encode($version.$salt.$iv.$ciphertext.$tag);
return $encrypted;
}
现在的问题是,当我使用 PHP 加密数据然后尝试使用 Java 代码对其进行解密时,出现以下异常
:Exception in thread "main" javax.crypto.AEADBadTagException: Tag mismatch!
at com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:578)
at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1049)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:985)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
我在这里错过了什么?
PHP 代码中的 Base64 存在,在调用 encode/decode 函数时 Java 代码中存在同样的内容,因此在此 post 代码中不存在。
PHP 和 Java 之间使用 AES GCM 模式的跨平台加密正在运行。有一些细节可能会阻止您成功完成。
首先:在 PHP 侧 openssl_encrypt returns 一个 base64 编码的密文,当将密文与版本、iv 和标签连接时,它再次被 base64 编码。为了避免这种情况,我将 OPENSSL 选项设置为“OPENSSL_RAW_DATA”。
其次:在Java端,标签附加到密文,因此“密文|标签”可以直接使用。
请注意:我的示例只是展示了 PHP 端的加密和 Java 端的解密如何工作,但可能与您的源代码无关(特别是Java side) - 我懒得采用我的例子:-)
这是PHP端的输出:
AES GCM in PHP/Java
ciphertext: djAx/kMbxfJI5Zx7lTWeDbw601cD2wkjBvuKeVBbKOZHll98GstPNfi1xHvyRlBwJDQ6YWvpymsk76kwbBbD0cBsOzzK/tH8UpA=
将密文复制到Java程序中,让它运行:
AES GCM in PHP/Java
decryptedtext: The quick brown fox jumps over the lazy dog
您可以在下面找到这两个程序的源代码。 安全警告:代码使用固定和硬编码密钥 - 不要这样做
这个。这些程序没有任何异常处理,仅用于教育目的。
代码是 运行ning PHP > 7.2 和 Java 11+。
PHP-代码:
<?php
function encrypt($key, $textToEncrypt){
$cipher = 'aes-256-gcm';
$iv_len = 12;
$tag_length = 16;
$version_length = 3;
$version = "v01";
$iv = openssl_random_pseudo_bytes($iv_len);
$tag = ""; // will be filled by openssl_encrypt
$ciphertext = openssl_encrypt($textToEncrypt, $cipher, $key, OPENSSL_RAW_DATA, $iv, $tag, "", $tag_length);
$encrypted = base64_encode($version.$iv.$ciphertext.$tag);
return $encrypted;
}
echo 'AES GCM in PHP/Java' . PHP_EOL;
// ### security warning: never use hardcoded keys in source ###
$key = '12345678901234567890123456789012';
$plaintext = 'The quick brown fox jumps over the lazy dog';
$ciphertext = encrypt($key, $plaintext);
echo 'ciphertext: ' . $ciphertext . PHP_EOL;
?>
Java-代码:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
public class SO_final {
public static void main(String[] args) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
System.out.println("AES GCM in PHP/Java");
//
String ciphertext = "djAx/kMbxfJI5Zx7lTWeDbw601cD2wkjBvuKeVBbKOZHll98GstPNfi1xHvyRlBwJDQ6YWvpymsk76kwbBbD0cBsOzzK/tH8UpA=";
// ### security warning: never use hardcoded keys in source ###
byte[] key = "12345678901234567890123456789012".getBytes(StandardCharsets.UTF_8);
String decryptedtext = decryptGcmBase64(key, ciphertext);
System.out.println("decryptedtext: " + decryptedtext);
}
public static String decryptGcmBase64(byte[] key, String ciphertextBase64) throws
NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException {
byte[] ciphertextComplete = Base64.getDecoder().decode(ciphertextBase64);
// split data
// base64 encoding $encrypted = base64_encode($version.$iv.$ciphertext.$tag);
byte[] version = Arrays.copyOfRange(ciphertextComplete, 0, 3); // 3 bytes
byte[] iv = Arrays.copyOfRange(ciphertextComplete, 3, 15); // 12 bytes
byte[] ciphertextWithTag = Arrays.copyOfRange(ciphertextComplete, 15, ciphertextComplete.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(16 * 8, iv);
Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");//NOPadding
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec);
return new String(cipher.doFinal(ciphertextWithTag), StandardCharsets.UTF_8);
}
}
我必须使用 GCM 块模式在 AES-256 中编写 encryption/decryption 库。 我在 java 中写了同样的东西,它工作正常。
这是代码:
private static final int GCM_IV_SIZE_BYTES = 12;
private static final int GCM_TAG_SIZE_BYTES = 16;
private static final int GCM_SALT_SIZE_BYTES = 16;
public static byte[] encrypt(byte[] plaintext, byte[] dataKey, String version) throws Exception
{
long startTime = System.currentTimeMillis();
// Generate Initialization Vector
byte[] IV = generateIV();
// Get Cipher Instance
Cipher cipher = getCipher();
// Get Salt
byte[] salt = generateSalt();
// Store Version
byte[] versionArr = new byte[3];
versionArr = version.getBytes();
// Generate Key
SecretKeySpec keySpec = new SecretKeySpec(dataKey, "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_SIZE_BYTES * 8, IV);
// Initialize Cipher for ENCRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Encryption
byte[] cipherText = cipher.doFinal(plaintext);
int capacity = 3 + GCM_SALT_SIZE_BYTES + GCM_IV_SIZE_BYTES + plaintext.length + GCM_TAG_SIZE_BYTES;
// Create ByteBuffer & add SALT, IV & CipherText
ByteBuffer buffer = ByteBuffer.allocate(capacity);
buffer.put(versionArr);
buffer.put(salt);
buffer.put(IV);
buffer.put(cipherText);
long endTime = System.currentTimeMillis();
System.out.println("Encryption Time : "+(endTime - startTime)+"ms");
// return the final encrypted cipher txt
return buffer.array();
}
public static String decrypt(byte[] cipherText, byte[] dataKey) throws Exception
{
long startTime = System.currentTimeMillis();
if (cipherText.length < GCM_IV_SIZE_BYTES + GCM_TAG_SIZE_BYTES + GCM_SALT_SIZE_BYTES) throw new IllegalArgumentException();
ByteBuffer buffer = ByteBuffer.wrap(cipherText);
byte[]version = new byte[3];
buffer.get(version, 0, version.length);
System.out.println(new String(version));
// Get Salt from Cipher
byte[] salt = new byte[GCM_SALT_SIZE_BYTES];
buffer.get(salt, 0, salt.length);
System.out.println(new String(salt));
// GET IV from cipher
byte[] ivBytes1 = new byte[GCM_IV_SIZE_BYTES];
buffer.get(ivBytes1, 0, ivBytes1.length);
System.out.println(new String(ivBytes1));
byte[] encryptedTextBytes = new byte[buffer.capacity() - salt.length - ivBytes1.length- 3];
buffer.get(encryptedTextBytes);
System.out.println("enc tect bytes");
System.out.println(new String(encryptedTextBytes));
// Get Cipher Instance
Cipher cipher = getCipher();
// Generate Key
SecretKeySpec keySpec = new SecretKeySpec(dataKey, "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_SIZE_BYTES * 8, ivBytes1);
// Initialize Cipher for DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Decryption
byte[] decryptedText = cipher.doFinal(encryptedTextBytes);
long endTime = System.currentTimeMillis();
System.out.println("Decryption Time : "+(endTime - startTime)+"ms");
return new String(decryptedText);
}
现在的问题是我必须在 PHP 中编写相同的库,然后我必须使用 PHP 库加密并使用 Java 库解密,反之亦然
这是我的 PHP 加密代码:
function encrypt($key, $textToEncrypt){
$cipher = 'aes-256-gcm';
$iv_len = 12;
$tag_length = 16;
$version_length = 3;
$salt_length = 16;
$version = "v01";
$iv = openssl_random_pseudo_bytes($iv_len);
$salt = openssl_random_pseudo_bytes($salt_length);
$tag = ""; // will be filled by openssl_encrypt
$ciphertext = openssl_encrypt($textToEncrypt, $cipher, $key, 0, $iv, $tag, "", $tag_length);
$encrypted = base64_encode($version.$salt.$iv.$ciphertext.$tag);
return $encrypted;
}
现在的问题是,当我使用 PHP 加密数据然后尝试使用 Java 代码对其进行解密时,出现以下异常
:Exception in thread "main" javax.crypto.AEADBadTagException: Tag mismatch!
at com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:578)
at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1049)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:985)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
我在这里错过了什么?
PHP 代码中的 Base64 存在,在调用 encode/decode 函数时 Java 代码中存在同样的内容,因此在此 post 代码中不存在。
PHP 和 Java 之间使用 AES GCM 模式的跨平台加密正在运行。有一些细节可能会阻止您成功完成。
首先:在 PHP 侧 openssl_encrypt returns 一个 base64 编码的密文,当将密文与版本、iv 和标签连接时,它再次被 base64 编码。为了避免这种情况,我将 OPENSSL 选项设置为“OPENSSL_RAW_DATA”。
其次:在Java端,标签附加到密文,因此“密文|标签”可以直接使用。
请注意:我的示例只是展示了 PHP 端的加密和 Java 端的解密如何工作,但可能与您的源代码无关(特别是Java side) - 我懒得采用我的例子:-)
这是PHP端的输出:
AES GCM in PHP/Java
ciphertext: djAx/kMbxfJI5Zx7lTWeDbw601cD2wkjBvuKeVBbKOZHll98GstPNfi1xHvyRlBwJDQ6YWvpymsk76kwbBbD0cBsOzzK/tH8UpA=
将密文复制到Java程序中,让它运行:
AES GCM in PHP/Java
decryptedtext: The quick brown fox jumps over the lazy dog
您可以在下面找到这两个程序的源代码。 安全警告:代码使用固定和硬编码密钥 - 不要这样做 这个。这些程序没有任何异常处理,仅用于教育目的。
代码是 运行ning PHP > 7.2 和 Java 11+。
PHP-代码:
<?php
function encrypt($key, $textToEncrypt){
$cipher = 'aes-256-gcm';
$iv_len = 12;
$tag_length = 16;
$version_length = 3;
$version = "v01";
$iv = openssl_random_pseudo_bytes($iv_len);
$tag = ""; // will be filled by openssl_encrypt
$ciphertext = openssl_encrypt($textToEncrypt, $cipher, $key, OPENSSL_RAW_DATA, $iv, $tag, "", $tag_length);
$encrypted = base64_encode($version.$iv.$ciphertext.$tag);
return $encrypted;
}
echo 'AES GCM in PHP/Java' . PHP_EOL;
// ### security warning: never use hardcoded keys in source ###
$key = '12345678901234567890123456789012';
$plaintext = 'The quick brown fox jumps over the lazy dog';
$ciphertext = encrypt($key, $plaintext);
echo 'ciphertext: ' . $ciphertext . PHP_EOL;
?>
Java-代码:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
public class SO_final {
public static void main(String[] args) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
System.out.println("AES GCM in PHP/Java");
//
String ciphertext = "djAx/kMbxfJI5Zx7lTWeDbw601cD2wkjBvuKeVBbKOZHll98GstPNfi1xHvyRlBwJDQ6YWvpymsk76kwbBbD0cBsOzzK/tH8UpA=";
// ### security warning: never use hardcoded keys in source ###
byte[] key = "12345678901234567890123456789012".getBytes(StandardCharsets.UTF_8);
String decryptedtext = decryptGcmBase64(key, ciphertext);
System.out.println("decryptedtext: " + decryptedtext);
}
public static String decryptGcmBase64(byte[] key, String ciphertextBase64) throws
NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException {
byte[] ciphertextComplete = Base64.getDecoder().decode(ciphertextBase64);
// split data
// base64 encoding $encrypted = base64_encode($version.$iv.$ciphertext.$tag);
byte[] version = Arrays.copyOfRange(ciphertextComplete, 0, 3); // 3 bytes
byte[] iv = Arrays.copyOfRange(ciphertextComplete, 3, 15); // 12 bytes
byte[] ciphertextWithTag = Arrays.copyOfRange(ciphertextComplete, 15, ciphertextComplete.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(16 * 8, iv);
Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");//NOPadding
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec);
return new String(cipher.doFinal(ciphertextWithTag), StandardCharsets.UTF_8);
}
}