从 Java 负载中解密 PHP 中的 AES/GCM/PKCS5Padding
Decrypt AES/GCM/PKCS5Padding in PHP from Java payload
我想从 SecureGMC java 进行简单加密,然后使用 AES/GCM/PKCS5Padding 在 PHP 中解密。我假设要解密的数据来自本地银行,它强调 IV_SIZE=96 和 TAG_BIT_LENGTH=128;他们推荐我这个 link 作为参考。根据我的阅读,openssl_decrypt() 似乎是最好的选择,但是,我找不到任何 $tag 变量可供我在解密时发送过来 Java。 openssl_decrypt() 甚至推荐吗?
cipher.java
public class SecuredGCMUsage {
public static int AES_KEY_SIZE = 256 ;
public static int IV_SIZE = 96 ;
public static int TAG_BIT_LENGTH = 128 ;
public static String ALGO_TRANSFORMATION_STRING = "AES/GCM/PKCS5Padding" ;
public static void main(String args[]) {
String messageToEncrypt = "Testing message to decrypt" ;
byte[] aadData = "".getBytes() ; // Any random data can be used as tag. Some common examples could be domain name...
// Use different key+IV pair for encrypting/decrypting different parameters
// Generating Key
SecretKey aesKey = null ;
try {
KeyGenerator keygen = KeyGenerator.getInstance("AES") ; // Specifying algorithm key will be used for
keygen.init(AES_KEY_SIZE) ; // Specifying Key size to be used, Note: This would need JCE Unlimited Strength to be installed explicitly
aesKey = keygen.generateKey() ;
} catch(NoSuchAlgorithmException noSuchAlgoExc) { System.out.println("Key being request is for AES algorithm, but this cryptographic algorithm is not available in the environment " + noSuchAlgoExc) ; System.exit(1) ; }
// Generating IV
byte iv[] = new byte[IV_SIZE];
SecureRandom secRandom = new SecureRandom() ;
secRandom.nextBytes(iv); // SecureRandom initialized using self-seeding
// Initialize GCM Parameters
GCMParameterSpec gcmParamSpec = new GCMParameterSpec(TAG_BIT_LENGTH, iv) ;
byte[] encryptedText = aesEncrypt(messageToEncrypt, aesKey, gcmParamSpec, aadData) ;
System.out.println("Encrypted Text = " + Base64.getEncoder().encodeToString(encryptedText) ) ;
byte[] decryptedText = aesDecrypt(encryptedText, aesKey, gcmParamSpec, aadData) ; // Same key, IV and GCM Specs for decryption as used for encryption.
System.out.println("Decrypted text " + new String(decryptedText)) ;
// Make sure not to repeat Key + IV pair, for encrypting more than one plaintext.
secRandom.nextBytes(iv);
}
public static byte[] aesEncrypt(String message, SecretKey aesKey, GCMParameterSpec gcmParamSpec, byte[] aadData) {
Cipher c = null ;
try {
c = Cipher.getInstance(ALGO_TRANSFORMATION_STRING); // Transformation specifies algortihm, mode of operation and padding
}catch(NoSuchAlgorithmException noSuchAlgoExc) {System.out.println("Exception while encrypting. Algorithm being requested is not available in this environment " + noSuchAlgoExc); System.exit(1); }
catch(NoSuchPaddingException noSuchPaddingExc) {System.out.println("Exception while encrypting. Padding Scheme being requested is not available this environment " + noSuchPaddingExc); System.exit(1); }
try {
c.init(Cipher.ENCRYPT_MODE, aesKey, gcmParamSpec, new SecureRandom()) ;
} catch(InvalidKeyException invalidKeyExc) {System.out.println("Exception while encrypting. Key being used is not valid. It could be due to invalid encoding, wrong length or uninitialized " + invalidKeyExc) ; System.exit(1); }
catch(InvalidAlgorithmParameterException invalidAlgoParamExc) {System.out.println("Exception while encrypting. Algorithm parameters being specified are not valid " + invalidAlgoParamExc) ; System.exit(1); }
try {
c.updateAAD(aadData) ; // add AAD tag data before encrypting
}catch(IllegalArgumentException illegalArgumentExc) {System.out.println("Exception thrown while encrypting. Byte array might be null " +illegalArgumentExc ); System.exit(1);}
catch(IllegalStateException illegalStateExc) {System.out.println("Exception thrown while encrypting. CIpher is in an illegal state " +illegalStateExc); System.exit(1);}
catch(UnsupportedOperationException unsupportedExc) {System.out.println("Exception thrown while encrypting. Provider might not be supporting this method " +unsupportedExc); System.exit(1);}
byte[] cipherTextInByteArr = null ;
try {
cipherTextInByteArr = c.doFinal(message.getBytes()) ;
} catch(IllegalBlockSizeException illegalBlockSizeExc) {System.out.println("Exception while encrypting, due to block size " + illegalBlockSizeExc) ; System.exit(1); }
catch(BadPaddingException badPaddingExc) {System.out.println("Exception while encrypting, due to padding scheme " + badPaddingExc) ; System.exit(1); }
return cipherTextInByteArr ;
}
public static byte[] aesDecrypt(byte[] encryptedMessage, SecretKey aesKey, GCMParameterSpec gcmParamSpec, byte[] aadData) {
Cipher c = null ;
try {
c = Cipher.getInstance(ALGO_TRANSFORMATION_STRING); // Transformation specifies algortihm, mode of operation and padding
} catch(NoSuchAlgorithmException noSuchAlgoExc) {System.out.println("Exception while decrypting. Algorithm being requested is not available in environment " + noSuchAlgoExc); System.exit(1); }
catch(NoSuchPaddingException noSuchAlgoExc) {System.out.println("Exception while decrypting. Padding scheme being requested is not available in environment " + noSuchAlgoExc); System.exit(1); }
try {
c.init(Cipher.DECRYPT_MODE, aesKey, gcmParamSpec, new SecureRandom()) ;
} catch(InvalidKeyException invalidKeyExc) {System.out.println("Exception while encrypting. Key being used is not valid. It could be due to invalid encoding, wrong length or uninitialized " + invalidKeyExc) ; System.exit(1); }
catch(InvalidAlgorithmParameterException invalidParamSpecExc) {System.out.println("Exception while encrypting. Algorithm Param being used is not valid. " + invalidParamSpecExc) ; System.exit(1); }
try {
c.updateAAD(aadData) ; // Add AAD details before decrypting
}catch(IllegalArgumentException illegalArgumentExc) {System.out.println("Exception thrown while encrypting. Byte array might be null " +illegalArgumentExc ); System.exit(1);}
catch(IllegalStateException illegalStateExc) {System.out.println("Exception thrown while encrypting. CIpher is in an illegal state " +illegalStateExc); System.exit(1);}
byte[] plainTextInByteArr = null ;
try {
plainTextInByteArr = c.doFinal(encryptedMessage) ;
} catch(IllegalBlockSizeException illegalBlockSizeExc) {System.out.println("Exception while decryption, due to block size " + illegalBlockSizeExc) ; System.exit(1); }
catch(BadPaddingException badPaddingExc) {System.out.println("Exception while decryption, due to padding scheme " + badPaddingExc) ; System.exit(1); }
return plainTextInByteArr ;
}
}
cipher.java 会产生
ENCRYPTED- WHcLaJZWwTTKD1fVkmOoH0KpShZRn/LIDQwp9Djz+0MG7bp+gO+4pHCmGw==
KEY- 4A5wU7DQ0orJv91J8eZu2yMcr6sHyuAiKaNe5KdM7iw=
IV- cG8zFrxyYSeXvwx7bxQrCp6LxaZ8GQhxUcrGJkZTzfKJaErLztV9dy/iz123cw/4wEz44IMtpNR0OZSz2SA+zZLfsge3m/WJlS9xwNSYjatzYMm123hpyStcFKedi+A8
DECRYPTED TEXT- Testing message to dencrypt
decryption.java(仅解密)- 我已经隔离到一个新的 class 中,用于仅处理解密数据以模拟生产。
public class SecuredGCMUsage {
public static int AES_KEY_SIZE = 256 ;
public static int IV_SIZE = 96 ;
public static int TAG_BIT_LENGTH = 128 ;
public static String ALGO_TRANSFORMATION_STRING = "AES/GCM/PKCS5Padding" ;
public static void main(String args[]) {
// ARGS DETAILS
String payloadText = "WHcLaJZWwTTKD1fVkmOoH0KpShZRn/LIDQwp9Djz+0MG7bp+gO+4pHCmGw==";
String payloadKey = "4A5wU7DQ0orJv91J8eZu2yMcr6sHyuAiKaNe5KdM7iw=";
String payloadIv = "cG8zFrxyYSeXvwx7bxQrCp6LxaZ8GQhxUcrGJkZTzfKJaErLztV9dy/iz123cw/4wEz44IMtpNR0OZSz2SA+zZLfsge3m/WJlS9xwNSYjatzYMm123hpyStcFKedi+A8";
byte[] aadData2 = "testing.com".getBytes() ;
byte[] encryptedText2 = Base64.getDecoder().decode(payloadText);
byte[] decodedKey = Base64.getDecoder().decode(payloadKey);
byte[] iv = Base64.getDecoder().decode(payloadIv);
GCMParameterSpec gcmParamSpec2 = new GCMParameterSpec(TAG_BIT_LENGTH, iv) ;
SecretKey aesKey2 = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
byte[] decryptedText2 = aesDecrypt(
encryptedText2,
aesKey2,
gcmParamSpec2,
aadData2
) ; // Same key, IV and GCM Specs for decryption as used for encryption.
System.out.println("DECRYPTED TEXT- " + new String(decryptedText2)) ;
}
public static byte[] aesDecrypt(byte[] encryptedMessage, SecretKey aesKey, GCMParameterSpec gcmParamSpec, byte[] aadData) {
Cipher c = null ;
try {
c = Cipher.getInstance(ALGO_TRANSFORMATION_STRING); // Transformation specifies algortihm, mode of operation and padding
} catch(NoSuchAlgorithmException noSuchAlgoExc) {System.out.println("Exception while decrypting. Algorithm being requested is not available in environment " + noSuchAlgoExc); System.exit(1); }
catch(NoSuchPaddingException noSuchAlgoExc) {System.out.println("Exception while decrypting. Padding scheme being requested is not available in environment " + noSuchAlgoExc); System.exit(1); }
try {
c.init(Cipher.DECRYPT_MODE, aesKey, gcmParamSpec, new SecureRandom()) ;
} catch(InvalidKeyException invalidKeyExc) {System.out.println("Exception while encrypting. Key being used is not valid. It could be due to invalid encoding, wrong length or uninitialized " + invalidKeyExc) ; System.exit(1); }
catch(InvalidAlgorithmParameterException invalidParamSpecExc) {System.out.println("Exception while encrypting. Algorithm Param being used is not valid. " + invalidParamSpecExc) ; System.exit(1); }
try {
// c.updateAAD(aadData) ; // Add AAD details before decrypting
}catch(IllegalArgumentException illegalArgumentExc) {System.out.println("Exception thrown while encrypting. Byte array might be null " +illegalArgumentExc ); System.exit(1);}
catch(IllegalStateException illegalStateExc) {System.out.println("Exception thrown while encrypting. CIpher is in an illegal state " +illegalStateExc); System.exit(1);}
byte[] plainTextInByteArr = null ;
try {
plainTextInByteArr = c.doFinal(encryptedMessage) ;
} catch(IllegalBlockSizeException illegalBlockSizeExc) {System.out.println("Exception while decryption, due to block size " + illegalBlockSizeExc) ; System.exit(1); }
catch(BadPaddingException badPaddingExc) {System.out.println("Exception while decryption, due to padding scheme " + badPaddingExc) ; System.exit(1); }
return plainTextInByteArr ;
}
}
结果(成功)
DECRYPTED TEXT- Testing message to dencrypt
然而我的奋斗是在decryption.php不断失败的地方。
public static function decrypt_aes_gcm_pkcs5padding ($data)
{
$ciphertext = $data["encryptedPayload"];
$key = $data["encryptedSessionKey"];
$iv = $data["iv"];
// $aad = "";
$encrypt = base64_decode($ciphertext);
$ivlen = openssl_cipher_iv_length($cipher = "aes-256-gcm");
$tag_length = 16;
$iv = substr($encrypt, 0, $ivlen);
$tag = substr($encrypt, -$tag_length);
$ciphertext = substr($encrypt, $ivlen, -$tag_length);
$ciphertext_raw = openssl_decrypt($ciphertext, $cipher, $key, OPENSSL_NO_PADDING, $iv, $tag);
return $ciphertext_raw;
}
结果(失败)
PHP代码解密失败,因为IV、密文和tag判断错误
Cipher#doFinal()
在 Java 代码中隐式连接密文和标签 (ciphertext | tag
)。相反,IV 既不是隐式也不是显式连接。但是,PHP 代码假定 IV 也已连接 (iv | ciphertext | tag
)。因此,基于此假设执行的分离失败。
由于前面的分隔,传递给openssl_decrypt()
的密文不是Base64编码的,所以必须设置OPENSSL_RAW_DATA
标志(值:1)。 GCM 不使用填充。 OPENSSL_ZERO_PADDING
标志(值:2)禁用填充,但不需要显式设置,因为它是为 GCM 隐式设置的。 OPENSSL_NO_PADDING
标志(值:3)仅在非对称加密的上下文中定义,不应在对称加密的上下文中使用。如果设置,则与OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
效果相同,本例恰好是正确的,但一般无意中设置OPENSSL_RAW_DATA
。
以下PHP实现正确解密了Java代码的数据:
// Data from C# side
$encrypt = base64_decode('7iJzLyqHMu3sVsUI4dCghjbf4gsQaFto/4L+6ccywRgq33hcWKOrz/pF');
$key = base64_decode('ZQE3/FMRH1wOCFJWuaRpsizC/ltMFlxJvTYu0J4oN+Q=');
$iv = base64_decode('SzlX3ihgFwBwUcDdWatoNTQ9BcMQ5nJoJqoV2eBtQuh/uP+eceqdUiVZldwOiUX8m5qIDYpyeqz7z8a+7xRWNUL3xnlcKBEemCrASLCG2tKpEXXMQD+a9t2v2WGUKQ6D');
$aad = "sample aad";
$tagLength = 16;
$tag = substr($encrypt, strlen($encrypt) - $tagLength, $tagLength);
$ciphertext = substr($encrypt, 0, strlen($encrypt) - $tagLength);
$ciphertext_raw = openssl_decrypt($ciphertext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag, $aad);
print($ciphertext_raw);
其中测试数据是使用 Java 代码生成的。
Java代码为参考代码,不可更改。尽管如此,还是要注意以下几点:
- GCM 是一种流密码模式,不使用填充。因此,
AES/GCM/NoPadding
应该在 Java 代码中指定,而不是 AES/GCM/PKCS5Padding
。但是,SunJCE 提供者忽略 PKCS5Padding
规范并且不填充,因此这没有影响。
- 对于 GCM,建议 nonce/IV 为 12 字节。在 Java(参考)代码中,位和字节混淆,因此使用了 96 字节 nonce/IV。由于对于 GCM,如果 nonce/IV 不是 12 个字节 (here),则重新计算 nonce/IV,实际上太长的 nonce/IV 由算法处理,因此不会导致解密失败PHP 代码,但仍然不符合建议的长度。
我想从 SecureGMC java 进行简单加密,然后使用 AES/GCM/PKCS5Padding 在 PHP 中解密。我假设要解密的数据来自本地银行,它强调 IV_SIZE=96 和 TAG_BIT_LENGTH=128;他们推荐我这个 link 作为参考。根据我的阅读,openssl_decrypt() 似乎是最好的选择,但是,我找不到任何 $tag 变量可供我在解密时发送过来 Java。 openssl_decrypt() 甚至推荐吗?
cipher.java
public class SecuredGCMUsage {
public static int AES_KEY_SIZE = 256 ;
public static int IV_SIZE = 96 ;
public static int TAG_BIT_LENGTH = 128 ;
public static String ALGO_TRANSFORMATION_STRING = "AES/GCM/PKCS5Padding" ;
public static void main(String args[]) {
String messageToEncrypt = "Testing message to decrypt" ;
byte[] aadData = "".getBytes() ; // Any random data can be used as tag. Some common examples could be domain name...
// Use different key+IV pair for encrypting/decrypting different parameters
// Generating Key
SecretKey aesKey = null ;
try {
KeyGenerator keygen = KeyGenerator.getInstance("AES") ; // Specifying algorithm key will be used for
keygen.init(AES_KEY_SIZE) ; // Specifying Key size to be used, Note: This would need JCE Unlimited Strength to be installed explicitly
aesKey = keygen.generateKey() ;
} catch(NoSuchAlgorithmException noSuchAlgoExc) { System.out.println("Key being request is for AES algorithm, but this cryptographic algorithm is not available in the environment " + noSuchAlgoExc) ; System.exit(1) ; }
// Generating IV
byte iv[] = new byte[IV_SIZE];
SecureRandom secRandom = new SecureRandom() ;
secRandom.nextBytes(iv); // SecureRandom initialized using self-seeding
// Initialize GCM Parameters
GCMParameterSpec gcmParamSpec = new GCMParameterSpec(TAG_BIT_LENGTH, iv) ;
byte[] encryptedText = aesEncrypt(messageToEncrypt, aesKey, gcmParamSpec, aadData) ;
System.out.println("Encrypted Text = " + Base64.getEncoder().encodeToString(encryptedText) ) ;
byte[] decryptedText = aesDecrypt(encryptedText, aesKey, gcmParamSpec, aadData) ; // Same key, IV and GCM Specs for decryption as used for encryption.
System.out.println("Decrypted text " + new String(decryptedText)) ;
// Make sure not to repeat Key + IV pair, for encrypting more than one plaintext.
secRandom.nextBytes(iv);
}
public static byte[] aesEncrypt(String message, SecretKey aesKey, GCMParameterSpec gcmParamSpec, byte[] aadData) {
Cipher c = null ;
try {
c = Cipher.getInstance(ALGO_TRANSFORMATION_STRING); // Transformation specifies algortihm, mode of operation and padding
}catch(NoSuchAlgorithmException noSuchAlgoExc) {System.out.println("Exception while encrypting. Algorithm being requested is not available in this environment " + noSuchAlgoExc); System.exit(1); }
catch(NoSuchPaddingException noSuchPaddingExc) {System.out.println("Exception while encrypting. Padding Scheme being requested is not available this environment " + noSuchPaddingExc); System.exit(1); }
try {
c.init(Cipher.ENCRYPT_MODE, aesKey, gcmParamSpec, new SecureRandom()) ;
} catch(InvalidKeyException invalidKeyExc) {System.out.println("Exception while encrypting. Key being used is not valid. It could be due to invalid encoding, wrong length or uninitialized " + invalidKeyExc) ; System.exit(1); }
catch(InvalidAlgorithmParameterException invalidAlgoParamExc) {System.out.println("Exception while encrypting. Algorithm parameters being specified are not valid " + invalidAlgoParamExc) ; System.exit(1); }
try {
c.updateAAD(aadData) ; // add AAD tag data before encrypting
}catch(IllegalArgumentException illegalArgumentExc) {System.out.println("Exception thrown while encrypting. Byte array might be null " +illegalArgumentExc ); System.exit(1);}
catch(IllegalStateException illegalStateExc) {System.out.println("Exception thrown while encrypting. CIpher is in an illegal state " +illegalStateExc); System.exit(1);}
catch(UnsupportedOperationException unsupportedExc) {System.out.println("Exception thrown while encrypting. Provider might not be supporting this method " +unsupportedExc); System.exit(1);}
byte[] cipherTextInByteArr = null ;
try {
cipherTextInByteArr = c.doFinal(message.getBytes()) ;
} catch(IllegalBlockSizeException illegalBlockSizeExc) {System.out.println("Exception while encrypting, due to block size " + illegalBlockSizeExc) ; System.exit(1); }
catch(BadPaddingException badPaddingExc) {System.out.println("Exception while encrypting, due to padding scheme " + badPaddingExc) ; System.exit(1); }
return cipherTextInByteArr ;
}
public static byte[] aesDecrypt(byte[] encryptedMessage, SecretKey aesKey, GCMParameterSpec gcmParamSpec, byte[] aadData) {
Cipher c = null ;
try {
c = Cipher.getInstance(ALGO_TRANSFORMATION_STRING); // Transformation specifies algortihm, mode of operation and padding
} catch(NoSuchAlgorithmException noSuchAlgoExc) {System.out.println("Exception while decrypting. Algorithm being requested is not available in environment " + noSuchAlgoExc); System.exit(1); }
catch(NoSuchPaddingException noSuchAlgoExc) {System.out.println("Exception while decrypting. Padding scheme being requested is not available in environment " + noSuchAlgoExc); System.exit(1); }
try {
c.init(Cipher.DECRYPT_MODE, aesKey, gcmParamSpec, new SecureRandom()) ;
} catch(InvalidKeyException invalidKeyExc) {System.out.println("Exception while encrypting. Key being used is not valid. It could be due to invalid encoding, wrong length or uninitialized " + invalidKeyExc) ; System.exit(1); }
catch(InvalidAlgorithmParameterException invalidParamSpecExc) {System.out.println("Exception while encrypting. Algorithm Param being used is not valid. " + invalidParamSpecExc) ; System.exit(1); }
try {
c.updateAAD(aadData) ; // Add AAD details before decrypting
}catch(IllegalArgumentException illegalArgumentExc) {System.out.println("Exception thrown while encrypting. Byte array might be null " +illegalArgumentExc ); System.exit(1);}
catch(IllegalStateException illegalStateExc) {System.out.println("Exception thrown while encrypting. CIpher is in an illegal state " +illegalStateExc); System.exit(1);}
byte[] plainTextInByteArr = null ;
try {
plainTextInByteArr = c.doFinal(encryptedMessage) ;
} catch(IllegalBlockSizeException illegalBlockSizeExc) {System.out.println("Exception while decryption, due to block size " + illegalBlockSizeExc) ; System.exit(1); }
catch(BadPaddingException badPaddingExc) {System.out.println("Exception while decryption, due to padding scheme " + badPaddingExc) ; System.exit(1); }
return plainTextInByteArr ;
}
}
cipher.java 会产生
ENCRYPTED- WHcLaJZWwTTKD1fVkmOoH0KpShZRn/LIDQwp9Djz+0MG7bp+gO+4pHCmGw==
KEY- 4A5wU7DQ0orJv91J8eZu2yMcr6sHyuAiKaNe5KdM7iw=
IV- cG8zFrxyYSeXvwx7bxQrCp6LxaZ8GQhxUcrGJkZTzfKJaErLztV9dy/iz123cw/4wEz44IMtpNR0OZSz2SA+zZLfsge3m/WJlS9xwNSYjatzYMm123hpyStcFKedi+A8
DECRYPTED TEXT- Testing message to dencrypt
decryption.java(仅解密)- 我已经隔离到一个新的 class 中,用于仅处理解密数据以模拟生产。
public class SecuredGCMUsage {
public static int AES_KEY_SIZE = 256 ;
public static int IV_SIZE = 96 ;
public static int TAG_BIT_LENGTH = 128 ;
public static String ALGO_TRANSFORMATION_STRING = "AES/GCM/PKCS5Padding" ;
public static void main(String args[]) {
// ARGS DETAILS
String payloadText = "WHcLaJZWwTTKD1fVkmOoH0KpShZRn/LIDQwp9Djz+0MG7bp+gO+4pHCmGw==";
String payloadKey = "4A5wU7DQ0orJv91J8eZu2yMcr6sHyuAiKaNe5KdM7iw=";
String payloadIv = "cG8zFrxyYSeXvwx7bxQrCp6LxaZ8GQhxUcrGJkZTzfKJaErLztV9dy/iz123cw/4wEz44IMtpNR0OZSz2SA+zZLfsge3m/WJlS9xwNSYjatzYMm123hpyStcFKedi+A8";
byte[] aadData2 = "testing.com".getBytes() ;
byte[] encryptedText2 = Base64.getDecoder().decode(payloadText);
byte[] decodedKey = Base64.getDecoder().decode(payloadKey);
byte[] iv = Base64.getDecoder().decode(payloadIv);
GCMParameterSpec gcmParamSpec2 = new GCMParameterSpec(TAG_BIT_LENGTH, iv) ;
SecretKey aesKey2 = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
byte[] decryptedText2 = aesDecrypt(
encryptedText2,
aesKey2,
gcmParamSpec2,
aadData2
) ; // Same key, IV and GCM Specs for decryption as used for encryption.
System.out.println("DECRYPTED TEXT- " + new String(decryptedText2)) ;
}
public static byte[] aesDecrypt(byte[] encryptedMessage, SecretKey aesKey, GCMParameterSpec gcmParamSpec, byte[] aadData) {
Cipher c = null ;
try {
c = Cipher.getInstance(ALGO_TRANSFORMATION_STRING); // Transformation specifies algortihm, mode of operation and padding
} catch(NoSuchAlgorithmException noSuchAlgoExc) {System.out.println("Exception while decrypting. Algorithm being requested is not available in environment " + noSuchAlgoExc); System.exit(1); }
catch(NoSuchPaddingException noSuchAlgoExc) {System.out.println("Exception while decrypting. Padding scheme being requested is not available in environment " + noSuchAlgoExc); System.exit(1); }
try {
c.init(Cipher.DECRYPT_MODE, aesKey, gcmParamSpec, new SecureRandom()) ;
} catch(InvalidKeyException invalidKeyExc) {System.out.println("Exception while encrypting. Key being used is not valid. It could be due to invalid encoding, wrong length or uninitialized " + invalidKeyExc) ; System.exit(1); }
catch(InvalidAlgorithmParameterException invalidParamSpecExc) {System.out.println("Exception while encrypting. Algorithm Param being used is not valid. " + invalidParamSpecExc) ; System.exit(1); }
try {
// c.updateAAD(aadData) ; // Add AAD details before decrypting
}catch(IllegalArgumentException illegalArgumentExc) {System.out.println("Exception thrown while encrypting. Byte array might be null " +illegalArgumentExc ); System.exit(1);}
catch(IllegalStateException illegalStateExc) {System.out.println("Exception thrown while encrypting. CIpher is in an illegal state " +illegalStateExc); System.exit(1);}
byte[] plainTextInByteArr = null ;
try {
plainTextInByteArr = c.doFinal(encryptedMessage) ;
} catch(IllegalBlockSizeException illegalBlockSizeExc) {System.out.println("Exception while decryption, due to block size " + illegalBlockSizeExc) ; System.exit(1); }
catch(BadPaddingException badPaddingExc) {System.out.println("Exception while decryption, due to padding scheme " + badPaddingExc) ; System.exit(1); }
return plainTextInByteArr ;
}
}
结果(成功)
DECRYPTED TEXT- Testing message to dencrypt
然而我的奋斗是在decryption.php不断失败的地方。
public static function decrypt_aes_gcm_pkcs5padding ($data)
{
$ciphertext = $data["encryptedPayload"];
$key = $data["encryptedSessionKey"];
$iv = $data["iv"];
// $aad = "";
$encrypt = base64_decode($ciphertext);
$ivlen = openssl_cipher_iv_length($cipher = "aes-256-gcm");
$tag_length = 16;
$iv = substr($encrypt, 0, $ivlen);
$tag = substr($encrypt, -$tag_length);
$ciphertext = substr($encrypt, $ivlen, -$tag_length);
$ciphertext_raw = openssl_decrypt($ciphertext, $cipher, $key, OPENSSL_NO_PADDING, $iv, $tag);
return $ciphertext_raw;
}
结果(失败)
PHP代码解密失败,因为IV、密文和tag判断错误
Cipher#doFinal()
在 Java 代码中隐式连接密文和标签 (ciphertext | tag
)。相反,IV 既不是隐式也不是显式连接。但是,PHP 代码假定 IV 也已连接 (iv | ciphertext | tag
)。因此,基于此假设执行的分离失败。
由于前面的分隔,传递给openssl_decrypt()
的密文不是Base64编码的,所以必须设置OPENSSL_RAW_DATA
标志(值:1)。 GCM 不使用填充。 OPENSSL_ZERO_PADDING
标志(值:2)禁用填充,但不需要显式设置,因为它是为 GCM 隐式设置的。 OPENSSL_NO_PADDING
标志(值:3)仅在非对称加密的上下文中定义,不应在对称加密的上下文中使用。如果设置,则与OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
效果相同,本例恰好是正确的,但一般无意中设置OPENSSL_RAW_DATA
。
以下PHP实现正确解密了Java代码的数据:
// Data from C# side
$encrypt = base64_decode('7iJzLyqHMu3sVsUI4dCghjbf4gsQaFto/4L+6ccywRgq33hcWKOrz/pF');
$key = base64_decode('ZQE3/FMRH1wOCFJWuaRpsizC/ltMFlxJvTYu0J4oN+Q=');
$iv = base64_decode('SzlX3ihgFwBwUcDdWatoNTQ9BcMQ5nJoJqoV2eBtQuh/uP+eceqdUiVZldwOiUX8m5qIDYpyeqz7z8a+7xRWNUL3xnlcKBEemCrASLCG2tKpEXXMQD+a9t2v2WGUKQ6D');
$aad = "sample aad";
$tagLength = 16;
$tag = substr($encrypt, strlen($encrypt) - $tagLength, $tagLength);
$ciphertext = substr($encrypt, 0, strlen($encrypt) - $tagLength);
$ciphertext_raw = openssl_decrypt($ciphertext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag, $aad);
print($ciphertext_raw);
其中测试数据是使用 Java 代码生成的。
Java代码为参考代码,不可更改。尽管如此,还是要注意以下几点:
- GCM 是一种流密码模式,不使用填充。因此,
AES/GCM/NoPadding
应该在 Java 代码中指定,而不是AES/GCM/PKCS5Padding
。但是,SunJCE 提供者忽略PKCS5Padding
规范并且不填充,因此这没有影响。 - 对于 GCM,建议 nonce/IV 为 12 字节。在 Java(参考)代码中,位和字节混淆,因此使用了 96 字节 nonce/IV。由于对于 GCM,如果 nonce/IV 不是 12 个字节 (here),则重新计算 nonce/IV,实际上太长的 nonce/IV 由算法处理,因此不会导致解密失败PHP 代码,但仍然不符合建议的长度。