无法使用 "Libsodium-net" (C#) 打开由 TweetNacl (Java) 生成的 "SecretBox"
Cannot open "SecretBox" generated by TweetNacl (Java) with "Libsodium-net" (C#)
我在 C# 中使用 libsodium-net 库打开 Nacl SecretBox(使用 TweetNaclFast 库在 java 中生成)时遇到问题。
我也不能反其道而行之(使用 TweetNaclFast 打开 libsodium-net 生成的框)。
在下面的示例中,我将使用 TweetNaclFast (Java) 创建一个 SecretBox,并尝试使用 libsodium-net (C#) 打开它
创建 SecretBox (Java)
String secretMessage = "Hello Stack overflow!";
byte[] messageBytes = secretMessage.getBytes("UTF-8");
byte[] keyBytes = secureRandomGenerator(); //returns 32 random bytes (256 bits)
byte[] nonceBytes = TweetNaclFast.makeSecretBoxNonce();
byte[] boxBytes = new TweetNaclFast.SecretBox(keyBytes).box(messageBytes,nonceBytes);
System.out.println("Base64 box -> "+Base64.encodeBase64String(boxBytes));
System.out.println("Base64 key -> "+Base64.encodeBase64String(keyBytes));
System.out.println("Base64 nonce -> "+Base64.encodeBase64String(nonceBytes));
创作输出
Base64 box -> iNEpgwFIo6nyaLNgMpSWqwTQ9Z5y/y+BUXszXVFZ2gP2A3XJ0Q==
Base64 key -> FKpCo/AhRRUjdQIpzMbZSnnzfBx1e/Ni9VZyNWYEB8E=
Base64 nonce -> 2qngWbMLFVNiPTFqTVO9nsraB8ACIrwV
打开 SecretBox (C#)
string box = "iNEpgwFIo6nyaLNgMpSWqwTQ9Z5y/y+BUXszXVFZ2gP2A3XJ0Q==";
string key = "FKpCo/AhRRUjdQIpzMbZSnnzfBx1e/Ni9VZyNWYEB8E=";
string nonce = "2qngWbMLFVNiPTFqTVO9nsraB8ACIrwV";
try
{
byte[] message = Sodium.SecretBox.Open(
Convert.FromBase64String(box),
Convert.FromBase64String(nonce),
Convert.FromBase64String(key));
Console.WriteLine(Encoding.UTF8.GetString(message));
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
打开输出
Failed to open SecretBox
at Sodium.SecretBox.Open(Byte[] cipherText, Byte[] nonce, Byte[] key)
知道我可能做错了什么吗?
编辑
我猜问题出在其中一个库(最有可能是 libsodium-net)。
如果我用相同的变量创建一个 SecretBox,我会得到一个不同的盒子...
使用 TweetNaclFast 创建一个秘密盒子
String message = "Hello Stack overflow!";
String key = "uCEgauAQDWGDkcclGe1rNV6V77xtizuemhgxzM5nqO4=";
String nonce = "+RTDstWX1Wps5/btQzSMHWBqHU9s6iqq";
SecretBox box = new SecretBox(Base64.decodeBase64(key));
byte[] cipherText = box.box(message.getBytes("UTF-8"), Base64.decodeBase64(nonce));
RETURNS: yDCt/kOLFUWPZpV3deVNUZaH0ZHLVmj9Nvm8QlbVKPe1a/INDw==
使用 libsodium-net 创建一个秘密盒子
string message = "Hello Stack overflow!";
string key = "uCEgauAQDWGDkcclGe1rNV6V77xtizuemhgxzM5nqO4=";
string nonce = "+RTDstWX1Wps5/btQzSMHWBqHU9s6iqq";
byte[] box = Sodium.SecretBox.Create(Encoding.UTF8.GetBytes(message),
Convert.FromBase64String(nonce),
Convert.FromBase64String(key));
Console.WriteLine(Convert.ToBase64String(box));
RETURNS: AAAAAAAAAAAAAAAAAAAAAMgwrf5DixVFj2aVd3XlTVGWh9GRy1Zo/Tb5vEJW1Sj3tWvyDQ8=
我应该阅读文档 (RTFM)...
显然 libsodium-net 在密文的开头添加了一个 16 字节的身份验证标记 (https://bitbeans.gitbooks.io/libsodium-net/content/secret-key_cryptography/authenticated_encryption.html)。如果我删除前 16 个字节,我会得到与 TweetNaclFast SecretBox 相同的输出。
string message = "Hello Stack overflow!";
string key = "uCEgauAQDWGDkcclGe1rNV6V77xtizuemhgxzM5nqO4=";
string nonce = "+RTDstWX1Wps5/btQzSMHWBqHU9s6iqq";
byte[] box = Sodium.SecretBox.Create(Encoding.UTF8.GetBytes(message),
Convert.FromBase64String(nonce),
Convert.FromBase64String(key));
byte[] boxWithoutAuthenticationTag = new byte[box.Length - 16];
Array.Copy(box, 16, boxWithoutAuthenticationTag, 0, box.Length - 16);
Console.WriteLine(Convert.ToBase64String(boxWithoutAuthenticationTag));
现在 returns: yDCt/kOLFUWPZpV3deVNUZaH0ZHLVmj9Nvm8QlbVKPe1a/INDw==
要打开(解密)第一个示例的秘密盒子,请使用以下代码:
string box = "iNEpgwFIo6nyaLNgMpSWqwTQ9Z5y/y+BUXszXVFZ2gP2A3XJ0Q==";
string key = "FKpCo/AhRRUjdQIpzMbZSnnzfBx1e/Ni9VZyNWYEB8E=";
string nonce = "2qngWbMLFVNiPTFqTVO9nsraB8ACIrwV";
try
{
//Libsodium-net SecretBox.Open() requires a 16 byte authentication tag at the start of the ciphertext
//TweetNaclFast boxing method does not append a 16 byte authentication tag anywhere
//Thus, we need to add a 16 byte authentication tag at the start of ciphertext encripted by TweetNaclFast
byte[] authenticationTag = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; //Zeroed 16 Bytes Authentication Tag
byte[] tweetNaclFastCiphertextBytes = Convert.FromBase64String(box);
byte[] libsodiumNetLikeCiphertext = new byte[tweetNaclFastCiphertextBytes.Length + authenticationTag.Length];
Array.Copy(authenticationTag, libsodiumNetLikeCiphertext, authenticationTag.Length);
Array.Copy(tweetNaclFastCiphertextBytes, 0, libsodiumNetLikeCiphertext, authenticationTag.Length, tweetNaclFastCiphertextBytes.Length);
byte[] nonceBytes = Convert.FromBase64String(nonce);
byte[] keyBytes = Convert.FromBase64String(key);
Console.WriteLine(Encoding.UTF8.GetString(Sodium.SecretBox.Open(libsodiumNetLikeCiphertext, nonceBytes, keyBytes)));
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
现在应该 return Hello Stack overflow!
Sodium.SecretBox.Create
使用原始NaClcrypto_box()
API,需要在消息和密文前额外填充
这个 API 有点令人困惑,除了在 C 中很少有用。即使在 C 中,使用它的人最终也会编写包装器来添加或删除填充。
box
和 secretbox
结构,正如大多数 API 所公开的那样,不需要额外的填充。之前没有额外增加16字节,直接返回密文。消息可以直接给定,不用前置16个nul字节。
TweetNaclFast 不需要任何填充,但 libsodium-net 显然需要。
您使用 libsodium-net 观察到的密文之前的额外 16 个字节不包含任何有用的信息。这只是一堆零。您可以安全地删除它们,稍后在调用 Sodium.SecretBox.Open
.
时添加它们
请注意,与 Sodium.SecretBox
不同,Sodium.PublicKeyBox
不需要填充。
我在 C# 中使用 libsodium-net 库打开 Nacl SecretBox(使用 TweetNaclFast 库在 java 中生成)时遇到问题。 我也不能反其道而行之(使用 TweetNaclFast 打开 libsodium-net 生成的框)。
在下面的示例中,我将使用 TweetNaclFast (Java) 创建一个 SecretBox,并尝试使用 libsodium-net (C#) 打开它
创建 SecretBox (Java)
String secretMessage = "Hello Stack overflow!";
byte[] messageBytes = secretMessage.getBytes("UTF-8");
byte[] keyBytes = secureRandomGenerator(); //returns 32 random bytes (256 bits)
byte[] nonceBytes = TweetNaclFast.makeSecretBoxNonce();
byte[] boxBytes = new TweetNaclFast.SecretBox(keyBytes).box(messageBytes,nonceBytes);
System.out.println("Base64 box -> "+Base64.encodeBase64String(boxBytes));
System.out.println("Base64 key -> "+Base64.encodeBase64String(keyBytes));
System.out.println("Base64 nonce -> "+Base64.encodeBase64String(nonceBytes));
创作输出
Base64 box -> iNEpgwFIo6nyaLNgMpSWqwTQ9Z5y/y+BUXszXVFZ2gP2A3XJ0Q==
Base64 key -> FKpCo/AhRRUjdQIpzMbZSnnzfBx1e/Ni9VZyNWYEB8E=
Base64 nonce -> 2qngWbMLFVNiPTFqTVO9nsraB8ACIrwV
打开 SecretBox (C#)
string box = "iNEpgwFIo6nyaLNgMpSWqwTQ9Z5y/y+BUXszXVFZ2gP2A3XJ0Q==";
string key = "FKpCo/AhRRUjdQIpzMbZSnnzfBx1e/Ni9VZyNWYEB8E=";
string nonce = "2qngWbMLFVNiPTFqTVO9nsraB8ACIrwV";
try
{
byte[] message = Sodium.SecretBox.Open(
Convert.FromBase64String(box),
Convert.FromBase64String(nonce),
Convert.FromBase64String(key));
Console.WriteLine(Encoding.UTF8.GetString(message));
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
打开输出
Failed to open SecretBox
at Sodium.SecretBox.Open(Byte[] cipherText, Byte[] nonce, Byte[] key)
知道我可能做错了什么吗?
编辑
我猜问题出在其中一个库(最有可能是 libsodium-net)。 如果我用相同的变量创建一个 SecretBox,我会得到一个不同的盒子...
使用 TweetNaclFast 创建一个秘密盒子
String message = "Hello Stack overflow!";
String key = "uCEgauAQDWGDkcclGe1rNV6V77xtizuemhgxzM5nqO4=";
String nonce = "+RTDstWX1Wps5/btQzSMHWBqHU9s6iqq";
SecretBox box = new SecretBox(Base64.decodeBase64(key));
byte[] cipherText = box.box(message.getBytes("UTF-8"), Base64.decodeBase64(nonce));
RETURNS: yDCt/kOLFUWPZpV3deVNUZaH0ZHLVmj9Nvm8QlbVKPe1a/INDw==
使用 libsodium-net 创建一个秘密盒子
string message = "Hello Stack overflow!";
string key = "uCEgauAQDWGDkcclGe1rNV6V77xtizuemhgxzM5nqO4=";
string nonce = "+RTDstWX1Wps5/btQzSMHWBqHU9s6iqq";
byte[] box = Sodium.SecretBox.Create(Encoding.UTF8.GetBytes(message),
Convert.FromBase64String(nonce),
Convert.FromBase64String(key));
Console.WriteLine(Convert.ToBase64String(box));
RETURNS: AAAAAAAAAAAAAAAAAAAAAMgwrf5DixVFj2aVd3XlTVGWh9GRy1Zo/Tb5vEJW1Sj3tWvyDQ8=
我应该阅读文档 (RTFM)... 显然 libsodium-net 在密文的开头添加了一个 16 字节的身份验证标记 (https://bitbeans.gitbooks.io/libsodium-net/content/secret-key_cryptography/authenticated_encryption.html)。如果我删除前 16 个字节,我会得到与 TweetNaclFast SecretBox 相同的输出。
string message = "Hello Stack overflow!";
string key = "uCEgauAQDWGDkcclGe1rNV6V77xtizuemhgxzM5nqO4=";
string nonce = "+RTDstWX1Wps5/btQzSMHWBqHU9s6iqq";
byte[] box = Sodium.SecretBox.Create(Encoding.UTF8.GetBytes(message),
Convert.FromBase64String(nonce),
Convert.FromBase64String(key));
byte[] boxWithoutAuthenticationTag = new byte[box.Length - 16];
Array.Copy(box, 16, boxWithoutAuthenticationTag, 0, box.Length - 16);
Console.WriteLine(Convert.ToBase64String(boxWithoutAuthenticationTag));
现在 returns: yDCt/kOLFUWPZpV3deVNUZaH0ZHLVmj9Nvm8QlbVKPe1a/INDw==
要打开(解密)第一个示例的秘密盒子,请使用以下代码:
string box = "iNEpgwFIo6nyaLNgMpSWqwTQ9Z5y/y+BUXszXVFZ2gP2A3XJ0Q==";
string key = "FKpCo/AhRRUjdQIpzMbZSnnzfBx1e/Ni9VZyNWYEB8E=";
string nonce = "2qngWbMLFVNiPTFqTVO9nsraB8ACIrwV";
try
{
//Libsodium-net SecretBox.Open() requires a 16 byte authentication tag at the start of the ciphertext
//TweetNaclFast boxing method does not append a 16 byte authentication tag anywhere
//Thus, we need to add a 16 byte authentication tag at the start of ciphertext encripted by TweetNaclFast
byte[] authenticationTag = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; //Zeroed 16 Bytes Authentication Tag
byte[] tweetNaclFastCiphertextBytes = Convert.FromBase64String(box);
byte[] libsodiumNetLikeCiphertext = new byte[tweetNaclFastCiphertextBytes.Length + authenticationTag.Length];
Array.Copy(authenticationTag, libsodiumNetLikeCiphertext, authenticationTag.Length);
Array.Copy(tweetNaclFastCiphertextBytes, 0, libsodiumNetLikeCiphertext, authenticationTag.Length, tweetNaclFastCiphertextBytes.Length);
byte[] nonceBytes = Convert.FromBase64String(nonce);
byte[] keyBytes = Convert.FromBase64String(key);
Console.WriteLine(Encoding.UTF8.GetString(Sodium.SecretBox.Open(libsodiumNetLikeCiphertext, nonceBytes, keyBytes)));
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
现在应该 return Hello Stack overflow!
Sodium.SecretBox.Create
使用原始NaClcrypto_box()
API,需要在消息和密文前额外填充
这个 API 有点令人困惑,除了在 C 中很少有用。即使在 C 中,使用它的人最终也会编写包装器来添加或删除填充。
box
和 secretbox
结构,正如大多数 API 所公开的那样,不需要额外的填充。之前没有额外增加16字节,直接返回密文。消息可以直接给定,不用前置16个nul字节。
TweetNaclFast 不需要任何填充,但 libsodium-net 显然需要。
您使用 libsodium-net 观察到的密文之前的额外 16 个字节不包含任何有用的信息。这只是一堆零。您可以安全地删除它们,稍后在调用 Sodium.SecretBox.Open
.
请注意,与 Sodium.SecretBox
不同,Sodium.PublicKeyBox
不需要填充。