在之前的一篇博文中简单介绍了使用 C# 链接 AD 的方法,点击这里
其中用户姓名(DictionaryString)、用户ID(OctetString)属于不同的数据类型。
DictionaryString 可以直接被程序解析成 string 类型。
然而程序接收到的 OctetString 数据却是一组字节流,我们需要将其转换为 16 进制字符串。
文章目录
C# 字节流与 Guid
当我们解析字节流为 Guid 的时候,往往需要注意一下字节的顺序。
字节序
常见序有两种:
- Little endian:将低序字节存储在起始地址
- Big endian:将高序字节存储在起始地址
比方说有一个四字节数字 0x08070605
如果使用 Little endian 的方式存储,它存在的形式就是 05 06 07 08
如果使用 Big endian 的方式存储,它存在的形式就是 08 07 06 05
dotnet 中的 Guid 结构使用的混合字节序
dotnet 的 Guid 使用的是一种混合字节序
Guid 实际上分为五个部分 ,并使用 - 分隔
例如 2cb7d683-17f2-434e-9c9e-7e945da33106
在转换为字节流时,前三个部分使用反序,后两个使用正序
字节流转换为 Guid 字符串
C# 提供简单的方法
Guid guid = new Guid(bytes);
如果是自己解析,则可以参考以下代码:
/// <summary>
/// 字节流转 Guid
/// </summary>
/// <param name="GUID"></param>
/// <returns></returns>
private string ByteArrToString(byte[] GUID)
{
String strGUID = "";
strGUID = "";
strGUID = strGUID + ByteToHexString((int)GUID[3] & 0xFF);
strGUID = strGUID + ByteToHexString((int)GUID[2] & 0xFF);
strGUID = strGUID + ByteToHexString((int)GUID[1] & 0xFF);
strGUID = strGUID + ByteToHexString((int)GUID[0] & 0xFF);
strGUID = strGUID + "-";
strGUID = strGUID + ByteToHexString((int)GUID[5] & 0xFF);
strGUID = strGUID + ByteToHexString((int)GUID[4] & 0xFF);
strGUID = strGUID + "-";
strGUID = strGUID + ByteToHexString((int)GUID[7] & 0xFF);
strGUID = strGUID + ByteToHexString((int)GUID[6] & 0xFF);
strGUID = strGUID + "-";
strGUID = strGUID + ByteToHexString((int)GUID[8] & 0xFF);
strGUID = strGUID + ByteToHexString((int)GUID[9] & 0xFF);
strGUID = strGUID + "-";
strGUID = strGUID + ByteToHexString((int)GUID[10] & 0xFF);
strGUID = strGUID + ByteToHexString((int)GUID[11] & 0xFF);
strGUID = strGUID + ByteToHexString((int)GUID[12] & 0xFF);
strGUID = strGUID + ByteToHexString((int)GUID[13] & 0xFF);
strGUID = strGUID + ByteToHexString((int)GUID[14] & 0xFF);
strGUID = strGUID + ByteToHexString((int)GUID[15] & 0xFF);
return strGUID;
}
/// <summary>
/// 十进制 转 十六进制字符串
/// </summary>
/// <param name="kb"></param>
/// <returns></returns>
private static string ByteToHexString(int kb)
{
byte[] bytes = new byte[1];
bytes[0] = (byte)(kb & 0xFF);
string S = Convert.ToHexString(bytes);
return S;
}
Guid 字符串转为字节流
C# 提供简单的方法
Guid guid = new Guid(guidStr);
byte[] guidBytes = guid.ToByteArray();
自己解析可以参照字节流转 Guid 的方法做一个反向的操作。
LDAP 根据 OctetString 类型字段做查询
当我们想通过 Guid 字符串去查询信息的时候,会发现根本查询不到我们想要的信息。
比如 2cb7d683-17f2-434e-9c9e-7e945da33106 是小张的信息,但是当我们在过滤条件中加入 (obejctGuid = 2cb7d683-17f2-434e-9c9e-7e945da33106) 时,我们却查询不出任何东西。
使用 Acticty Directory Explorer 工具去查询,我们会发现这个工具将我们的条件进行了转换,即将 2cb7d683-17f2-434e-9c9e-7e945da33106 转换为了 \83\D6\B7\2C\F2\17NC\9C\9E\7E\94\5D\A31\06
所以我们要找一下其中的转换规则。
对比及解析
将 Guid 按照原来的顺序重新排序后,不难发现, \ 后面跟两位其实就是我们的十六进制字符串。与转换后的不同点在于红框中的几个值:
然而,十六进制的 4E 正好对应 ASCII 码的 N,43 对应 C ,31 对应 1。我们不妨将所有字节都转换成 ASCII 码看一看:
结论很明显,转换后的 ASCII 码只保留了字母和数字。
总结来说,转换时需要遵循以下规则:
- 按照 Guid 的字节序规则,8-4-4-4-12 分对应 反序-反序-反序-正序-正序 去排列
- 将十六进制转换为 ASCII 码 ,如果转换之后是字母或者数字,则保留 ASCII 码,否则保留十六进制字符串,并在前面加 \ 标识。
代码实现
private List<string> validChar= new List<string>{
"0","1","2","3","4","5","6","7","8","9"
,"A","B","C","D","E","F","G","H","I","J","K","L","M","N"
,"O","P","Q","R","S","T","U","V","W","X","Y","Z"
,"a","b","c","d","e","f","g","h","i","j","k","l","m","n"
,"o","p","q","r","s","t","u","v","w","x","y","z"
};
/// <summary>
/// Guid 转化为查询条件需要的结构
/// </summary>
/// <param name="guid"></param>
/// <returns></returns>
private string GuidConvertForFilter(string guid)
{
string guidnew = guid.Replace("-", "");
string result = "";
result += ConvertHexToAscii(guidnew.Substring(6, 2));
result += ConvertHexToAscii(guidnew.Substring(4, 2));
result += ConvertHexToAscii(guidnew.Substring(2, 2));
result += ConvertHexToAscii(guidnew.Substring(0, 2));
result += ConvertHexToAscii(guidnew.Substring(10, 2));
result += ConvertHexToAscii(guidnew.Substring(8, 2));
result += ConvertHexToAscii(guidnew.Substring(14, 2));
result += ConvertHexToAscii(guidnew.Substring(12, 2));
result += ConvertHexToAscii(guidnew.Substring(16, 2));
result += ConvertHexToAscii(guidnew.Substring(18, 2));
result += ConvertHexToAscii(guidnew.Substring(20, 2));
result += ConvertHexToAscii(guidnew.Substring(22, 2));
result += ConvertHexToAscii(guidnew.Substring(24, 2));
result += ConvertHexToAscii(guidnew.Substring(26, 2));
result += ConvertHexToAscii(guidnew.Substring(28, 2));
result += ConvertHexToAscii(guidnew.Substring(30, 2));
return result;
}
/// <summary>
/// 十六进制转ASCII码(ASCII码只保留字母和数字)
/// </summary>
/// <param name="hex"></param>
/// <returns></returns>
private string ConvertHexToAscii(string hex) {
try
{
// 十六进制字符串转Int
Int32 bt = ConvertHexToInt(hex);
// Int转ASCII码
char c = Convert.ToChar(bt);
string d = c.ToString();
if (validChar.Contains(d))
{
return d;
}
else
{
return "\\" + hex;
}
}
catch (Exception ex)
{
throw new Exception("invalid hexString");
}
}
/// <summary>
/// 十六进制字符串转Int32
/// </summary>
/// <param name="kb"></param>
/// <returns></returns>
private static Int32 ConvertHexToInt(string kb)
{
Int32 S = Convert.ToInt32(kb, 16);
return S;
}