Source code of Windows XP (NT5)
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.

389 lines
10 KiB

  1. #include "windows.h"
  2. #include "stdio.h"
  3. #include "wchar.h"
  4. #include "wincrypt.h"
  5. #include "stddef.h"
  6. static const WCHAR wchMicrosoftLogo[] =
  7. L"Microsoft (R) Side-By-Side Public Key Token Extractor 1.1.0.0\n"
  8. L"Copyright (C) Microsoft Corporation 2000-2001. All Rights Reserved\n\n";
  9. #define STRONG_NAME_BYTE_LENGTH ( 8 )
  10. typedef struct _SXS_PUBLIC_KEY_INFO
  11. {
  12. unsigned int SigAlgID;
  13. unsigned int HashAlgID;
  14. ULONG KeyLength;
  15. BYTE pbKeyInfo[1];
  16. } SXS_PUBLIC_KEY_INFO, *PSXS_PUBLIC_KEY_INFO;
  17. #define BUFFER_SIZE ( 8192 )
  18. BOOL
  19. ParseArgs( WCHAR **argv, int argc, PCWSTR* ppcwszFilename, BOOL *fQuiet )
  20. {
  21. if ( fQuiet )
  22. *fQuiet = FALSE;
  23. if ( ppcwszFilename )
  24. *ppcwszFilename = NULL;
  25. if ( !fQuiet || !ppcwszFilename )
  26. {
  27. return FALSE;
  28. }
  29. for ( int i = 1; i < argc; i++ )
  30. {
  31. if ( ( argv[i][0] == L'-' ) || ( argv[i][0] == L'/' ) )
  32. {
  33. PCWSTR pval = argv[i] + 1;
  34. if (lstrcmpiW(pval, L"nologo") == 0)
  35. {
  36. }
  37. else if (lstrcmpiW(pval, L"quiet") == 0)
  38. {
  39. if ( fQuiet ) *fQuiet = TRUE;
  40. }
  41. else if (lstrcmpiW(pval, L"?") == 0 )
  42. {
  43. return FALSE;
  44. }
  45. else
  46. {
  47. fwprintf(stderr, L"Unrecognized parameter %ls\n", argv[i]);
  48. return FALSE;
  49. }
  50. }
  51. else
  52. {
  53. if ( *ppcwszFilename == NULL )
  54. {
  55. *ppcwszFilename = argv[i];
  56. }
  57. else
  58. {
  59. fwprintf(stderr, L"Only one filename parameter at a time.\n", argv[i]);
  60. return FALSE;
  61. }
  62. }
  63. }
  64. return TRUE;
  65. }
  66. void DispUsage( PCWSTR pcwszExeName )
  67. {
  68. const static WCHAR wchUsage[] =
  69. L"Extracts public key tokens from certificate files, in a format\n"
  70. L"usable in Side-By-Side assembly identities.\n"
  71. L"\n"
  72. L"Usage:\n"
  73. L"\n"
  74. L"%ls <filename.cer> [-quiet]\n";
  75. wprintf(wchUsage, pcwszExeName);
  76. }
  77. BOOL
  78. HashAndSwizzleKey(
  79. HCRYPTPROV hProvider,
  80. BYTE *pbPublicKeyBlob,
  81. SIZE_T cbPublicKeyBlob,
  82. BYTE *pbKeyToken,
  83. SIZE_T &cbKeyToken
  84. )
  85. {
  86. BOOL fResult = FALSE;
  87. HCRYPTHASH hHash = NULL;
  88. DWORD dwHashSize, dwHashSizeSize;
  89. ULONG top = STRONG_NAME_BYTE_LENGTH - 1;
  90. ULONG bottom = 0;
  91. if ( !CryptCreateHash( hProvider, CALG_SHA1, NULL, 0, &hHash ) )
  92. {
  93. fwprintf(stderr, L"Unable to create cryptological hash object, error %ld\n", ::GetLastError());
  94. goto Exit;
  95. }
  96. if ( !CryptHashData( hHash, pbPublicKeyBlob, static_cast<DWORD>(cbPublicKeyBlob), 0 ) )
  97. {
  98. fwprintf(stderr, L"Unable to hash public key information, error %ld\n", ::GetLastError());
  99. goto Exit;
  100. }
  101. if ( !CryptGetHashParam( hHash, HP_HASHSIZE, (PBYTE)&dwHashSize, &(dwHashSizeSize = sizeof(dwHashSize)), 0))
  102. {
  103. fwprintf(stderr, L"Unable to determine size of hashed public key bits, error %ld\n");
  104. goto Exit;
  105. }
  106. if ( dwHashSize > cbKeyToken )
  107. {
  108. fwprintf(stderr, L"Hashed data is too large - space for %ld bytes, got %ld.\n",
  109. cbKeyToken, dwHashSize);
  110. goto Exit;
  111. }
  112. if ( !CryptGetHashParam( hHash, HP_HASHVAL, pbKeyToken, &(dwHashSize = (DWORD)cbKeyToken), 0))
  113. {
  114. fwprintf(stderr, L"Unable to get hash of public key bits, error %ld\n", ::GetLastError());
  115. goto Exit;
  116. }
  117. cbKeyToken = dwHashSize;
  118. //
  119. // Now, move down the last eight bytes, then reverse them.
  120. //
  121. memmove(pbKeyToken,
  122. pbKeyToken + (cbKeyToken - STRONG_NAME_BYTE_LENGTH),
  123. STRONG_NAME_BYTE_LENGTH);
  124. while ( bottom < top )
  125. {
  126. const BYTE b = pbKeyToken[top];
  127. pbKeyToken[top] = pbKeyToken[bottom];
  128. pbKeyToken[bottom] = b;
  129. bottom++;
  130. top--;
  131. }
  132. fResult = TRUE;
  133. Exit:
  134. if ( hHash != NULL )
  135. {
  136. CryptDestroyHash(hHash);
  137. hHash = NULL;
  138. }
  139. return fResult;
  140. }
  141. BOOL
  142. GetTokenOfKey(
  143. PCERT_PUBLIC_KEY_INFO pKeyInfo,
  144. PBYTE prgbBuffer,
  145. SIZE_T &cbPublicKeyTokenLength
  146. )
  147. {
  148. BYTE rgbWorkingSpace[8192];
  149. PSXS_PUBLIC_KEY_INFO pKeyBlobWorkspace = reinterpret_cast<PSXS_PUBLIC_KEY_INFO>(rgbWorkingSpace);
  150. HCRYPTPROV hContext = NULL;
  151. HCRYPTKEY hCryptKey = NULL;
  152. BOOL fResult = FALSE;
  153. DWORD dwActualBlobSize;
  154. if ( !CryptAcquireContext(&hContext, NULL, NULL, PROV_RSA_FULL, CRYPT_SILENT | CRYPT_VERIFYCONTEXT))
  155. {
  156. fwprintf(stderr, L"Unable to aquire cryptological context, error %ld.\n", ::GetLastError());
  157. goto Exit;
  158. }
  159. ZeroMemory(prgbBuffer, cbPublicKeyTokenLength);
  160. //
  161. // Set up the public key info blob for hashing. Import the key to a real
  162. // HCRYPTKEY, then export the bits back out to a buffer. Set up the various
  163. // other settings in the blob as well, the type of key and the alg. used to
  164. // sign it.
  165. //
  166. if ( !CryptImportPublicKeyInfoEx(
  167. hContext,
  168. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  169. pKeyInfo,
  170. CALG_RSA_SIGN,
  171. 0,
  172. NULL,
  173. &hCryptKey) )
  174. {
  175. fwprintf(stderr, L"Unable to import the public key from this certificate. Error %ld.\n", ::GetLastError());
  176. goto Exit;
  177. }
  178. pKeyBlobWorkspace->KeyLength =
  179. sizeof(rgbWorkingSpace) - offsetof(SXS_PUBLIC_KEY_INFO, pbKeyInfo);
  180. if ( !CryptExportKey(
  181. hCryptKey,
  182. NULL,
  183. PUBLICKEYBLOB,
  184. 0,
  185. pKeyBlobWorkspace->pbKeyInfo,
  186. &pKeyBlobWorkspace->KeyLength) )
  187. {
  188. fwprintf(stderr, L"Unable to extract public key bits from this certificate. Error %ld.\n", ::GetLastError());
  189. goto Exit;
  190. }
  191. pKeyBlobWorkspace->SigAlgID = CALG_RSA_SIGN;
  192. pKeyBlobWorkspace->HashAlgID = CALG_SHA1;
  193. dwActualBlobSize = pKeyBlobWorkspace->KeyLength + offsetof(SXS_PUBLIC_KEY_INFO, pbKeyInfo);
  194. //
  195. // We now need to hash the public key bytes with SHA1.
  196. //
  197. if (!HashAndSwizzleKey(
  198. hContext,
  199. (PBYTE)pKeyBlobWorkspace,
  200. dwActualBlobSize,
  201. prgbBuffer,
  202. cbPublicKeyTokenLength))
  203. {
  204. goto Exit;
  205. }
  206. fResult = TRUE;
  207. Exit:
  208. if ( hCryptKey != NULL )
  209. {
  210. CryptDestroyKey(hCryptKey);
  211. hCryptKey = NULL;
  212. }
  213. if ( hContext != NULL )
  214. {
  215. CryptReleaseContext(hContext, 0);
  216. hContext = NULL;
  217. }
  218. return fResult;
  219. }
  220. int __cdecl wmain( int argc, WCHAR *argv[] )
  221. {
  222. HCERTSTORE hCertStore = NULL;
  223. PCCERT_CONTEXT pCertContext = NULL;
  224. BOOL fNoLogoDisplay = FALSE;
  225. BOOL fQuiet = FALSE;
  226. DWORD STRONG_NAME_LENGTH = 8;
  227. PCWSTR pcwszFilename = NULL;
  228. DWORD dwRetVal = ERROR_SUCCESS;
  229. //
  230. // Quick check - are we to display the logo?
  231. for ( int j = 0; j < argc; j++ )
  232. {
  233. if (lstrcmpiW(argv[j], L"-nologo") == 0)
  234. fNoLogoDisplay = TRUE;
  235. }
  236. if ( !fNoLogoDisplay )
  237. wprintf(wchMicrosoftLogo);
  238. //
  239. // Now go look for the arguments.
  240. //
  241. if ((argc < 2) || !ParseArgs( argv, argc, &pcwszFilename, &fQuiet ))
  242. {
  243. DispUsage( argv[0] );
  244. dwRetVal = ERROR_INVALID_PARAMETER;
  245. goto Exit;
  246. }
  247. else if ( !pcwszFilename )
  248. {
  249. DispUsage( argv[0] );
  250. dwRetVal = ERROR_INVALID_PARAMETER;
  251. goto Exit;
  252. }
  253. hCertStore = CertOpenStore(
  254. CERT_STORE_PROV_FILENAME,
  255. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  256. NULL,
  257. CERT_STORE_OPEN_EXISTING_FLAG,
  258. (void*)pcwszFilename);
  259. if ( !hCertStore )
  260. {
  261. fwprintf(
  262. stderr,
  263. L"Unable to open the input file %ls, error %ld\n",
  264. pcwszFilename,
  265. dwRetVal = ::GetLastError());
  266. goto Exit;
  267. }
  268. while ( pCertContext = CertEnumCertificatesInStore( hCertStore, pCertContext ) )
  269. {
  270. if ( !pCertContext->pCertInfo )
  271. {
  272. fwprintf( stderr, L"Oddity with file %ls - Certificate information not decodable\n" );
  273. continue;
  274. }
  275. WCHAR wsNiceName[BUFFER_SIZE] = { L'\0' };
  276. BYTE bBuffer[BUFFER_SIZE];
  277. SIZE_T cbBuffer = BUFFER_SIZE;
  278. DWORD dwKeyLength;
  279. PCERT_PUBLIC_KEY_INFO pKeyInfo = &(pCertContext->pCertInfo->SubjectPublicKeyInfo);
  280. DWORD dwDump;
  281. ZeroMemory( wsNiceName, sizeof( wsNiceName ) / sizeof( *wsNiceName ) );
  282. dwDump = CertGetNameStringW(
  283. pCertContext,
  284. CERT_NAME_FRIENDLY_DISPLAY_TYPE,
  285. CERT_NAME_ISSUER_FLAG,
  286. NULL,
  287. wsNiceName,
  288. BUFFER_SIZE
  289. );
  290. if ( dwDump == 0 )
  291. {
  292. fwprintf(stderr, L"Unable to get certificate name string! Error %ld.", GetLastError());
  293. wcscpy(wsNiceName, L"(Unknown)");
  294. }
  295. if ( !fQuiet )
  296. {
  297. dwKeyLength = CertGetPublicKeyLength( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pKeyInfo );
  298. wprintf(L"\nCertificate: \"%ls\" - %ld bits long\n", wsNiceName, dwKeyLength);
  299. if ( dwKeyLength < 2048 )
  300. {
  301. wprintf(L"\tWarning! This key is too short to sign SxS assemblies with.\n\tSigning keys need to be 2048 bits or more.\n");
  302. }
  303. }
  304. if (!GetTokenOfKey( pKeyInfo, bBuffer, cbBuffer ))
  305. {
  306. fwprintf(stderr, L"Unable to generate public key token for this certificate.\n");
  307. }
  308. else
  309. {
  310. if ( !fQuiet ) wprintf(L"\tpublicKeyToken=\"");
  311. for ( SIZE_T i = 0; i < cbBuffer; i++ )
  312. {
  313. wprintf(L"%02x", bBuffer[i] );
  314. }
  315. if ( !fQuiet )
  316. wprintf(L"\"\n");
  317. else
  318. wprintf(L"\n");
  319. }
  320. }
  321. Exit:
  322. if ( hCertStore != NULL )
  323. {
  324. CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
  325. hCertStore = NULL;
  326. }
  327. return dwRetVal;
  328. }