I'm having problems opening a Nacl SecretBox (generated in java using the TweetNaclFast library) in C# using the libsodium-net library. I also can't do it the other way around (open a libsodium-net generated box using TweetNaclFast).
In the following example i'll create a SecretBox using TweetNaclFast (Java) and try to open it with libsodium-net (C#)
Creating the 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));
Creation Output
Base64 box -> iNEpgwFIo6nyaLNgMpSWqwTQ9Z5y/y+BUXszXVFZ2gP2A3XJ0Q==
Base64 key -> FKpCo/AhRRUjdQIpzMbZSnnzfBx1e/Ni9VZyNWYEB8E=
Base64 nonce -> 2qngWbMLFVNiPTFqTVO9nsraB8ACIrwV
Opening the 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);
}
Open Output
Failed to open SecretBox
at Sodium.SecretBox.Open(Byte[] cipherText, Byte[] nonce, Byte[] key)
Any idea about what i might be doing wrong?
EDIT
I guess the problem is with one of the libraries (libsodium-net most likely). If i create a SecretBox with the same variables i get a different box...
Creating a Secret Box with 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==
Creating a Secret Box with 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=
Sodium.SecretBox.Create
uses the original NaCl crypto_box()
API, which requires extra padding before the message and the ciphertext.
This API is a bit confusing and is rarely useful except in C. Even in C, people using it end up writing wrappers to prepend or get rid of the padding.
The box
and secretbox
constructions, as exposed by most APIs, do not require extra padding. The ciphertext is directly returned without 16 extra bytes before. The message can be directly given without prepending 16 nul bytes.
TweetNaclFast does not require any padding, but libsodium-net apparently does.
The extra 16 bytes before the ciphertext you are observing with libsodium-net do not contain any useful information. It's just a bunch of zeros. You can safely strip them, and add them later when calling Sodium.SecretBox.Open
.
Note that unlike Sodium.SecretBox
, Sodium.PublicKeyBox
doesn't require padding.