如何使用未上架的NuGet包Azure.CodeSigning.Sdk在非Windows设备上通过代码对动态生成的PowerShell文件进行签名?
首先,咱们得先理清核心问题:你已经通过Azure.CodeSigning.Sdk拿到了签名字节和证书字节,但不知道怎么把这些东西转换成PowerShell能识别的签名格式——毕竟PowerShell的签名不是简单的二进制签名,而是有特定格式的注释块。
第一步:理解PowerShell脚本签名的本质
PowerShell脚本的签名是附加在脚本末尾的一个特殊注释块,格式大概是这样的:
# SIG # Begin signature block <Base64编码的签名数据> <Base64编码的证书链> # SIG # End signature block
这个块里的签名是对脚本内容的哈希值进行的签名,而你已经用SDK对SHA512哈希做了RS512签名,这一步是对的(毕竟你发现ES512不行,原因是PowerShell的签名机制目前只支持RSA系列算法,不支持ECDSA的ES系列)。
第二步:处理SDK返回的字节数据
你从response里拿到的两个字节数组需要做格式转换:
- 签名字节数组:直接转换成Base64字符串就行,因为PowerShell的签名块需要Base64编码的签名。
- 证书字节数组:把它转换成
X509Certificate2对象(在.NET里可以用new X509Certificate2(certificateBytes)),然后导出成Base64格式的CER文件内容(用cert.Export(X509ContentType.Cert)再转Base64)——如果有证书链的话,要把整个链都导出成Base64拼接起来。
第三步:构造签名块并追加到脚本
接下来就是把转换后的内容拼成PowerShell要求的签名块,然后追加到你的动态生成的脚本末尾。这里要注意一个关键点:你传给SDK的哈希必须和脚本内容的哈希完全一致——也就是说,你计算digest时用的文件流,必须和最终要签名的脚本内容完全相同,不能有任何额外的换行或空格,否则签名会无效。
给你一个C#的示例代码片段(适合Azure Function的Linux环境,纯.NET代码,不需要依赖任何Windows工具):
// 假设你已经拿到了SDK返回的signatureBytes和certificateBytes var signatureBase64 = Convert.ToBase64String(signatureBytes); var cert = new X509Certificate2(certificateBytes); var certBase64 = Convert.ToBase64String(cert.Export(X509ContentType.Cert)); // 构造签名块 var signatureBlock = $@" # SIG # Begin signature block {signatureBase64} {certBase64} # SIG # End signature block"; // 读取或获取你的动态生成的PowerShell脚本内容 var psScriptContent = await File.ReadAllTextAsync("your-script.ps1"); // 追加签名块 var signedScriptContent = psScriptContent.TrimEnd() + Environment.NewLine + signatureBlock; // 保存或返回签名后的脚本 await File.WriteAllTextAsync("signed-script.ps1", signedScriptContent);
第四步:解决Azure Function环境的问题
因为你用的是Azure Function(非Windows),纯.NET代码的方式完全可行,不需要安装signtool或Jsign——这两个工具要么是Windows专属,要么不支持Azure Trusted Signing,所以咱们用纯代码实现就绕开了这些问题。
关于SDK文档缺失的小技巧
既然这个SDK的源码和公开文档找不到,你可以尝试用.NET的反射工具(比如ILSpy)打开NuGet包的DLL,查看里面的类和方法的注释,或者看SignRequest、CertificateProfileClient这些类的参数说明,这样能更清楚地知道每个API的用途。另外,也可以提交Azure支持工单询问官方文档,毕竟这是Azure官方的SDK。
备注:内容来源于stack exchange,提问作者Thomas Eyde

