Java: Oracle jre1.8.0_45
提供者: BC,BouncyCastle v1.52
密码: AES 256位密钥(已安装安全策略)
AEAD模式: GCM
Algo: AES/GCM/NoPadding
我有一个完美工作的AES加密/解密与上述参数。然后,在调试期间,我添加了一个简单的篡改模拟,在解密前更改加密缓冲区中的数据。我原以为会抛出一个AEADBadTagException,但它没有发生。我还没有使用任何upedateAAD(),我们讨论的是纯加密的数据负载。
我所做的篡改就像这样,在byte[]已经包含加密数据和身份验证标记的16个额外字节之后,我覆盖它的第一个字节。
// 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生成“?”诸若此类。普通消息的其余部分保持正确,只有解密输出的第一个字符似乎受到影响。
我从密码类的标准Java8doc中了解到,在AEAD模式下,身份验证标记是在加密时创建的(这是因为我在加密后的输出byte[]中看到了它),并在解密时进行了验证(我提供了完整的加密输出,包括16字节标记作为解密输入),如果标记不使用该数据(包括我现在不使用但将使用的AAD数据),它将抛出一个AEADBadTagException。在我的代码中,它不会那么做。
我尝试使用16字节的倍数的数据,以及不等于16字节的数据。两者的结果是一样的。如果使用相同的篡改(0x67)值,则纯文本中的第一个字母在消息变得更长时会发生更改,但这是有意义的。如果我向消息中添加一些字节,使其不是16的倍数,那么前面提到的错误的第一个字符'c‘变成'6’。在使用的AES/GCM/NoPadding中,长度不能是16的倍数。
这是否是对文档的错误理解,是否需要调用其他方法来“启用”此抛出行为(我可以找到任何抛出行为),或者BounceyCastle不会抛出它(我知道提供程序需要实现密码类ISP,以便所有东西都能像Java8DocsCiPHER类中描述的那样运行。
我无法与SunJCE提供者进行比较,因为它不支持AES/GCM/NoPadding。
有没有人有额外的信息。提亚
UPDATE 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提供程序未被注意到的情况下,将引发错误:
类java.security.InvalidAlgorithmParameterException:不支持的参数: 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)
发布于 2015-08-29 10:32:39
原来的职位有两个问题正在讨论中。其中一个问题已经解决( AEADBadTagException),另一个问题尚未解决(见原文中的更新29/8月)。
解决的问题是:我最近不得不为密钥/秘钥类的使用编写异常代码,而不是使用SecretKeySpec。该更改导致了一个影响代码遵循的路径的错误,并且在搜索不抛出AEADBadTagException的上下文中进行了更正。事实上,其余部分都继续工作,是因为流更改导致两次初始化加密,而不是第二次解密。我不明白的是,无论如何解密都是正确的。AES是一种对称算法,但它有一个S盒和反向S盒,因此不能像DES这样的完全对称密码那样使用加密来代替解密。
第二个问题仍未解决:
类java.security.InvalidAlgorithmParameterException:不受支持的参数: javax.crypto.spec.IvParameterSpec@4fccd51b
它可以由提供的代码复制,只需选择代码顶部的提供程序,其余的都保持不变。代码适用于BC,而不是SunJCE。
我看到有一个Metalogbook行可能必须在您自己的日志代码中进行更改。
虽然SunJCE提供程序不是我用于加密的提供者,但就我而言,GCM问题已经得到解决,我将继续跟踪这个问题,以便在需要时为SunJCE抛出提供更多的信息。
更新:通过进一步挖掘,我发现了IVParamSpec抛出的问题。BC为CBC和GCM接受此对象,并将身份验证标记默认为128位。另一方面,SunJCE特别需要一个GCMParamSpec对象和用于GCM的身份验证标记的显式大小,并接受IVParamSpec用于CBC,而不接受GCM。
https://stackoverflow.com/questions/32269928
复制相似问题