使用Azure CLI加密字符串后,在C#中调用Azure Managed HSM解密失败的问题排查
我来帮你排查这个问题!你遇到的核心矛盾很清晰:用Azure CLI加密得到的结果,CLI自己能解密,C# SDK自己加密解密也没问题,但CLI加密的结果拿到C#里就报错。你猜的方向完全没错——问题确实出在JSON里的iv、result、tag的解码方式上。
问题定位
先看你用到的Azure CLI加密命令:
az keyvault key encrypt --id "https://myhsmtest.managedhsm.azure.net/keys/aes256/1234aed127f8009e15d6c3a883b91f21" --algorithm A256GCM --value "123" --data-type plaintext
它返回的JSON里,iv、result、tag都是十六进制字符串,而不是Base64字符串!但你在C#代码里用了Convert.FromBase64String()去转换这些值,这就导致生成的字节数组完全错误,HSM自然会返回"Invalid input data/params"的错误。
对比一下就能明白:C# SDK的EncryptResult返回的Ciphertext、Iv、AuthenticationTag都是Base64编码的字节数组,所以你用Base64转换没问题;但Azure CLI的输出是十六进制格式,这就是两者的关键差异。
解决方案
把十六进制字符串转换成字节数组即可。.NET 5及以上版本提供了Convert.FromHexString()方法可以直接用,如果你用的是更低版本,也可以自己实现一个十六进制转字节的工具方法。
修改后的C#代码
var json = "{\n \"aad\": null,\n \"algorithm\": \"A256GCM\",\n \"iv\": \"8f7f424c8548bac26c947bfd\",\n \"kid\": \"https://myhsmtest.managedhsm.azure.net/keys/aes256/1234aed127f8009e15d6c3a883b91f21\",\n \"result\": \"PtLm\",\n \"tag\": \"e96de8eee3e194529095ef196f54cef0\"\n}"; var jsonDocument = JsonDocument.Parse(json); var cipherText = jsonDocument.RootElement.GetProperty("result").GetString(); var iv = jsonDocument.RootElement.GetProperty("iv").GetString(); var tag = jsonDocument.RootElement.GetProperty("tag").GetString(); // 关键修改:用十六进制转换替代Base64转换 var cipherTextBytes = Convert.FromHexString(cipherText); var ivBytes = Convert.FromHexString(iv); var tagBytes = Convert.FromHexString(tag); var keyVaultUrl = "https://myhsmtest.managedhsm.azure.net"; var credential = new DefaultAzureCredential(); var client = new KeyClient(vaultUri: new Uri(keyVaultUrl), credential); var cryptographyClient = client.GetCryptographyClient("aes256", "1234aed127f8009e15d6c3a883b91f21"); DecryptParameters decryptParameters = DecryptParameters.A256GcmParameters(ciphertext: cipherTextBytes, iv: ivBytes, authenticationTag: tagBytes); var result = cryptographyClient.Decrypt(decryptParameters); // 输出解密后的明文 var plaintext = Encoding.UTF8.GetString(result.Plaintext); Console.WriteLine(plaintext); // 应该输出"123"
低版本.NET兼容方案
如果你的项目基于.NET Framework或者.NET Core 3.1及以下,可以用这个自定义的十六进制转字节数组方法:
public static byte[] HexToByteArray(string hex) { if (hex.Length % 2 != 0) throw new ArgumentException("Hex string must have even length."); byte[] bytes = new byte[hex.Length / 2]; for (int i = 0; i < hex.Length; i += 2) { bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); } return bytes; }
然后把代码里的Convert.FromHexString替换成HexToByteArray即可。
验证逻辑
修改后再执行代码,应该就能成功解密出"123"了。原因很简单:
- Azure CLI输出的加密相关字段是十六进制编码,必须用十六进制转字节数组
- C# SDK内部加密返回的是Base64编码,所以自己加密解密时用Base64转换没问题
- Azure CLI自己解密时会自动识别十六进制格式,所以它能正常解密
备注:内容来源于stack exchange,提问作者dotdev

