我们正在开发一个应用程序,该应用程序必须使用LoraWan(https://www.lora-alliance.org)加密的数据
我们已经找到了有关如何加密数据的文档,并且在过去几天(https://www.lora-alliance.org/sites/default/files/2018-04/lorawantm_specification_-v1.1.pdf)进行了仔细阅读,但目前仍无法解决我们的问题。
我们必须使用带有零填充的AES 128位ECB解密来解密消息,但是问题在于它无法正常工作,因为我们接收到的加密消息对于AES 128来说不够长,因此该算法返回“不是最后一行的完整块”。
我们收到的示例密钥如下:D6740C0B8417FF1295D878B130784BC5(不是真实密钥)。它的长度为32个字符,因此为32个字节,但是如果将其视为十六进制,则它将变为16个字节长,这是AES 128位所需的。这是我们用来从字符串转换十六进制的代码:
public static string HextoString(string InputText)
{byte[] hex= Enumerable.Range(0, InputText.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(InputText.Substring(x, 2), 16))
.ToArray();
return System.Text.Encoding.ASCII.GetString(hex);}
(上面的代码需要注意的一点是,我们不确定要使用哪种编码,因为我们在Lora文档中找不到它,他们也没有告诉我们,但是根据这个小的设置,我们可以搞砸了我们的解密(尽管我们已经尝试了所有可能的组合,例如ascii,utf8,utf7等)
我们收到的示例消息是:d3 73 4c,我们假设它也是十六进制的。这只有6个字节,如果将其从六进制转换为普通,则只有3个字节,而与密钥长度匹配,我们需要最少16个字节。
这是我们正在使用的Aes 128解密的代码:
private static string Aes128Decrypt(string cipherText, string key){
string decrypted = null;
var cipherPlainTextBytes = HexStringToByteArray(cipherText);
//var cipherPlainTextBytes = ForcedZeroPadding(HexStringToByteArray(cipherText));
var keyBytes = HexStringToByteArray(key);
using (var aes = new AesCryptoServiceProvider())
{
aes.KeySize = 128;
aes.Key = keyBytes;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.Zeros;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream(cipherPlainTextBytes, 0, cipherPlainTextBytes.Length))
{
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
decrypted = sr.ReadToEnd();
}
}
}
}
return decrypted;}
因此,很显然,这将在sr.ReadToEnd()处返回“数据是不完整的块”。
从示例中可以看到,在注释掉的那一行中,我们还尝试了使用适当长度的完整零字节数组(16-cipherText)将文本“填充”到正确的大小。该算法运行良好,但返回的是完整的乱码而不是原始文本。
我们已经尝试了所有操作模式,并且还尝试了填充模式。他们没有为我们提供任何信息,而是cipherText和该文本的密钥。也没有初始化向量,因此我们假设每次都应该生成该向量(但是对于ECB,甚至不需要iirc)
更重要的是,他们能够对消息进行加密-解密。对此最令人困惑的是,我已经在谷歌上搜索了好几天,在解密期间,我在google上找不到CIPHERTEXT比密钥短的单个示例。
很明显,我发现了一些示例,其中正在加密的消息比所需消息短,但这就是加密侧的填充内容(对吗?)。这样,当您收到填充的消息时,可以告诉算法使用哪种填充模式来使其长度正确,从而可以将填充与消息分开。但是在所有这些情况下,解密期间收到的消息的长度都是正确的。
问题是-我们在做什么错?有什么方法可以用比密钥短的密文解密?还是他们通过产生太短的密码弄乱了某个地方?
感谢您的帮助。
答案 0 :(得分:2)
在AES-ECB中,唯一小于16字节的有效密文为空。 16字节的限制是AES的块(不是密钥)大小,恰好与AES-128的密钥大小匹配。
因此,问题的
我们收到的示例消息是:d3 73 4c
不显示ECB加密消息(因为comment告诉它来自JSON,所以不能是恰巧显示为十六进制的字节)。对于Join-Accept,这太短了,不足以成为FRMPayload(根据此comment),因为spec谈到后面的内容:
1625
消息长度为16或32个字节。
难道JSON消息包含的内容不是完整的FRMPayload,而是数据包的片段,编码为带有空格分隔符的十六进制对吗?只要不知道如何构建FRMPayload,就没有必要对其进行解密。
更新:如果该神秘消息始终为3个字节,并且对于给定密钥始终相同(或每个密钥一次可用),则根据Maarten Bodewes的{{3} }可能是关键检查值。 KCV通常是每个原始数据块密码(等效地:每个ECB)使用密钥对全零值加密的前3个字节。 comment可以完全脱机工作(这对于不公开密钥是必需的),并且可以用来手动验证假设。它告诉我们对于问题中给出的16字节密钥,KCV将为cd15e1
(或下一部分中的每个变体形式为c076fc
)。
此外:CreateDecryptor
用于制作负责ECB解密的Gizmo。在对LoraWan有效负载进行解密的情况下,这可能是不正确的,这需要ECB encryption 来对某些字段进行解密:
1626
注:ECB模式下的AES解密操作用于加密加入接受的消息,以便最终设备可以使用AES加密操作来解密消息。这样一来,终端设备仅需实现AES加密,而无需实现AES解密。
在解密LoraWan数据包的情况下,您想使用字节数组而不是字符串与AES引擎进行通信。字符串具有编码,而LoraWan密文和相应的明文则没有。 Herbert Hanewinkel's javascript AES似乎已经成功地强迫了不错的.NET全功能加密API来完成底层工作。
在HextoString
代码中,我含糊其词意为,也许结果是hex
成为了原来的十六进制输入,作为字节数组(完全摆脱了十六进制和其他编码的罪恶;在这种情况下,变量hex
应该重命名为pure_bytes
。但是后来我迷茫了System.Text.Encoding.ASCII.GetString(hex)
。如果它只是从字节数组创建字节字符串,或者将密钥转回十六进制以供以后在HexStringToByteArray
中输入Aes128Decrypt
,我会感到惊讶。加Others使我担心[0x80..0xFF]中的任何字节都可能会变成0x3F,这对于密钥,密文和相应的LoraWan有效负载来说是不好的。取消十六进制时,它们没有字符编码。
我的结论是,如果HexStringToByteArray
遵循其名称的含义,并且考虑到Aes128Decrypt
的当前接口,HextoString
应该只是删除空格(如果HexStringToByteArray
则不需要即时删除空白)。但是我的建议是更改接口以使用字节数组,而不是字符串(请参见上一节)。
顺便说一句:从其密钥创建ICryptoTransform对象应该多次执行该对象。