我知道我可以使用下面的代码来检索使用X509Certificate类的可执行文件的数字签名信息:
X509Certificate cert = X509Certificate.CreateFromSignedFile("MySignedProgram.exe"); string certSubject = cert.Subject;
我的问题是如何从一个非exe文件,例如,一个签名的.js
或.vbs
文件检索数字证书的详细信息?
PS。 这个本地代码和CryptQueryObject API似乎正确地完成了这项工作。 但是,我很好奇,如果有一个.NET的比喻呢? 上面的代码抛出一个exception,错误代码为0x80092009
和说明如果将“MySignedScript.js”传递给CreateFromSignedFile
方法,则Cannot find the requested object
。
我想发布一个更新,因为我还没有得到我原来的问题的答案(任何人可能处理这个问题。)
我能够通过很难的方式来解决这个问题,即通过从非托管进程获取数字证书的详细信息。
好消息是有一个很好的来自微软的C样本显示了它是如何完成的。 坏消息是,如果使用PInvoke重写.NET的C代码,它似乎不工作。 (我不知道PInvoke是否足以告诉你为什么,所以如果有人知道,我也想知道,所以请在下面的评论中分享。
所以唯一的办法就是使用Microsoft代码制作一个小型的C ++控制台可执行文件,并从C#进程中调用它。 诀窍是传递参数。 你可以使用命令行参数来传递参数,但是为了让它们出来,你可以使用STDOUT
流。 ( 这是一个例子。)
最后,这是我的推理,为什么我得到一个0x80092009
错误代码,这是在WinAPI世界,顺便说一句, CRYPT_E_NO_MATCH
异常。 当然,这个错误是由CryptQueryObject
API产生的。 不幸的是,和微软一样,这个错误太模糊,不能提供任何关于失败的额外细节。 例如,如果CryptQueryObject
API本身的任何输入参数配置错误,则可能会返回。 但是通过玩微软的C代码,我了解到CryptQueryObject
API依靠代码签名文件的扩展名来正确解释附加数字签名的格式。 例如,如果它是一个可执行文件,它将是签名的数字格式。 如果是JScript
文件,则签名将具有其他格式,第三种格式将用于VBScript
文件或PowerShell
脚本等。 很有可能,托管的X509Certificate
类重命名导致CryptQueryObject
API失败的目标文件。 尽管如此,这并不能解释为什么PInvoke方法似乎失败了相同的错误代码。 (虽然在这种情况下,可能是由于缺乏PInvoke调用的知识导致的,因为对于错误的输入参数返回相同的错误代码。)
最后,如果微软的原始页面下面,这里是我所指的C代码的复制和粘贴:
#include <windows.h> #include <wincrypt.h> #include <wintrust.h> #include <stdio.h> #include <tchar.h> #pragma comment(lib, "crypt32.lib") #define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) typedef struct { LPWSTR lpszProgramName; LPWSTR lpszPublisherLink; LPWSTR lpszMoreInfoLink; } SPROG_PUBLISHERINFO, *PSPROG_PUBLISHERINFO; BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo, PSPROG_PUBLISHERINFO Info); BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st); BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext); BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo, PCMSG_SIGNER_INFO *pCounterSignerInfo); int _tmain(int argc, TCHAR *argv[]) { WCHAR szFileName[MAX_PATH]; HCERTSTORE hStore = NULL; HCRYPTMSG hMsg = NULL; PCCERT_CONTEXT pCertContext = NULL; BOOL fResult; DWORD dwEncoding, dwContentType, dwFormatType; PCMSG_SIGNER_INFO pSignerInfo = NULL; PCMSG_SIGNER_INFO pCounterSignerInfo = NULL; DWORD dwSignerInfo; CERT_INFO CertInfo; SPROG_PUBLISHERINFO ProgPubInfo; SYSTEMTIME st; ZeroMemory(&ProgPubInfo, sizeof(ProgPubInfo)); __try { if (argc != 2) { _tprintf(_T("Usage: SignedFileInfo <filename>\n")); return 0; } #ifdef UNICODE lstrcpynW(szFileName, argv[1], MAX_PATH); #else if (mbstowcs(szFileName, argv[1], MAX_PATH) == -1) { printf("Unable to convert to unicode.\n"); __leave; } #endif // Get message handle and store handle from the signed file. fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE, szFileName, CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, CERT_QUERY_FORMAT_FLAG_BINARY, 0, &dwEncoding, &dwContentType, &dwFormatType, &hStore, &hMsg, NULL); if (!fResult) { _tprintf(_T("CryptQueryObject failed with %x\n"), GetLastError()); __leave; } // Get signer information size. fResult = CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwSignerInfo); if (!fResult) { _tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError()); __leave; } // Allocate memory for signer information. pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo); if (!pSignerInfo) { _tprintf(_T("Unable to allocate memory for Signer Info.\n")); __leave; } // Get Signer Information. fResult = CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, (PVOID)pSignerInfo, &dwSignerInfo); if (!fResult) { _tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError()); __leave; } // Get program name and publisher information from // signer info structure. if (GetProgAndPublisherInfo(pSignerInfo, &ProgPubInfo)) { if (ProgPubInfo.lpszProgramName != NULL) { wprintf(L"Program Name : %s\n", ProgPubInfo.lpszProgramName); } if (ProgPubInfo.lpszPublisherLink != NULL) { wprintf(L"Publisher Link : %s\n", ProgPubInfo.lpszPublisherLink); } if (ProgPubInfo.lpszMoreInfoLink != NULL) { wprintf(L"MoreInfo Link : %s\n", ProgPubInfo.lpszMoreInfoLink); } } _tprintf(_T("\n")); // Search for the signer certificate in the temporary // certificate store. CertInfo.Issuer = pSignerInfo->Issuer; CertInfo.SerialNumber = pSignerInfo->SerialNumber; pCertContext = CertFindCertificateInStore(hStore, ENCODING, 0, CERT_FIND_SUBJECT_CERT, (PVOID)&CertInfo, NULL); if (!pCertContext) { _tprintf(_T("CertFindCertificateInStore failed with %x\n"), GetLastError()); __leave; } // Print Signer certificate information. _tprintf(_T("Signer Certificate:\n\n")); PrintCertificateInfo(pCertContext); _tprintf(_T("\n")); // Get the timestamp certificate signerinfo structure. if (GetTimeStampSignerInfo(pSignerInfo, &pCounterSignerInfo)) { // Search for Timestamp certificate in the temporary // certificate store. CertInfo.Issuer = pCounterSignerInfo->Issuer; CertInfo.SerialNumber = pCounterSignerInfo->SerialNumber; pCertContext = CertFindCertificateInStore(hStore, ENCODING, 0, CERT_FIND_SUBJECT_CERT, (PVOID)&CertInfo, NULL); if (!pCertContext) { _tprintf(_T("CertFindCertificateInStore failed with %x\n"), GetLastError()); __leave; } // Print timestamp certificate information. _tprintf(_T("TimeStamp Certificate:\n\n")); PrintCertificateInfo(pCertContext); _tprintf(_T("\n")); // Find Date of timestamp. if (GetDateOfTimeStamp(pCounterSignerInfo, &st)) { _tprintf(_T("Date of TimeStamp : %02d/%02d/%04d %02d:%02d\n"), st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute); } _tprintf(_T("\n")); } } __finally { // Clean up. if (ProgPubInfo.lpszProgramName != NULL) LocalFree(ProgPubInfo.lpszProgramName); if (ProgPubInfo.lpszPublisherLink != NULL) LocalFree(ProgPubInfo.lpszPublisherLink); if (ProgPubInfo.lpszMoreInfoLink != NULL) LocalFree(ProgPubInfo.lpszMoreInfoLink); if (pSignerInfo != NULL) LocalFree(pSignerInfo); if (pCounterSignerInfo != NULL) LocalFree(pCounterSignerInfo); if (pCertContext != NULL) CertFreeCertificateContext(pCertContext); if (hStore != NULL) CertCloseStore(hStore, 0); if (hMsg != NULL) CryptMsgClose(hMsg); } return 0; } BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext) { BOOL fReturn = FALSE; LPTSTR szName = NULL; DWORD dwData; __try { // Print Serial Number. _tprintf(_T("Serial Number: ")); dwData = pCertContext->pCertInfo->SerialNumber.cbData; for (DWORD n = 0; n < dwData; n++) { _tprintf(_T("%02x "), pCertContext->pCertInfo->SerialNumber.pbData[dwData - (n + 1)]); } _tprintf(_T("\n")); // Get Issuer name size. if (!(dwData = CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, NULL, 0))) { _tprintf(_T("CertGetNameString failed.\n")); __leave; } // Allocate memory for Issuer name. szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR)); if (!szName) { _tprintf(_T("Unable to allocate memory for issuer name.\n")); __leave; } // Get Issuer name. if (!(CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, szName, dwData))) { _tprintf(_T("CertGetNameString failed.\n")); __leave; } // print Issuer name. _tprintf(_T("Issuer Name: %s\n"), szName); LocalFree(szName); szName = NULL; // Get Subject name size. if (!(dwData = CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0))) { _tprintf(_T("CertGetNameString failed.\n")); __leave; } // Allocate memory for subject name. szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR)); if (!szName) { _tprintf(_T("Unable to allocate memory for subject name.\n")); __leave; } // Get subject name. if (!(CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, szName, dwData))) { _tprintf(_T("CertGetNameString failed.\n")); __leave; } // Print Subject Name. _tprintf(_T("Subject Name: %s\n"), szName); fReturn = TRUE; } __finally { if (szName != NULL) LocalFree(szName); } return fReturn; } LPWSTR AllocateAndCopyWideString(LPCWSTR inputString) { LPWSTR outputString = NULL; outputString = (LPWSTR)LocalAlloc(LPTR, (wcslen(inputString) + 1) * sizeof(WCHAR)); if (outputString != NULL) { lstrcpyW(outputString, inputString); } return outputString; } BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo, PSPROG_PUBLISHERINFO Info) { BOOL fReturn = FALSE; PSPC_SP_OPUS_INFO OpusInfo = NULL; DWORD dwData; BOOL fResult; __try { // Loop through authenticated attributes and find // SPC_SP_OPUS_INFO_OBJID OID. for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++) { if (lstrcmpA(SPC_SP_OPUS_INFO_OBJID, pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0) { // Get Size of SPC_SP_OPUS_INFO structure. fResult = CryptDecodeObject(ENCODING, SPC_SP_OPUS_INFO_OBJID, pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData, pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData, 0, NULL, &dwData); if (!fResult) { _tprintf(_T("CryptDecodeObject failed with %x\n"), GetLastError()); __leave; } // Allocate memory for SPC_SP_OPUS_INFO structure. OpusInfo = (PSPC_SP_OPUS_INFO)LocalAlloc(LPTR, dwData); if (!OpusInfo) { _tprintf(_T("Unable to allocate memory for Publisher Info.\n")); __leave; } // Decode and get SPC_SP_OPUS_INFO structure. fResult = CryptDecodeObject(ENCODING, SPC_SP_OPUS_INFO_OBJID, pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData, pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData, 0, OpusInfo, &dwData); if (!fResult) { _tprintf(_T("CryptDecodeObject failed with %x\n"), GetLastError()); __leave; } // Fill in Program Name if present. if (OpusInfo->pwszProgramName) { Info->lpszProgramName = AllocateAndCopyWideString(OpusInfo->pwszProgramName); } else Info->lpszProgramName = NULL; // Fill in Publisher Information if present. if (OpusInfo->pPublisherInfo) { switch (OpusInfo->pPublisherInfo->dwLinkChoice) { case SPC_URL_LINK_CHOICE: Info->lpszPublisherLink = AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszUrl); break; case SPC_FILE_LINK_CHOICE: Info->lpszPublisherLink = AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszFile); break; default: Info->lpszPublisherLink = NULL; break; } } else { Info->lpszPublisherLink = NULL; } // Fill in More Info if present. if (OpusInfo->pMoreInfo) { switch (OpusInfo->pMoreInfo->dwLinkChoice) { case SPC_URL_LINK_CHOICE: Info->lpszMoreInfoLink = AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszUrl); break; case SPC_FILE_LINK_CHOICE: Info->lpszMoreInfoLink = AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszFile); break; default: Info->lpszMoreInfoLink = NULL; break; } } else { Info->lpszMoreInfoLink = NULL; } fReturn = TRUE; break; // Break from for loop. } // lstrcmp SPC_SP_OPUS_INFO_OBJID } // for } __finally { if (OpusInfo != NULL) LocalFree(OpusInfo); } return fReturn; } BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st) { BOOL fResult; FILETIME lft, ft; DWORD dwData; BOOL fReturn = FALSE; // Loop through authenticated attributes and find // szOID_RSA_signingTime OID. for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++) { if (lstrcmpA(szOID_RSA_signingTime, pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0) { // Decode and get FILETIME structure. dwData = sizeof(ft); fResult = CryptDecodeObject(ENCODING, szOID_RSA_signingTime, pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData, pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData, 0, (PVOID)&ft, &dwData); if (!fResult) { _tprintf(_T("CryptDecodeObject failed with %x\n"), GetLastError()); break; } // Convert to local time. FileTimeToLocalFileTime(&ft, &lft); FileTimeToSystemTime(&lft, st); fReturn = TRUE; break; // Break from for loop. } //lstrcmp szOID_RSA_signingTime } // for return fReturn; } BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo, PCMSG_SIGNER_INFO *pCounterSignerInfo) { PCCERT_CONTEXT pCertContext = NULL; BOOL fReturn = FALSE; BOOL fResult; DWORD dwSize; __try { *pCounterSignerInfo = NULL; // Loop through unathenticated attributes for // szOID_RSA_counterSign OID. for (DWORD n = 0; n < pSignerInfo->UnauthAttrs.cAttr; n++) { if (lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId, szOID_RSA_counterSign) == 0) { // Get size of CMSG_SIGNER_INFO structure. fResult = CryptDecodeObject(ENCODING, PKCS7_SIGNER_INFO, pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData, pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData, 0, NULL, &dwSize); if (!fResult) { _tprintf(_T("CryptDecodeObject failed with %x\n"), GetLastError()); __leave; } // Allocate memory for CMSG_SIGNER_INFO. *pCounterSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSize); if (!*pCounterSignerInfo) { _tprintf(_T("Unable to allocate memory for timestamp info.\n")); __leave; } // Decode and get CMSG_SIGNER_INFO structure // for timestamp certificate. fResult = CryptDecodeObject(ENCODING, PKCS7_SIGNER_INFO, pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData, pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData, 0, (PVOID)*pCounterSignerInfo, &dwSize); if (!fResult) { _tprintf(_T("CryptDecodeObject failed with %x\n"), GetLastError()); __leave; } fReturn = TRUE; break; // Break from for loop. } } } __finally { // Clean up. if (pCertContext != NULL) CertFreeCertificateContext(pCertContext); } return fReturn; }
某些文件格式具有以标准定义的某种方式应用的数字签名,并且这种方式与其他文件格式不兼容。 Authenticode就是这样的例子之一 – 在Authenticode的EXE文件中嵌入签名是一种标准的方法。 其他文件格式,如PDF或XML有自己的方式来嵌入签名。
有几种通用的方法来进行签名,但这些方式意味着一个分离的签名(存储在一个单独的数据块中)或一个包装签名。 在后一种情况下,原始文件内容与签名相关的数据被“包装”,并且这样的文件不能像原始文件一样处理 – 它的结构是不同的。 这种通用格式是PKCS7 / CMS(和CAdES),S / MIME(基于CMS),OpenPGP,XMLDSig(它可以处理二进制内容)。
尽管与PKCS7 / CMS一起使用的功能似乎是最普遍的(与OpenPGP一起使用,但后者使用自己的密钥,而不是X.509证书),但是没有处理全球所有签名的通用功能。
我们的SecureBlackbox库具有创建和验证(以及从中提取数据)最流行文档格式和上述通用签名格式的签名的方法。