aco*_*ner 3 c# cryptography marshalling digital-signature x509certificate
我能够使用以下代码成功地使用此页面上的信息,使用 x509 证书(.pfx 文件)对文件进行数字签名:
const Int32 CRYPTUI_WIZ_NO_UI = 1;
const Int32 CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE = 1;
const Int32 CRYPTUI_WIZ_DIGITAL_SIGN_CERT = 1;
struct CRYPTUI_WIZ_DIGITAL_SIGN_INFO
{
public Int32 dwSize;
public Int32 dwSubjectChoice;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszFileName;
public Int32 dwSigningCertChoice;
public IntPtr pSigningCertContext;
public string pwszTimestampURL;
public Int32 dwAdditionalCertChoice;
public IntPtr pSignExtInfo;
}
[DllImport("Cryptui.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool CryptUIWizDigitalSign(Int32 dwFlags, IntPtr hwndParent, string pwszWizardTitle, ref CRYPTUI_WIZ_DIGITAL_SIGN_INFO pDigitalSignInfo, ref IntPtr ppSignContext);
public bool SignExecutable(string certificatePath, string applicationPath, string certificatePassword)
{
var cert = new X509Certificate2(certificatePath, certificatePassword);
var pSigningCertContext = cert.Handle;
var digitalSignInfo = default(CRYPTUI_WIZ_DIGITAL_SIGN_INFO);
digitalSignInfo = new CRYPTUI_WIZ_DIGITAL_SIGN_INFO()
{
dwSize = Marshal.SizeOf(digitalSignInfo),
dwSubjectChoice = CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE,
pwszFileName = applicationPath,
dwSigningCertChoice = CRYPTUI_WIZ_DIGITAL_SIGN_CERT,
pSigningCertContext = pSigningCertContext,
pwszTimestampURL = null,
dwAdditionalCertChoice = 0,
pSignExtInfo = IntPtr.Zero
};
var pSignContext = default(IntPtr);
return CryptUIWizDigitalSign(CRYPTUI_WIZ_NO_UI, IntPtr.Zero, null, ref digitalSignInfo, ref pSignContext));
}
Run Code Online (Sandbox Code Playgroud)
但是,此过程要求我首先将文件写入磁盘。我需要做的是在将文件写入磁盘之前对内存中的文件内容进行数字签名(因为它是动态生成的)。CryptUI 文档应该支持这一点,所以我将代码更改如下:
const Int32 CRYPTUI_WIZ_NO_UI = 1;
const Int32 CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_BLOB = 2;
const Int32 CRYPTUI_WIZ_DIGITAL_SIGN_CERT = 1;
public struct CRYPTUI_WIZ_DIGITAL_SIGN_BLOB_INFO
{
public Int32 dwSize;
public IntPtr pGuidSubject;
public Int32 cbBlob;
public IntPtr pbBlob;
public string pwszDisplayName;
}
struct CRYPTUI_WIZ_DIGITAL_SIGN_INFO
{
public Int32 dwSize;
public Int32 dwSubjectChoice;
public IntPtr pSignBlobInfo;
public Int32 dwSigningCertChoice;
public IntPtr pSigningCertContext;
public string pwszTimestampURL;
public Int32 dwAdditionalCertChoice;
public IntPtr pSignExtInfo;
}
struct CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT
{
public Int32 dwSize;
public Int32 cbBlob;
public IntPtr pbBlob;
};
[DllImport("Cryptui.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool CryptUIWizDigitalSign(Int32 dwFlags, IntPtr hwndParent, string pwszWizardTitle, ref CRYPTUI_WIZ_DIGITAL_SIGN_INFO pDigitalSignInfo, ref CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT ppSignContext);
public byte[] SignExecutableBLOB(string certificatePath, byte[] applicationContent, string certificatePassword)
{
var cert = new X509Certificate2(certificatePath, certificatePassword);
var pSigningCertContext = cert.Handle;
int size = Marshal.SizeOf(applicationContent[0]) * applicationContent.Length;
var blobInfo = default(CRYPTUI_WIZ_DIGITAL_SIGN_BLOB_INFO);
blobInfo = new CRYPTUI_WIZ_DIGITAL_SIGN_BLOB_INFO()
{
dwSize = Marshal.SizeOf(blobInfo),
pGuidSubject = IntPtr.Zero,
cbBlob = size,
pbBlob = Marshal.AllocHGlobal(size),
pwszDisplayName = null
};
Marshal.Copy(applicationContent, 0, blobInfo.pbBlob, size);
IntPtr pBlobInfo = Marshal.AllocHGlobal(Marshal.SizeOf(blobInfo));
Marshal.StructureToPtr(blobInfo, pBlobInfo, false);
var digitalSignInfo = default(CRYPTUI_WIZ_DIGITAL_SIGN_INFO);
digitalSignInfo = new CRYPTUI_WIZ_DIGITAL_SIGN_INFO()
{
dwSize = Marshal.SizeOf(digitalSignInfo),
dwSubjectChoice = CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_BLOB,
pSignBlobInfo = pBlobInfo,
dwSigningCertChoice = CRYPTUI_WIZ_DIGITAL_SIGN_CERT,
pSigningCertContext = pSigningCertContext,
pwszTimestampURL = null,
dwAdditionalCertChoice = 0,
pSignExtInfo = IntPtr.Zero
};
try
{
var pSignContext = default(CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT);
if (!CryptUIWizDigitalSign(CRYPTUI_WIZ_NO_UI, IntPtr.Zero, null, ref digitalSignInfo, ref pSignContext))
throw new Win32Exception($"An error occurred attempting to digitally sign the content");
var signedApplicationContent = new byte[pSignContext.cbBlob];
Marshal.Copy(pSignContext.pbBlob, signedApplicationContent, 0, pSignContext.cbBlob);
return signedApplicationContent;
}
finally
{
Marshal.FreeHGlobal(pBlobInfo);
Marshal.FreeHGlobal(blobInfo.pbBlob);
}
}
Run Code Online (Sandbox Code Playgroud)
到目前为止,所有尝试运行此代码都会导致错误 0x80070057(参数不正确)。我尝试了很多小的改变,但我似乎找不到正确的方法来让这个调用成功。我很确定这与托管代码和非托管代码之间的数据编组不当有关,但我无法弄清楚。任何帮助,将不胜感激。
小智 6
几天前,我偶然发现了这个悬而未决的问题,因为我正在与同样的事情作斗争。我的公司(Gibson Research Corporation)销售一种商业软件产品(SpinRite),它是一个混合 DOS/Windows 可执行文件。每个许可的可执行文件都是为其购买者定制的,因此我们需要我们的网络服务器能够即时签署这些下载。就像发布这个问题的人一样,将文件写入文件系统只是为了对其进行签名,然后发送给其用户,这会很混乱(而且是错误的)。正确的方式是将文件保存在 RAM 中,对其进行签名,将签名的文件数据流式传输给其所有者,然后释放 RAM 分配。
\n令人如此恼火的是令人惊讶的关于使用 Windows 加密 API 的文档这甚至是错误的,因为 Microsoft 的 \xe2\x80\x9cCRYPTUI_WIZ_DIGITAL_SIGN_BLOB_INFO\xe2\x80\x9d 结构的关键 \xe2\x80\x9cpGuidSubject\xe2\x80\x9d 成员的文档将其描述为 \xe2\x80\x9cA 指向GUID 包含标识要加载的会话启动协议 (SIP) 功能的 GUID。\xe2\x80\x9d 问题是,在此上下文中 \xe2\x80\x9cSIP\xe2\x80\x9d 不代表 \xe2\ x80\x9cSession Initiation Protocol\xe2\x80\x9d...代表\xe2\x80\x9c主体接口包。\xe2\x80\x9d
\n今天之前我从来没有成为过这里的会员。但我过去从这里得到了很多帮助,所以我决定是时候回馈了。这是发生的事情:
\n正如我们在上面发帖者的原始问题中看到的,当他给 Windows 一个文件名时,他就能够开始工作。它起作用的原因是,这允许 Windows 检查文件以确定如何签署该特定类型的文件。但是,如果我们想让 Windows 在 RAM 中签署一个无定形的 \xe2\x80\x9cblob\xe2\x80\x9d,我们需要告诉 Windows 它是什么类型的 \xe2\x80\x9cblob\xe2\x80\x9d我们希望它签名...这就是 \xe2\x80\x9cpGuidSubject\xe2\x80\x9d 结构成员的用武之地。:) 在 CRYPTUI_WIZ_DIGITAL_SIGN_BLOB_INFO 结构中,我们需要提供一个 GUID,告诉 Windows 我们的 blob给它签名的是一个Windows可执行文件。
\n那么,我们在哪里可以找到 GUID 呢?通常这样的 GUID 会在头文件中的某个位置预定义。但在本例中并非如此。为了使这个更加\xe2\x80\x9copen-ending\xe2\x80\x9d,微软定义了一个函数,可以提供一个示例文件,它将返回SIP \xe2\x80\x9c主题接口包\xe2\该文件类型的 x80\x9d GUID。其功能是:
\nCryptSIPRetrieveSubjectGuid
\n如果您给它一个示例 Windows 可执行文件、一个空句柄和一个指向要填充的空 GUID 的指针,它将返回:
\n{C689AAB8-8E78-11D0-8C47-00C04FC295EE}
\n这就是使签名 blob 发挥作用所需的缺失的魔法咒语。只需将 \xe2\x80\x9cpGuidSubject\xe2\x80\x9d 结构内存指向该 GUID 即可。如果您想让 Windows 签署其他类型的 blob,只需将其放入文件中,将 \xe2\x80\x9cCryptSIPRetrieveSubjectGuid\xe2\x80\x9d 指向它,它将返回可用于 \xe2\x80 的 GUID \x9cin-内存\xe2\x80\x9d 签名。:)
\n我希望这对其他有此需求的人有用。让我惊讶的是,我们需要填补微软令人惊讶的文档(甚至示例代码)的缺乏,但这就是我们今天的处境。至少这个社区可以弥补它的不足。
\n