You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
438 lines
12 KiB
438 lines
12 KiB
#include "windows.h"
|
|
#include "stdio.h"
|
|
#include "wchar.h"
|
|
#include "wincrypt.h"
|
|
#include "stddef.h"
|
|
|
|
#ifndef NUMBER_OF
|
|
#define NUMBER_OF(x) (sizeof(x)/sizeof(*(x)))
|
|
#endif
|
|
|
|
static const WCHAR wchMicrosoftLogo[] =
|
|
L"Microsoft (R) Side-By-Side Public Key Token Extractor 1.1.3.0\n"
|
|
L"Copyright (C) Microsoft Corporation 2000-2002. All Rights Reserved\n\n";
|
|
|
|
|
|
#define STRONG_NAME_BYTE_LENGTH ( 8 )
|
|
|
|
typedef struct _SXS_PUBLIC_KEY_INFO
|
|
{
|
|
unsigned int SigAlgID;
|
|
unsigned int HashAlgID;
|
|
ULONG KeyLength;
|
|
BYTE pbKeyInfo[1];
|
|
} SXS_PUBLIC_KEY_INFO, *PSXS_PUBLIC_KEY_INFO;
|
|
|
|
|
|
#define BUFFER_SIZE ( 8192 )
|
|
|
|
BOOL
|
|
ParseArgs( WCHAR **argv, int argc, PCWSTR* ppcwszFilename, BOOL *fQuiet )
|
|
{
|
|
if ( fQuiet )
|
|
*fQuiet = FALSE;
|
|
if ( ppcwszFilename )
|
|
*ppcwszFilename = NULL;
|
|
|
|
if (argv == NULL)
|
|
return FALSE;
|
|
|
|
if ( !fQuiet || !ppcwszFilename )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
for ( int i = 1; i < argc; i++ )
|
|
{
|
|
if (argv[i] == NULL)
|
|
{
|
|
::fwprintf(stderr, L"Bad parameter in argument list\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if ( ( argv[i][0] == L'-' ) || ( argv[i][0] == L'/' ) )
|
|
{
|
|
PCWSTR pval = argv[i] + 1;
|
|
if (::_wcsicmp(pval, L"nologo") == 0)
|
|
{
|
|
}
|
|
else if (::_wcsicmp(pval, L"quiet") == 0)
|
|
{
|
|
if ( fQuiet ) *fQuiet = TRUE;
|
|
}
|
|
else if (::_wcsicmp(pval, L"?") == 0 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
::fwprintf(stderr, L"Unrecognized parameter %ls\n", argv[i]);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( *ppcwszFilename == NULL )
|
|
{
|
|
*ppcwszFilename = argv[i];
|
|
}
|
|
else
|
|
{
|
|
::fwprintf(stderr, L"Only one filename parameter at a time.\n");
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void DispUsage( PCWSTR pcwszExeName )
|
|
{
|
|
const static WCHAR wchUsage[] =
|
|
L"Extracts public key tokens from certificate files, in a format\n"
|
|
L"usable in Side-By-Side assembly identities.\n"
|
|
L"\n"
|
|
L"Usage:\n"
|
|
L"\n"
|
|
L"%ls <filename.cer> [-quiet]\n";
|
|
|
|
::wprintf(wchUsage, pcwszExeName);
|
|
}
|
|
|
|
BOOL
|
|
HashAndSwizzleKey(
|
|
HCRYPTPROV hProvider,
|
|
BYTE *pbPublicKeyBlob,
|
|
SIZE_T cbPublicKeyBlob,
|
|
BYTE *pbKeyToken,
|
|
SIZE_T &cbKeyToken
|
|
)
|
|
{
|
|
BOOL fResult = FALSE;
|
|
HCRYPTHASH hHash = NULL;
|
|
DWORD dwHashSize, dwHashSizeSize;
|
|
ULONG top = STRONG_NAME_BYTE_LENGTH - 1;
|
|
ULONG bottom = 0;
|
|
|
|
|
|
if (cbKeyToken < STRONG_NAME_BYTE_LENGTH) {
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !::CryptCreateHash( hProvider, CALG_SHA1, NULL, 0, &hHash ) )
|
|
{
|
|
::fwprintf(stderr, L"Unable to create cryptological hash object, error %ld\n", ::GetLastError());
|
|
goto Exit;
|
|
}
|
|
|
|
if ( !::CryptHashData( hHash, pbPublicKeyBlob, static_cast<DWORD>(cbPublicKeyBlob), 0 ) )
|
|
{
|
|
::fwprintf(stderr, L"Unable to hash public key information, error %ld\n", ::GetLastError());
|
|
goto Exit;
|
|
}
|
|
|
|
if ( !::CryptGetHashParam( hHash, HP_HASHSIZE, (PBYTE)&dwHashSize, &(dwHashSizeSize = sizeof(dwHashSize)), 0))
|
|
{
|
|
::fwprintf(stderr, L"Unable to determine size of hashed public key bits, error %ld\n", ::GetLastError());
|
|
goto Exit;
|
|
}
|
|
|
|
if ( dwHashSize > cbKeyToken )
|
|
{
|
|
::fwprintf(stderr, L"Hashed data is too large - space for %ld bytes, got %ld.\n",
|
|
cbKeyToken, dwHashSize);
|
|
goto Exit;
|
|
}
|
|
|
|
if ( !::CryptGetHashParam( hHash, HP_HASHVAL, pbKeyToken, &(dwHashSize = (DWORD)cbKeyToken), 0))
|
|
{
|
|
::fwprintf(stderr, L"Unable to get hash of public key bits, error %ld\n", ::GetLastError());
|
|
goto Exit;
|
|
}
|
|
|
|
cbKeyToken = dwHashSize;
|
|
|
|
if (cbKeyToken < STRONG_NAME_BYTE_LENGTH)
|
|
{
|
|
::fwprintf(stderr, L"Internal error - length of hash object (%d) is less than strong name length (%d)\n",
|
|
cbKeyToken,
|
|
STRONG_NAME_BYTE_LENGTH);
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Now, move down the last eight bytes, then reverse them.
|
|
//
|
|
::memmove(pbKeyToken,
|
|
pbKeyToken + (cbKeyToken - STRONG_NAME_BYTE_LENGTH),
|
|
STRONG_NAME_BYTE_LENGTH);
|
|
|
|
while ( bottom < top )
|
|
{
|
|
const BYTE b = pbKeyToken[top];
|
|
pbKeyToken[top] = pbKeyToken[bottom];
|
|
pbKeyToken[bottom] = b;
|
|
bottom++;
|
|
top--;
|
|
}
|
|
|
|
//
|
|
// The tokens are always this long.
|
|
//
|
|
cbKeyToken = STRONG_NAME_BYTE_LENGTH;
|
|
|
|
fResult = TRUE;
|
|
Exit:
|
|
if ( hHash != NULL )
|
|
{
|
|
::CryptDestroyHash(hHash);
|
|
hHash = NULL;
|
|
}
|
|
return fResult;
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetTokenOfKey(
|
|
PCERT_PUBLIC_KEY_INFO pKeyInfo,
|
|
PBYTE prgbBuffer,
|
|
SIZE_T &cbPublicKeyTokenLength
|
|
)
|
|
{
|
|
PBYTE rgbWorkingSpace = NULL;
|
|
DWORD dwRequiredSpace = 0;
|
|
PSXS_PUBLIC_KEY_INFO pKeyBlobWorkspace = NULL;
|
|
HCRYPTPROV hContext = NULL;
|
|
HCRYPTKEY hCryptKey = NULL;
|
|
BOOL fResult = FALSE;
|
|
|
|
if ( !CryptAcquireContext(&hContext, NULL, NULL, PROV_RSA_FULL, CRYPT_SILENT | CRYPT_VERIFYCONTEXT))
|
|
{
|
|
::fwprintf(stderr, L"Unable to aquire cryptological context, error %ld.\n", ::GetLastError());
|
|
goto Exit;
|
|
}
|
|
|
|
::ZeroMemory(prgbBuffer, cbPublicKeyTokenLength);
|
|
|
|
//
|
|
// Set up the public key info blob for hashing. Import the key to a real
|
|
// HCRYPTKEY, then export the bits back out to a buffer. Set up the various
|
|
// other settings in the blob as well, the type of key and the alg. used to
|
|
// sign it.
|
|
//
|
|
if ( !::CryptImportPublicKeyInfoEx(
|
|
hContext,
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
pKeyInfo,
|
|
CALG_RSA_SIGN,
|
|
0,
|
|
NULL,
|
|
&hCryptKey) )
|
|
{
|
|
::fwprintf(stderr, L"Unable to import the public key from this certificate. Error %ld.\n", ::GetLastError());
|
|
goto Exit;
|
|
}
|
|
|
|
if (!::CryptExportKey(hCryptKey, NULL, PUBLICKEYBLOB, 0, NULL, &dwRequiredSpace))
|
|
{
|
|
::fwprintf(stderr, L"Unable to get required space for exporting public key data\n");
|
|
goto Exit;
|
|
}
|
|
|
|
dwRequiredSpace += sizeof(SXS_PUBLIC_KEY_INFO);
|
|
rgbWorkingSpace = (PBYTE)HeapAlloc(GetProcessHeap(), 0, dwRequiredSpace);
|
|
if (rgbWorkingSpace == NULL)
|
|
{
|
|
::fwprintf(stderr, L"Not enough memory to export public key data\n");
|
|
goto Exit;
|
|
}
|
|
|
|
pKeyBlobWorkspace = reinterpret_cast<PSXS_PUBLIC_KEY_INFO>(rgbWorkingSpace);
|
|
pKeyBlobWorkspace->KeyLength = dwRequiredSpace - offsetof(SXS_PUBLIC_KEY_INFO, pbKeyInfo);
|
|
|
|
if ( !::CryptExportKey(
|
|
hCryptKey,
|
|
NULL,
|
|
PUBLICKEYBLOB,
|
|
0,
|
|
pKeyBlobWorkspace->pbKeyInfo,
|
|
&pKeyBlobWorkspace->KeyLength) )
|
|
{
|
|
::fwprintf(stderr, L"Unable to extract public key bits from this certificate. Error %ld.\n", ::GetLastError());
|
|
goto Exit;
|
|
}
|
|
|
|
pKeyBlobWorkspace->SigAlgID = CALG_RSA_SIGN;
|
|
pKeyBlobWorkspace->HashAlgID = CALG_SHA1;
|
|
|
|
//
|
|
// We now need to hash the public key bytes with SHA1.
|
|
//
|
|
dwRequiredSpace = pKeyBlobWorkspace->KeyLength + offsetof(SXS_PUBLIC_KEY_INFO, pbKeyInfo);
|
|
if (!::HashAndSwizzleKey(
|
|
hContext,
|
|
(PBYTE)pKeyBlobWorkspace,
|
|
dwRequiredSpace,
|
|
prgbBuffer,
|
|
cbPublicKeyTokenLength))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
fResult = TRUE;
|
|
Exit:
|
|
if ( hCryptKey != NULL )
|
|
{
|
|
::CryptDestroyKey(hCryptKey);
|
|
hCryptKey = NULL;
|
|
}
|
|
if (rgbWorkingSpace != NULL)
|
|
{
|
|
::HeapFree(GetProcessHeap(), 0, rgbWorkingSpace);
|
|
rgbWorkingSpace = NULL;
|
|
}
|
|
if ( hContext != NULL )
|
|
{
|
|
::CryptReleaseContext(hContext, 0);
|
|
hContext = NULL;
|
|
}
|
|
|
|
return fResult;
|
|
|
|
}
|
|
|
|
|
|
int __cdecl wmain( int argc, WCHAR *argv[] )
|
|
{
|
|
HCERTSTORE hCertStore = NULL;
|
|
PCCERT_CONTEXT pCertContext = NULL;
|
|
BOOL fNoLogoDisplay = FALSE;
|
|
BOOL fQuiet = FALSE;
|
|
DWORD STRONG_NAME_LENGTH = 8;
|
|
PCWSTR pcwszFilename = NULL;
|
|
DWORD dwRetVal = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Quick check - are we to display the logo?
|
|
for ( int j = 0; j < argc; j++ )
|
|
{
|
|
if (::_wcsicmp(argv[j], L"-nologo") == 0)
|
|
fNoLogoDisplay = TRUE;
|
|
}
|
|
|
|
if ( !fNoLogoDisplay )
|
|
{
|
|
::fputws(wchMicrosoftLogo, stdout);
|
|
}
|
|
|
|
//
|
|
// Now go look for the arguments.
|
|
//
|
|
if ((argc < 2) || !ParseArgs( argv, argc, &pcwszFilename, &fQuiet ))
|
|
{
|
|
::DispUsage( argv[0] );
|
|
dwRetVal = ERROR_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
else if ( !pcwszFilename )
|
|
{
|
|
::DispUsage( argv[0] );
|
|
dwRetVal = ERROR_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
hCertStore = ::CertOpenStore(
|
|
CERT_STORE_PROV_FILENAME,
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
NULL,
|
|
CERT_STORE_OPEN_EXISTING_FLAG,
|
|
(void*)pcwszFilename);
|
|
|
|
if ( !hCertStore )
|
|
{
|
|
::fwprintf(
|
|
stderr,
|
|
L"Unable to open the input file %ls, error %ld\n",
|
|
pcwszFilename,
|
|
dwRetVal = ::GetLastError());
|
|
goto Exit;
|
|
}
|
|
|
|
while ( pCertContext = ::CertEnumCertificatesInStore( hCertStore, pCertContext ) )
|
|
{
|
|
if ( !pCertContext->pCertInfo )
|
|
{
|
|
::fwprintf( stderr, L"Oddity with file %ls - Certificate information not decodable\n", pcwszFilename );
|
|
continue;
|
|
}
|
|
|
|
// NTRAID#NTBUG9 - 536275 - jonwis - 2002/04/25 - Stack buffers are bad, replace with heap allocated blobs
|
|
WCHAR wsNiceName[BUFFER_SIZE] = { L'\0' };
|
|
BYTE bBuffer[BUFFER_SIZE];
|
|
SIZE_T cbBuffer = BUFFER_SIZE;
|
|
DWORD dwKeyLength = 0;
|
|
PCERT_PUBLIC_KEY_INFO pKeyInfo = &(pCertContext->pCertInfo->SubjectPublicKeyInfo);
|
|
DWORD dwDump = 0;
|
|
|
|
dwDump = ::CertGetNameStringW(
|
|
pCertContext,
|
|
CERT_NAME_FRIENDLY_DISPLAY_TYPE,
|
|
CERT_NAME_ISSUER_FLAG,
|
|
NULL,
|
|
wsNiceName,
|
|
BUFFER_SIZE
|
|
);
|
|
|
|
if ( dwDump == 0 )
|
|
{
|
|
::fwprintf(stderr, L"Unable to get certificate name string! Error %ld.", GetLastError());
|
|
::wcsncpy(wsNiceName, L"(Unknown)", NUMBER_OF(wsNiceName));
|
|
wsNiceName[NUMBER_OF(wsNiceName) - 1] = 0;
|
|
}
|
|
|
|
if ( !fQuiet )
|
|
{
|
|
dwKeyLength = CertGetPublicKeyLength( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pKeyInfo );
|
|
|
|
::wprintf(L"\nCertificate: \"%ls\" - %ld bits long\n", wsNiceName, dwKeyLength);
|
|
|
|
if ( dwKeyLength < 2048 )
|
|
{
|
|
::wprintf(L"\tWarning! This key is too short to sign SxS assemblies with.\n\tSigning keys need to be 2048 bits or more.\n");
|
|
}
|
|
}
|
|
|
|
if (!::GetTokenOfKey( pKeyInfo, bBuffer, cbBuffer ))
|
|
{
|
|
::fwprintf(stderr, L"Unable to generate public key token for this certificate.\n");
|
|
}
|
|
else
|
|
{
|
|
if ( !fQuiet ) ::wprintf(L"\tpublicKeyToken=\"");
|
|
for ( SIZE_T i = 0; i < cbBuffer; i++ )
|
|
{
|
|
::wprintf(L"%02x", bBuffer[i] );
|
|
}
|
|
if ( !fQuiet )
|
|
::wprintf(L"\"\n");
|
|
else
|
|
::wprintf(L"\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Exit:
|
|
|
|
if ( hCertStore != NULL )
|
|
{
|
|
::CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
|
|
hCertStore = NULL;
|
|
}
|
|
|
|
return dwRetVal;
|
|
}
|
|
|
|
|