windows-nt/Source/XPSP1/NT/base/win32/fusion/tools/certinfo/certinfo.cpp

390 lines
10 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include "windows.h"
#include "stdio.h"
#include "wchar.h"
#include "wincrypt.h"
#include "stddef.h"
static const WCHAR wchMicrosoftLogo[] =
L"Microsoft (R) Side-By-Side Public Key Token Extractor 1.1.0.0\n"
L"Copyright (C) Microsoft Corporation 2000-2001. 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 ( !fQuiet || !ppcwszFilename )
{
return FALSE;
}
for ( int i = 1; i < argc; i++ )
{
if ( ( argv[i][0] == L'-' ) || ( argv[i][0] == L'/' ) )
{
PCWSTR pval = argv[i] + 1;
if (lstrcmpiW(pval, L"nologo") == 0)
{
}
else if (lstrcmpiW(pval, L"quiet") == 0)
{
if ( fQuiet ) *fQuiet = TRUE;
}
else if (lstrcmpiW(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", argv[i]);
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 ( !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");
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;
//
// 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--;
}
fResult = TRUE;
Exit:
if ( hHash != NULL )
{
CryptDestroyHash(hHash);
hHash = NULL;
}
return fResult;
}
BOOL
GetTokenOfKey(
PCERT_PUBLIC_KEY_INFO pKeyInfo,
PBYTE prgbBuffer,
SIZE_T &cbPublicKeyTokenLength
)
{
BYTE rgbWorkingSpace[8192];
PSXS_PUBLIC_KEY_INFO pKeyBlobWorkspace = reinterpret_cast<PSXS_PUBLIC_KEY_INFO>(rgbWorkingSpace);
HCRYPTPROV hContext = NULL;
HCRYPTKEY hCryptKey = NULL;
BOOL fResult = FALSE;
DWORD dwActualBlobSize;
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;
}
pKeyBlobWorkspace->KeyLength =
sizeof(rgbWorkingSpace) - 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;
dwActualBlobSize = pKeyBlobWorkspace->KeyLength + offsetof(SXS_PUBLIC_KEY_INFO, pbKeyInfo);
//
// We now need to hash the public key bytes with SHA1.
//
if (!HashAndSwizzleKey(
hContext,
(PBYTE)pKeyBlobWorkspace,
dwActualBlobSize,
prgbBuffer,
cbPublicKeyTokenLength))
{
goto Exit;
}
fResult = TRUE;
Exit:
if ( hCryptKey != NULL )
{
CryptDestroyKey(hCryptKey);
hCryptKey = 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 (lstrcmpiW(argv[j], L"-nologo") == 0)
fNoLogoDisplay = TRUE;
}
if ( !fNoLogoDisplay )
wprintf(wchMicrosoftLogo);
//
// 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" );
continue;
}
WCHAR wsNiceName[BUFFER_SIZE] = { L'\0' };
BYTE bBuffer[BUFFER_SIZE];
SIZE_T cbBuffer = BUFFER_SIZE;
DWORD dwKeyLength;
PCERT_PUBLIC_KEY_INFO pKeyInfo = &(pCertContext->pCertInfo->SubjectPublicKeyInfo);
DWORD dwDump;
ZeroMemory( wsNiceName, sizeof( wsNiceName ) / sizeof( *wsNiceName ) );
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());
wcscpy(wsNiceName, L"(Unknown)");
}
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;
}