JavaScript与C#编码QNAP NAS密码时特殊字符输出差异排查
The core issue here is a mismatch in how you're converting the intermediate string back to bytes in your C# code. Let's break this down step by step:
Root Cause
Your Utf16to8 function correctly converts the input UTF-16 string to UTF-8 bytes, then wraps each byte into a char (resulting in a string of U+00xx characters where xx is the original UTF-8 byte value).
However, when you use Encoding.UTF8.GetBytes(utf8Input) on this string, you're re-encoding those U+00xx characters as UTF-8. For characters in the U+0080 to U+00FF range (like à = U+00C3 or ¤ = U+00A4), UTF-8 uses two bytes instead of one. This doubles the number of bytes for non-ASCII characters, leading to a longer Base64 output than the JavaScript version.
In contrast, QNAP's JavaScript script uses btoa, which takes each character's low 8 bits directly as a byte (treating the string as Latin-1/ISO-8859-1 encoded). This preserves the original UTF-8 byte sequence, so the Base64 output matches the expected result.
Solution
You have two straightforward fixes to align your C# code with the JavaScript behavior:
Option 1: Skip the intermediate string step (simplest)
Directly convert the input string to UTF-8 bytes and encode to Base64, cutting out the unnecessary Utf16to8 function entirely:
public static class EncodeHelper { public static string Encode(string input) { byte[] utf8Bytes = Encoding.UTF8.GetBytes(input); return Convert.ToBase64String(utf8Bytes); } }
This works because QNAP's API expects the password to be UTF-8 encoded then Base64'd, which is exactly what this code does.
Option 2: Use Latin-1 encoding for the intermediate string
If you need to keep the Utf16to8 logic (for consistency with your existing code structure), replace Encoding.UTF8.GetBytes with Encoding.GetEncoding("ISO-8859-1").GetBytes. Latin-1 maps each U+00xx character directly to a single byte, preserving the original UTF-8 sequence:
public static class EncodeHelper { private const string EzEncodechars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; public static string Utf16to8(string input) { byte[] utf16Bytes = Encoding.Unicode.GetBytes(input); byte[] utf8Bytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, utf16Bytes); char[] chars = (char[])Array.CreateInstance(typeof(char), utf8Bytes.Length); for (int i = 0; i < utf8Bytes.Length; i++) { chars[i] = BitConverter.ToChar(new byte[2] { utf8Bytes[i], 0 }, 0); } return new string(chars); } public static string Encode(string input) { var utf8Input = Utf16to8(input); var plainTextBytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(utf8Input); return Convert.ToBase64String(plainTextBytes); } }
Verification
For the input äçéè:
- Both fixes will produce the Base64 string
w6TDp8Opw6g=, which matches the JavaScript output from QNAP'sget_sid.js. - Your original C# code produced a longer string because it was re-encoding the intermediate string with UTF-8, expanding each non-ASCII character to two bytes.
内容的提问来源于stack exchange,提问作者Cornelis

