Java:Oracle jre1.8.0_45
提供者:BC,BouncyCastle v1.52
密码:AES 256位密钥(已安装安全策略)
AEAD模式:GCM
Algo:AES / GCM / NoPadding
我有一个完美的AES加密/解密功能,其参数如上所示。然后在调试期间,我通过在解密之前更改加密缓冲区中的数据添加了一个简单的TAMPERING模拟。我期望抛出AEADBadTagException,但它没有发生。我没有使用任何upedateAAD(),我们正在讨论纯加密数据的有效负载。
我就像这样篡改,在它已经包含加密数据和认证标记的16个额外字节之后,我覆盖了byte []的第一个字节。
// set-up for encryption, key, IV, etc...
...
try
{
String sPlainText="The non-encrypted (AES) message.";
byte[] baEncrypted=oCIPH.doFinal(sPlainText.getBytes());
MetaLogbook.info(baEncrypted); // Shows well encrypted buffer
// Tampering simulations
baEncrypted[0]=0x67;
// re-initialize for decryption, same key and IV...
String sDecryptedText=new String(oCIPH.doFinal(baEncrypted),"UTF-8");
MetaLogbook.info(sDecryptedText);
// The above log line shows the plain text with a different first letter
// each time that i change 0x67 in other values. The rest of the message
// matches the plain text on input. I can see the 16 extra bytes of the
// authentication tag appended to the clear text.
}
catch(Exception e)
{
// I expected to come here due to a AEADBadTagException but I never
// come here.
MetaLogbook.error(e);
}
生成的解密文本在第一个字符处发生更改,因为我在模拟篡改时更改了我指定的值。它以非线性方式变化。当0x65产生' c时,0x67产生一个'?'等等。其余的明文消息仍然正确,只有解密输出的第一个字符才会受到影响。
我从Cipher类的标准Java 8 doc中了解到,在AEAD GCM模式下,身份验证标记是在加密时创建的(这是因为我在最后附加的加密输出byte []中看到它)并且已经过验证在解密时(我提供了加密的完整输出,包括16字节标记作为解密输入),如果标签不能验证该数据(包括我现在不会使用的AAD数据,那么它会抛出) AEADBadTagException。在我的代码中,它没有那样做。
我尝试使用16字节倍数的数据以及不是16字节的数据。两者的结果相同。如果使用相同的篡改(0x67)值,则如果消息变长但有意义,则纯文本输出中的第一个字母会发生变化。提到的错误的第一个字符' c'成为一个' 6'如果我在消息中添加一些字节,使其不是16的倍数。在使用的AES / GCM / NoPadding中,长度不能是16的倍数。
这是对文档的误解,是否需要调用其他方法来启用'这个抛出行为(我可以找到任何),或者没有BounceyCastle抛出它(我知道提供者需要实现加密类ISP,以便所有行为都如Java 8 Docs Cipher类中所述。
我无法与SunJCE提供商进行比较,因为它不支持AES / GCM / NoPadding。
有没有人有额外的信息。 TIA
更新29 / AUG:添加代码以显示相同的代码抛出SunJCE而不是BC提供者作为评论讨论的一部分。
private static void testing()
{
try
{
// Unremark these lines to see it work
//Security.addProvider(new BouncyCastleProvider()); // "BC"
//Cipher oCIPH=Cipher.getInstance("AES/GCM/NoPadding", "BC");
// Unremark these lines to see it fail
Cipher oCIPH=Cipher.getInstance("AES/GCM/NoPadding", "SunJCE");
// Make a quick and dirty IV and Symmetric Key
byte[] baIV="EECE34808EF2A9AC".getBytes("UTF-8");
byte[] baKey="010F05E3E0104EB59D10F37EA8D4BB6B".getBytes("UTF-8");
// Make IV and Key (well KeySpec for AES) object. Use IV parspec because
// defaults to 128bit Authentication tag size & works in both GCM & CBC.
IvParameterSpec ps=new IvParameterSpec(baIV);
SecretKeySpec sk=new SecretKeySpec(baKey,"AES");
// Unremakr one line, either shrtline (multiple of 16 bytes) or long line
//String sPlainText="The non-encrypted (AES) message.";
String sPlainText="The non-encrypted (AES) message. Everything after the . makes this NOT a multiple of 16 bytes.";
// Encrypt
oCIPH.init(Cipher.ENCRYPT_MODE, sk, ps);
byte[] baEncrypted=oCIPH.doFinal(sPlainText.getBytes());
// Decrypt
oCIPH.init(Cipher.DECRYPT_MODE, sk, ps);
String sDecryptedText=new String(oCIPH.doFinal(baEncrypted),"UTF-8");
}
catch(Exception e)
{
MetaLogbook.log("Security Tools Exception",e);
}
}
上面的代码可以通过SunJCE或BouncyCastle运行,无需标记顶部的所需行。在BC中,这些代码运行并执行预期的操作。如果SunJCE提供程序未标记,则会引发错误:
class java.security.InvalidAlgorithmParameterException:Unsupported 参数:javax.crypto.spec.IvParameterSpec@4fccd51b com.sun.crypto.provider.CipherCore.init(CipherCore.java:509) com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:339) javax.crypto.Cipher.init(Cipher.java:1394)javax.crypto.Cipher.init (Cipher.java:1327)
答案 0 :(得分:1)
原帖有两个问题正在讨论中。一个已经解决(AEADBadTagException),另一个仍然未决(参见原始帖子中的UPDATE 29 / AUG)。
解决的问题: 我最近为使用AES的Key / SecretKey类的使用编写了异常代码,而不是使用SecretKeySpec。该更改引发了一个错误,该错误影响了代码所遵循的路径,并且现在在搜索非抛出AEADBadTagException的上下文中进行了更正。所有其余部分继续工作的事实是因为流量变化导致第二次初始化加密而不是解密。 我不明白的是,解密无论如何都能正常工作。 AES是一种对称算法,但它有一个S-Box和反向S-Box,人们会认为加密不能像DES这样的完全对称密码一样用来代替解密。
第二个问题仍未解决:
class java.security.InvalidAlgorithmParameterException:不支持的参数:javax.crypto.spec.IvParameterSpec@4fccd51b
只需选择代码顶部的提供程序并保持其余部分保持不变,即可通过提供的代码进行复制。该代码适用于BC,而不适用于SunJCE。
我看到有一个Metalogbook系列可能需要在您自己的日志代码中进行更改。
虽然SunJCE提供程序不是我用于加密的,但GCM问题已解决,就我而言,我会继续关注这个问题,以便在需要时为SunJCE throw提供更多信息。
更新:通过进一步挖掘,我发现了IVParamSpec抛出的问题。 BC为CBC和GCM接受此对象,并将身份验证标记默认为128位。另一方面,SunJCE需要专门的GCMParamSpec对象和GCM身份验证标记的显式大小,并接受CPa的IVParamSpec,但不接受GCM。