从 Java 负载中解密 PHP 中的 AES/GCM/PKCS5Padding

如何解决从 Java 负载中解密 PHP 中的 AES/GCM/PKCS5Padding

我想从 SecureGMC java 做一个简单的加密,然后使用 AES/GCM/PKCS5Padding 在 PHP 中解密。我假设要解密的数据来自本地银行,它强调 IV_SIZE=96 和 TAG_BIT_LENGTH=128;他们向我推荐了这个 link 作为参考。从我读到的内容来看,openssl_decrypt() 似乎是最好的选择,但是,我在解密 Java 时找不到任何可供我发送的 $tag 变量. openssl_decrypt() 甚至推荐用于此吗?

密码.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,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,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,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,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(仅解密) - 我已隔离到一个新类中,用于仅处理解密数据以模拟生产。

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,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,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 padding scheme " + badPaddingExc) ; System.exit(1); }

            return plainTextInByteArr ;
    }
}

结果(成功)

DECRYPTED TEXT- Testing message to dencrypt

然而,我的斗争是在解密.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,$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、密文、标签判断错误。

Java 代码中的

Cipher#doFinal() 隐式连接密文和标签 (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,strlen($encrypt) - $tagLength);

$ciphertext_raw = openssl_decrypt($ciphertext,'aes-256-gcm',$key,OPENSSL_RAW_DATA,$iv,$tag,$aad);
print($ciphertext_raw);

使用 Java 代码生成测试数据的地方。

Java 代码为参考代码,不可更改。不过,需要注意以下几点:

  • GCM 是一种流密码模式,不使用填充。因此,应在 Java 代码中指定 AES/GCM/NoPadding 而不是 AES/GCM/PKCS5Padding。但是,SunJCE 提供程序会忽略 PKCS5Padding 规范并且不会填充,因此这没有影响。
  • 对于 GCM,建议使用 12 字节的 nonce/IV。在 Java(参考)代码中,位和字节混淆,因此使用了 96 字节的 nonce/IV。由于对于 GCM,如果 nonce/IV 不是 12 个字节(here),则重新计算,因此算法处理了实际上太长的 nonce/IV,因此不会导致 PHP 代码中的解密失败,但是仍然不符合推荐的长度。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)> insert overwrite table dwd_trade_cart_add_inc > select data.id, > data.user_id, > data.course_id, > date_format(
错误1 hive (edu)> insert into huanhuan values(1,'haoge'); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive> show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 <configuration> <property> <name>yarn.nodemanager.res