【问题标题】:How to make GCM Encrypt with authentication tag for Android如何使用 Android 的身份验证标签制作 GCM 加密
【发布时间】:2017-11-09 14:03:42
【问题描述】:

我想通过 GCM 模式使用 Android 的身份验证标签来加密数据。 这是我的源代码:

public static byte[] GCMEncrypt(String hexKey, String hexIV, byte[] aad) throws Exception {
        byte[] aKey = hexStringToByteArray(hexKey);
        byte[] aIV = hexStringToByteArray(hexIV);
        Key key = new SecretKeySpec(aKey, "AES");
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(16 * Byte.SIZE, aIV));
        cipher.updateAAD(aad);
        byte[] encrypted = cipher.doFinal(aKey);
        return encrypted;
    }

如何在这段代码中插入认证标签?

【问题讨论】:

    标签: android encryption aes-gcm


    【解决方案1】:

    您已经包含了身份验证标签。 Java(不幸的是 - 见下文)在密文中包含身份验证标签。这意味着它是在最后一次调用 doFinal 期间生成的。

    您可以通过查看密文的大小轻松检查这一点。它应该与明文(在本例中为aKey)大小相同,加上 128 位,这是身份验证标签的默认和合理大小。

    这也是为什么AEADBadTagException 派生自BadPaddingException 的原因:在解密期间,验证发生在隐式。所以旧代码仍然与 GCM 模式兼容。


    正如我之前指出的,我认为在密文中包含身份验证标签是 API 的主要设计错误:

    • 它不允许用户将身份验证标签放在别处 - 至少没有数组(大小)操作;
    • 它消除了 GCM 的 在线 特性,其中明文的解密是瞬时的(对于每次更新调用),因为需要缓冲身份验证标签;
    • 如果预先知道认证标签的位置(即协议直接指定标签位置或密文的大小),它会使代码更大、效率更低,并且需要虚假缓冲;

    在我看来,这并不能体现在不添加方法和保持兼容性的情况下改造 Cipher 类的优势。如果设计者只是添加了分别检索和验证身份验证标签的方法会更好。


    因为现在正在下雨,所以我创建了一个示例:

    public static void main(String... args) throws Exception {
        int tagSize = 96;
    
        Cipher gcm = Cipher.getInstance("AES/GCM/NoPadding");
    
        SecretKey aesKey = new SecretKeySpec(new byte[16], "AES");
    
        GCMParameterSpec gcmSpec = new GCMParameterSpec(tagSize, new byte[gcm.getBlockSize()]);
    
        gcm.init(Cipher.ENCRYPT_MODE, aesKey, gcmSpec);
    
        byte[] pt = "Maarten Bodewes creates code".getBytes(StandardCharsets.UTF_8);
        System.out.println(pt.length);
        byte[] ctAndTag = new byte[gcm.getOutputSize(pt.length)];
    
        System.out.println(ctAndTag.length);
    
        int off = 0;
        off += gcm.update(pt, 0, pt.length, ctAndTag, off);
        // prints 16 (for the Oracle crypto provider)
        // meaning it is not online, buffering even during encryption
        System.out.println(off);
        off += gcm.doFinal(new byte[0], 0, 0, ctAndTag, off);
        // prints 40 for the Oracle crypto provider, meaning it doesn't *just*
        // output the tag during doFinal !
        System.out.println(off);
    
        int ctSize = ctAndTag.length - tagSize / Byte.SIZE;
        System.out.println(ctSize);
    
        byte[] ct = Arrays.copyOfRange(ctAndTag, 0, ctSize);
        byte[] tag = Arrays.copyOfRange(ctAndTag, ctSize, ctAndTag.length);
    
        System.out.println(Hex.toHexString(ct));
        System.out.println(Hex.toHexString(tag));
    }
    

    注意事项:

    • 用自己加密密钥没有意义,我希望这只是为了演示目的
    • 对于较小的身份验证标签,GCM 很快就会失去安全性,因此不建议更改大小,除非您的协议是专门为它设计的。

    【讨论】:

    • 感谢您的回答。我的 API 需要 2 个参数:加密和身份验证标签。但是,在我的代码中,我无法获得身份验证标签。你有示例代码吗?
    • 您只需要剪切密文的最后一部分,在本例中为 128 位或 16 字节。你可以把一个字节数组分成两部分,对吧?
    • 非常感谢!
    • Android中有void main
    • @IgorGanapolsky 可能不是,但由于 Android 完全兼容这种代码,否则不会有太大的不同,不是吗?我可以处理启动事件,但我想代码足够清晰。
    猜你喜欢
    • 2020-05-29
    • 2022-01-04
    • 2016-08-24
    • 2014-07-14
    • 2015-10-29
    • 1970-01-01
    • 2012-03-30
    • 2019-02-15
    • 2013-07-17
    相关资源
    最近更新 更多