Попробуйте сделать следующее
sn.exe -p MyCompany.pfx MyCompany.pub
tlbimp.exe My.dll /delaysign /publickey:MyCompany.pub /out:Interop.My.dll
sn.exe -R Interop.My.dll MyCompany.pfx
Если вы создадите основную сборку взаимодействия из tlb-файла COM DLL, вы tlbimp.exe
в форме
tlbimp.exe My.tlb /primary /delaysign /publickey:MyCompany.pub /out:Interop.My.dll
Вы можете дополнительноиспользуйте подпись кода сборки взаимодействия:
signtool.exe sign /f MyCompany.pfx /p password /t http://timestamp.verisign.com/scripts/timstamp.dll /v Interop.My.dll
ОБНОВЛЕНО : Я нашел причину вашей проблемы, которая у вас есть.Прежде всего я должен объяснить (или напомнить) об одной особенности контейнера ключей.
Закрытый ключ существует в контейнере key .Контейнер ключа может находиться на локальном жестком диске в каком-то файле из папки %APPDATA%\Microsoft\Crypto\RSA\YourUserSid
.Точно так же контейнер ключей может быть частью файла PFX.В обоих случаях один контейнер ключей может сохранить до двух закрытых ключей: один для цифровой подписи (AT_SIGNATURE
) и другой для обмена ключами (AT_KEYEXCHANGE
) .Вы можете просмотреть параметры функции CryptGenKey для более подробной информации.Оба ключа могут быть абсолютно разными.Например, вы можете хранить в цифровой подписи один закрытый ключ контейнера с ключом размера 4096 и в части обмена ключами того же контейнера другой закрытый ключ размера 2048.
Сертификат имеет CERT_KEY_PROV_INFO_PROP_ID
как единицу из его расширенного свойства сертификата .Он сохраняет структуру CRYPT_KEY_PROV_INFO , которая dwKeySpec
(со значением AT_SIGNATURE
, AT_KEYEXCHANGE
или некоторым другим, например, AT_KEYEXCHANGE | AT_SIGNATURE
) используется вместе с другими полями pwszContainerName
(le-fe9728d2-af26-4f15-9be0-48c5af6f21dc
, например, например)), pwszProvName
(например, «Microsoft Enhanced Cryptographic Provider v1.0»), dwProvType
(PROV_RSA_FULL
, например).Таким образом, каждый имеет полную ссылку на ключ из сертификата.
Проблема в том, что у вашего сертификата есть ключ, сохраненный в части обмена ключами контейнера, а не в части цифровой подписи .Это не создает проблем в случае использования инструмента SignTool.exe
, но в случае использования tlbimp.exe
вы получаете ошибку
TlbImp: ошибка TI1000: Импортер библиотеки типов обнаружил непредвиденное исключение:System.Security.SecurityException - Неверный открытый ключ сборки.(Исключение из HRESULT: 0x8013141E)
Я полагаю, что это общая проблема Comodo или, возможно, Comodo сертификатов, которые получают через Tucows.
CN = UTN-USERFirst-Object
OU = http://www.usertrust.com
O = The USERTRUST Network
L = Salt Lake City
S = UT
C = US
Чтобы воспроизвести проблему, которую я описал, вы можете сделать следующие шаги:
1) Создать пару ключей, сохранить ее в обмене ключами нового контейнера ключей и создать собственнуюподписанный сертификат, который вы пару ключей.Для этого мы можем использовать, например, следующий параметр MakeCert.exe
:
MakeCert.exe -pe -ss MY -a sha1 -cy полномочия -len 2048 -e 31.12.2020 -r -n "CN = корневая администрация моей компании, O = моя компания, C = DE" -eku 1.3.6.1.5.5.7.3.3 -sky exchange -len 2048 -sk myKeyContainer
InВ приведенном выше примере мы сохраняем новый ключ в контейнере ключей с явным указанным именем 'myKeyContainer'
, чтобы упростить его поиск и проверку в будущем.На практике параметр не указывается, и имя контейнера будет генерироваться автоматически на основе нового GUID.
2) Используйте оснастку «Сертификаты» для экспорта из MMC.EXE нового созданного сертификата в виде файла PFX.,Вы должны выбрать пароль во время экспорта.В качестве альтернативы вы можете использовать CertUtil.exe
, чтобы сделать то же самое.
CertUtil.exe -privatekey -user -exportpfx -p yourPassword "Root Authority моей компании" MyCompany.pfx
3) Экспорт публичной части ключа в формате SNK.Я предпочитаю использовать расширение .PUB вместо .SNK, чтобы более четко отличить его от файла .SNK, содержащего пару ключей.Для этого можно использовать sn.exe
с параметром -p:
sn.exe -p MyCompany.pfx MyCompany.pub
4) Теперь можно использовать tlbimp.exe
для воспроизведения ошибки.
tlbimp.exe My.tlb / machine: X86 / primary / delaysign /publickey:MyCompany.pub /out:Interop.My.dll
Если вы не используете переключатель -sky exchange
на первом шаге, использование tlbimp.exe
будет успешно, и вы можете использовать на следующем шаге
sn.exe -R Interop.My.dll MyCompany.pfx
для подписи Interop.My.dll
. После этого вы можете использовать подпись кода для dll дополнительно
signtool.exe sign / f MyCompany.pfx / p yourPassword / t
http://timestamp.verisign.com/scripts/timstamp.dll / v Interop.My.dll
Извините за длинный текст, но я хочу быть уверен, что люди, которые будут иметь такую же проблему, смогут понять это.
Для решения этой проблемы вам понадобится любой инструмент, который может экспортировать и импортировать закрытый ключ. Вы должны экспортировать ключ из части обмена ключами контейнера и импортировать его в тот же контейнер в части цифровой подписи. Если у вас возникнут проблемы с поиском соответствующего инструмента, я могу попробовать помочь. Я не очень хорошо знаю существующие инструменты, но я знаю много способов, как написать программу, которая делает это. В случае .NET я бы использовал ExportCspBlob для экспорта ключа и используйте ImportCspBlob для его импорта обратно. Нужно изменить только один бит в блоке ключей, чтобы использовать AT_SIGNATURE
вместо AT_KEYEXCHANGE. Если вы открываете контейнер ключей, вы должны использовать KeyNumber.Exchange
один раз в качестве параметра CspParameters.KeyNumber
и KeyNumber.Signature
на следующем шаге.
ОБНОВЛЕНО 2 : Я нашел здесь описание, которое соответствует моим предположениям. Один из обходных путей, который предлагается, - это установить значение REG_DWORD
с именем KeySpec
и значением 2 (AT_SIGNATURE
) в ключе HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\StrongName
. Я проверял это, но это не помогает для использования tlbimp.exe
.
Так что я просто написал утилиту, которая делает всю работу. Вы должны импортировать свой файл PFX и затем запустить мою утилиту ChangeKeyProvInfo.exe
с именем выпуска сертификата. Например
ChangeKeyProvInfo.exe "Dmitry Streblechenko"
После этого я добавляю часть цифровой подписи в контейнер ключей с тем же секретным ключом, что и часть обмена ключами. Затем я изменяю свойство сертификата для использования части цифровой подписи контейнера ключей.
Вы можете скачать утилиту здесь и здесь исходный код.
Чтобы упростить поиск кода, я также размещаю его ниже. Вы должны видеть код только как быстрое и грязное решение, которое я написал сегодня, поэтому код не идеален.
#define STRICT
#include <windows.h>
#include <wincrypt.h>
#include <tchar.h>
#pragma comment (lib, "Crypt32.lib")
int _tmain(int argc, LPCTSTR *argv)
{
HCERTSTORE hStore = NULL;
PCCERT_CONTEXT pCertContext = NULL;
LPCTSTR pszCertSubjectName = NULL;
BOOL bSuccess, bResult = FALSE;
DWORD cbData = 0, dwKeySpec = 0;
HCRYPTKEY hKey = 0, hKeyNew = 0;
BOOL fCallerFreeProvOrNCryptKey;
PBYTE pPrivateKeyBlob = NULL;
PUBLICKEYSTRUC *pPublicKey = NULL;
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProvOrNCryptKey = 0;
PCRYPT_KEY_PROV_INFO pKeyProvInfo = NULL;
if (argc != 2) {
_tprintf(TEXT("USAGE:\r\n\tChangeKeyProvInfo.exe CertSubjectName\n"));
return 1;
}
pszCertSubjectName = argv[1];
__try {
hStore = CertOpenStore (CERT_STORE_PROV_SYSTEM,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
(HCRYPTPROV_LEGACY)0,
CERT_STORE_ENUM_ARCHIVED_FLAG | CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_CURRENT_USER,
TEXT("MY"));
if (hStore == NULL) {
_tprintf(TEXT("Error in CertOpenStore(): %d"), GetLastError());
__leave;
}
pCertContext = CertFindCertificateInStore (hStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR,
pszCertSubjectName,
NULL);
if (pCertContext == NULL) {
_tprintf(TEXT("Error in CertFindCertificateInStore(): %d"), GetLastError());
__leave;
}
bSuccess = CryptAcquireCertificatePrivateKey(pCertContext,
CRYPT_ACQUIRE_COMPARE_KEY_FLAG | CRYPT_ACQUIRE_NO_HEALING,
NULL,
&hCryptProvOrNCryptKey,
&dwKeySpec,
&fCallerFreeProvOrNCryptKey);
if (!bSuccess) {
_tprintf(TEXT("Error in CryptAcquireCertificatePrivateKey(): %d"), GetLastError());
__leave;
}
bSuccess = CryptGetUserKey (hCryptProvOrNCryptKey, dwKeySpec, &hKey);
if (!bSuccess) {
_tprintf(TEXT("Error in CryptGetUserKey(): %d"), GetLastError());
__leave;
}
// get privale key as clear text data in form PRIVATEKEYBLOB
cbData = 0;
bSuccess = CryptExportKey (hKey, 0, PRIVATEKEYBLOB, 0, NULL, &cbData);
if (!bSuccess) {
_tprintf(TEXT("Error in CryptExportKey(): %d"), GetLastError());
__leave;
}
pPrivateKeyBlob = (PBYTE)LocalAlloc(LPTR, cbData);
if (pPrivateKeyBlob == NULL) {
_tprintf(TEXT("Error in LocalAlloc(): %d"), GetLastError());
__leave;
}
bSuccess = CryptExportKey (hKey, 0, PRIVATEKEYBLOB, 0, pPrivateKeyBlob, &cbData);
if (!bSuccess) {
_tprintf(TEXT("Error in CryptExportKey(): %d"), GetLastError());
__leave;
}
// the PRIVATEKEYBLOB started with the PUBLICKEYSTRUC which contains the KeyAlg
// CALG_RSA_KEYX are used for AT_KEYEXCHANGE
// CALG_RSA_SIGN are used for AT_SIGNATURE
pPublicKey = (PUBLICKEYSTRUC *)pPrivateKeyBlob;
if (pPublicKey->aiKeyAlg == CALG_RSA_SIGN) {
_tprintf(TEXT("Currently AT_SIGNATURE are used - nothing to do.\n"));
__leave;
} else if (pPublicKey->aiKeyAlg != CALG_RSA_KEYX) {
_tprintf(TEXT("ERROR: Unknown algorithm 0x%X are used\n"), pPublicKey->aiKeyAlg);
__leave;
}
// !!!!!! the next line in the most important !!!!!!
pPublicKey->aiKeyAlg = CALG_RSA_SIGN;
bSuccess = CryptImportKey (hCryptProvOrNCryptKey, pPrivateKeyBlob, cbData, (HCRYPTKEY)NULL, CRYPT_EXPORTABLE, &hKeyNew);
if (!bSuccess) {
_tprintf(TEXT("Error in CertGetCertificateContextProperty(): %d"), GetLastError());
__leave;
}
_tprintf(TEXT("The key container has successfully imported the same key for the AT_SIGNATURE usage.\n"));
bSuccess = CertGetCertificateContextProperty (pCertContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &cbData);
if (!bSuccess) {
_tprintf(TEXT("Error in CertGetCertificateContextProperty(): %d"), GetLastError());
__leave;
}
pKeyProvInfo = (PCRYPT_KEY_PROV_INFO)LocalAlloc (LPTR, cbData);
if (pKeyProvInfo == NULL) {
_tprintf(TEXT("Error in LocalAlloc(): %d"), GetLastError());
__leave;
}
bSuccess = CertGetCertificateContextProperty (pCertContext, CERT_KEY_PROV_INFO_PROP_ID, pKeyProvInfo, &cbData);
if (!bSuccess) {
_tprintf(TEXT("Error in CertGetCertificateContextProperty(): %d"), GetLastError());
__leave;
}
// !!!!!! the next line in the most important !!!!!!
pKeyProvInfo->dwKeySpec = AT_SIGNATURE;
bSuccess = CertSetCertificateContextProperty (pCertContext, CERT_KEY_PROV_INFO_PROP_ID, CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, pKeyProvInfo);
if (!bSuccess) {
_tprintf(TEXT("Error in CertSetCertificateContextProperty(): %d"), GetLastError());
__leave;
}
_tprintf(TEXT("The certificale property was successfully changed to use the key with AT_SIGNATURE.\n"));
bResult = TRUE;
}
__finally {
if (pPrivateKeyBlob)
pPrivateKeyBlob = (PBYTE) LocalFree (pPrivateKeyBlob);
if (pKeyProvInfo)
pKeyProvInfo = (PCRYPT_KEY_PROV_INFO) LocalFree (pKeyProvInfo);
if (pCertContext)
bSuccess = CertFreeCertificateContext (pCertContext);
if (hKeyNew)
bSuccess = CryptDestroyKey (hKeyNew);
if (hCryptProvOrNCryptKey)
bSuccess = CryptReleaseContext (hCryptProvOrNCryptKey, 0);
if (hStore)
bSuccess = CertCloseStore (hStore, CERT_CLOSE_STORE_CHECK_FLAG);
}
return bResult? 0: 1;
}
ОБНОВЛЕНО 2 : Здесь вы можете загрузить полный протокол эксперимента, который каждый может повторить, чтобы воспроизвести проблему, как я понимаю, если. Первый сертификат, сохраненный в My.pfx , может быть проанализирован в отношении certutil.exe -dump -v My.pfx
. Полный вывод вы можете увидеть здесь . Самая важная часть вывода:
...
CERT_KEY_PROV_INFO_PROP_ID(2):
...
KeySpec = 1 -- AT_KEYEXCHANGE
...
Private Key:
PRIVATEKEYBLOB
Version: 2
aiKeyAlg: 0xa400
CALG_RSA_KEYX
Algorithm Class: 0xa000(5) ALG_CLASS_KEY_EXCHANGE
...
Encryption test passed
Измененные сертификаты будут сохранены в My1.pfx . Вывод из certutil.exe -dump -v My.pfx
показывает, что проблема устранена:
...
CERT_KEY_PROV_INFO_PROP_ID(2):
...
KeySpec = 2 -- AT_SIGNATURE
...
Private Key:
PRIVATEKEYBLOB
Version: 2
aiKeyAlg: 0x2400
CALG_RSA_SIGN
Algorithm Class: 0x2000(1) ALG_CLASS_SIGNATURE
...
Signature test passed
ОБНОВЛЕНИЕ 3:
Через 3 года проблема с подписями Comodo все еще присутствует. Ниже их ответ. В следующий раз, когда мой сертификат будет продлен, я пойду с кем-нибудь еще.
Наши сертификаты подписи кода предназначены для использования для подписей Microsoft Authenticode (и других связанных с подписью объектов элементов, например подписи jar) и не совсем предназначены для подписи Microsoft Strong Name для сборок.Если приложение Microsoft не будет использовать пару ключей с установленным по умолчанию значением keySpec (1, AT_KEYEXCHANGE; которое позволяет подписывать и шифровать) и работает только с AT_SIGNATURE (разрешает только подпись), то это будет проблема обработки сУтилита Microsoft, и вам нужно будет открыть дело с Microsoft, чтобы исправить проблему.У нас есть сотни тысяч клиентов, которые могут применять Microsoft Authenticode и другие связанные с объектами подписи без проблем с этим параметром keySpec, установленным как есть.Мы не хотим модифицировать процесс, который работает безупречно практически для всех, кроме нескольких человек, которые хотят пойти на крайние меры и использовать общедоступный CA для подписи strongName своих сборок.Большинство разработчиков, с которыми мы сталкиваемся, не используют подписанный ЦС сертификат для сборок с подписью Строго Имени, а используют самозаверяющий сертификат из-за максимальных сроков, которые публичные ЦС накладывают на все сертификаты (не более 3-5 лет максимум.)
Основываясь на сообщении stackoverflow, вы создали утилиту, которая исправляет и предоставляет вам обходной путь для вашей конкретной проблемы, поэтому, к сожалению, мы ничего не можем посоветовать в этом вопросе.
Приносим свои извинения за доставленные неудобства.
С уважением,
Техническая поддержка