AES GCM 解密绕过 JAVA 中的身份验证

Posted

技术标签:

【中文标题】AES GCM 解密绕过 JAVA 中的身份验证【英文标题】:AES GCM decryption bypassing authentication in JAVA 【发布时间】:2018-08-20 02:03:50 【问题描述】:

我有一些 AES/GCM 加密数据并想对其进行解密。我想绕过身份验证对其进行解密,因为数据不包含身份验证信息(数据由第三方应用程序加密)。我尝试使用 javax.crypto 包解密,它总是抛出标签不匹配错误。有没有办法绕过这个标签检查和解密数据。数据采用 AES128 加密,使用 12 字节初始化向量。

编辑:我为这个问题找到了一个临时解决方案。不确定这是否是正确的方法。

            Key key = new SecretKeySpec(hlsKey, "AES");
            GCMParameterSpec gCMParameterSpec = new     GCMParameterSpec(96, initialisationVector);
            final Cipher c = Cipher.getInstance("AES/GCM/NoPadding", "BC");
            c.init(Cipher.DECRYPT_MODE, key, gCMParameterSpec);

            byte[] nodata = new byte[len * 2];
            System.arraycopy(cipherText, 0, nodata, 0, len);
            byte[] plaindata = new byte[len * 2];
            try  

                int decrypted_index = 0;
                while (decrypted_index < len) 
                    int cp = c.update(nodata, decrypted_index, nodata.length - decrypted_index, plaindata, decrypted_index);//doFinal(nodata);
                    decrypted_index += cp;
                
               if(decrypted_index>=len)
                   System.arraycopy(plaindata, 0, plainText, 0, len);
                   retvalue=1;
               
             catch (Exception e) 
                e.printStackTrace();
            

【问题讨论】:

描述没有任何意义。你说它是 AES-GCM 加密的。好的,这意味着它有一个身份验证标签。不支持零标签长度,最小长度(仅针对特殊情况)为32。然后您说它不包含身份验证信息。也许它没有使用 AES-GCM 加密。 @user1802492 也许您的意思是没有关联的数据,而不是它没有身份验证。没有身份验证的 GCM 模式毫无意义.. @JamesKPolk ,我从使用 AES/GCM 加密数据的第三方应用程序获取此数据,并且它不提供身份验证信息,我想对此进行解密以了解来自设备的实际数据。 我得到了这个问题的临时解决方案,不确定这是否正确 请将编辑放在答案而不是问题中。代码没问题,但前提是c.update 在验证身份验证标签之前总是返回明文数据。这是特定于实现的。它的内存效率也相对较低,但对于临时解决方案来说还可以。哦,是的,考虑到 while 循环内的测试,if(decrypted_index&gt;=len) 测试当然是废话; decrypted_index 必须是 &gt;= len 【参考方案1】:

是的,可以在没有身份验证标签的情况下解密消息:如果您阅读 GCM 规范,您会看到 CTR 的 IV 只是 IV,附加四个字节 00000002(即从零开始的计数器, 计算认证标签时加一,加密时计数器的起始值加一)。

这里是代码,我用它来验证我的计数器代码时,我做了两次inc;当然也可以简单地将最后一个字节设置为值0x02

package nl.owlstead.so;

import java.nio.charset.StandardCharsets;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.util.Arrays;

public class DecryptGCMWithoutVerification 

    private static final int TAG_SIZE = 128;

    public DecryptGCMWithoutVerification() 
        // TODO Auto-generated constructor stub
    

    public static void main(String[] args) throws Exception 

        // --- encryption using GCM

        Cipher gcm = Cipher.getInstance("AES/GCM/NoPadding");
        SecretKey key = new SecretKeySpec(new byte[16], "AES");
        byte[] ivBytes = new byte[12];
        GCMParameterSpec iv = new GCMParameterSpec(TAG_SIZE, ivBytes);
        gcm.init(Cipher.ENCRYPT_MODE, key, iv);
        byte[] ct = gcm.doFinal("owlstead".getBytes(StandardCharsets.US_ASCII));

        // --- decryption using underlying CTR mode

        Cipher ctr = Cipher.getInstance("AES/CTR/NoPadding");

        // WARNING: this is only correct for a 12 byte IV in GCM mode
        byte[] counter = Arrays.concatenate(ivBytes, new byte[4]);
        inc(counter);
        inc(counter);
        IvParameterSpec ctrIV = new IvParameterSpec(counter);
        ctr.init(Cipher.DECRYPT_MODE, key, ctrIV);

        byte[] pt = ctr.doFinal(ct, 0, ct.length - TAG_SIZE / Byte.SIZE);

        System.out.println(new String(pt, StandardCharsets.US_ASCII));
    

    private static final byte inc(byte[] counter) 
        for (int i = counter.length - 1; i >= 0; i--) 
            if (++counter[i] != 0) 
                return 0;
            
        
        return 1;
    


编辑:此代码用于无效标签或无法重新计算的标签(例如,AAD 可能丢失)。如果标签完全丢失,请从 doFinal 中删除 - TAG_SIZE / Byte.SIZE

编辑 2:请注意,这假定了 12 字节/96 位 IV,即 GCM 的默认 IV 大小。对于任何其他尺寸,您需要先计算 IV。

【讨论】:

你很幸运,因为你的 IV 是 12 字节。其他长度的 IV 需要额外的工作。 来自 Bouncy Castle 的 GCMBlockCipher 给出了如何计算所有 IV 长度的 CTR IV 的示例。足以实现类似于他们在 init() 中对 J0 所做的事情。然后你需要增加它,你可以使用SICBlockCipher来解密传递J0作为IV。 读者注意:如果不是很明显,这相当于用弯曲的钉子代替保险丝。当然,它可能会“起作用”,但你只是通过一个狡猾的黑客绕过了一个主要的安全功能,如果你的房子被烧毁或你的密码系统受到损害,那是你 100% 的错。

以上是关于AES GCM 解密绕过 JAVA 中的身份验证的主要内容,如果未能解决你的问题,请参考以下文章

如何在 GCM 模式下使用 AES 链接 BCryptEncrypt 和 BCryptDecrypt 调用?

使用 Java 8u20 进行慢速 AES GCM 加密和解密

在 JAVA 中使用 AES/GCM 检测不正确的密钥

Nodejs AES-256-GCM 通过 webcrypto api 解密加密的客户端消息

AES GCM 使用 web 微妙加密进行加密并使用颤振加密进行解密

如何使用 AES-GCM 对 C#.NET 加密()然后 JS WebCryptoApi 解密()?