Reactos

[CERTUTIL] Add -asn verb

+712 -159
+4 -2
base/applications/cmdutils/certutil/CMakeLists.txt
··· 2 2 include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils) 3 3 4 4 list(APPEND SOURCE 5 - certutil.c 5 + asn.cpp 6 + certutil.cpp 7 + hashfile.cpp 6 8 precomp.h) 7 9 8 10 add_executable(certutil ${SOURCE}) 9 11 set_module_type(certutil win32cui UNICODE) 10 12 target_link_libraries(certutil conutils ${PSEH_LIB}) 11 - add_importlibs(certutil advapi32 msvcrt kernel32) 13 + add_importlibs(certutil crypt32 advapi32 msvcrt kernel32) 12 14 add_pch(certutil precomp.h SOURCE) 13 15 add_cd_file(TARGET certutil DESTINATION reactos/system32 FOR all)
+508
base/applications/cmdutils/certutil/asn.cpp
··· 1 + /* 2 + * PROJECT: ReactOS certutil 3 + * LICENSE: MIT (https://spdx.org/licenses/MIT) 4 + * PURPOSE: CertUtil asn implementation 5 + * COPYRIGHT: Copyright 2020 Mark Jansen (mark.jansen@reactos.org) 6 + * 7 + * NOTES: 8 + * To keep it simple, Tag and Class are combined in one identifier 9 + * See for more details: 10 + * https://en.wikipedia.org/wiki/X.690#BER_encoding 11 + * https://www.strozhevsky.com/free_docs/asn1_by_simple_words.pdf 12 + * http://mikk.net/~chris/asn1.pdf 13 + * 14 + * And for a test suite: 15 + * https://github.com/YuryStrozhevsky/asn1-test-suite 16 + */ 17 + 18 + #include "precomp.h" 19 + #include <math.h> 20 + #include <wincrypt.h> 21 + #include <stdlib.h> 22 + 23 + 24 + #define ASN_TAG_IS_CONSTRUCTED 0x20 25 + 26 + 27 + #define ASN_TAG_BITSTRING 0x03 28 + #define ASN_TAG_OCTET_STRING 0x04 29 + #define ASN_TAG_OBJECT_ID 0x06 30 + 31 + #define ASN_TAG_SEQUENCE_RAW 0x10 32 + #define ASN_TAG_SET_RAW 0x11 33 + 34 + #define ASN_TAG_SEQUENCE 0x30 35 + #define ASN_TAG_SET 0x31 36 + 37 + 38 + #define ASN_TAG_CONTEXT_SPECIFIC 0x80 39 + #define ASN_TAG_CONTEXT_SPECIFIC_N(n) (ASN_TAG_CONTEXT_SPECIFIC | (n)) 40 + 41 + #define ASN_TAG_OPTIONAL 0xA0 42 + #define ASN_TAG_OPTIONAL_N(n) (ASN_TAG_OPTIONAL | (n)) 43 + 44 + /* NOTE: These names are not the names listed in f.e. the wikipedia pages, 45 + they are made to look like MS's names for this */ 46 + LPCWSTR TagToName(DWORD dwTag) 47 + { 48 + switch (dwTag) 49 + { 50 + case 0x0: return L"EOC"; 51 + case 0x1: return L"BOOL"; 52 + case 0x2: return L"INTEGER"; 53 + case ASN_TAG_BITSTRING: return L"BIT_STRING"; 54 + case ASN_TAG_OCTET_STRING: return L"OCTET_STRING"; 55 + case 0x5: return L"NULL"; 56 + case ASN_TAG_OBJECT_ID: return L"OBJECT_ID"; 57 + case 0x7: return L"Object Descriptor"; 58 + case 0x8: return L"EXTERNAL"; 59 + case 0x9: return L"REAL"; 60 + case 0xA: return L"ENUMERATED"; 61 + case 0xB: return L"EMBEDDED PDV"; 62 + case 0xC: return L"UTF8String"; 63 + case 0xD: return L"RELATIVE-OID"; 64 + case 0xE: return L"TIME"; 65 + case 0xF: return L"Reserved"; 66 + case ASN_TAG_SEQUENCE_RAW: __debugbreak(); return L"SEQUENCE_RAW"; 67 + case ASN_TAG_SET_RAW: __debugbreak(); return L"SET_RAW"; 68 + case 0x12: return L"NumericString"; 69 + case 0x13: return L"PRINTABLE_STRING"; 70 + case 0x14: return L"T61String"; 71 + case 0x15: return L"VideotexString"; 72 + case 0x16: return L"IA5String"; 73 + case 0x17: return L"UTC_TIME"; 74 + case 0x18: return L"GeneralizedTime"; 75 + case 0x19: return L"GraphicString"; 76 + case 0x1A: return L"VisibleString"; 77 + case 0x1B: return L"GeneralString"; 78 + case 0x1C: return L"UniversalString"; 79 + case 0x1D: return L"CHARACTER STRING"; 80 + case 0x1E: return L"BMPString"; 81 + case 0x1F: return L"DATE"; 82 + case 0x20: return L"CONSTRUCTED"; 83 + 84 + case ASN_TAG_SEQUENCE: return L"SEQUENCE"; 85 + case ASN_TAG_SET: return L"SET"; 86 + 87 + 88 + case ASN_TAG_CONTEXT_SPECIFIC_N(0): return L"CONTEXT_SPECIFIC[0]"; 89 + case ASN_TAG_CONTEXT_SPECIFIC_N(1): return L"CONTEXT_SPECIFIC[1]"; 90 + case ASN_TAG_CONTEXT_SPECIFIC_N(2): return L"CONTEXT_SPECIFIC[2]"; 91 + case ASN_TAG_CONTEXT_SPECIFIC_N(3): return L"CONTEXT_SPECIFIC[3]"; 92 + case ASN_TAG_CONTEXT_SPECIFIC_N(4): return L"CONTEXT_SPECIFIC[4]"; 93 + case ASN_TAG_CONTEXT_SPECIFIC_N(5): return L"CONTEXT_SPECIFIC[5]"; 94 + case ASN_TAG_CONTEXT_SPECIFIC_N(6): return L"CONTEXT_SPECIFIC[6]"; 95 + case ASN_TAG_CONTEXT_SPECIFIC_N(7): return L"CONTEXT_SPECIFIC[7]"; 96 + case ASN_TAG_CONTEXT_SPECIFIC_N(8): return L"CONTEXT_SPECIFIC[8]"; 97 + case ASN_TAG_CONTEXT_SPECIFIC_N(9): return L"CONTEXT_SPECIFIC[9]"; 98 + /* Experiments show that Windows' certutil only goes up to 9 */ 99 + 100 + 101 + case ASN_TAG_OPTIONAL_N(0): return L"OPTIONAL[0]"; 102 + case ASN_TAG_OPTIONAL_N(1): return L"OPTIONAL[1]"; 103 + case ASN_TAG_OPTIONAL_N(2): return L"OPTIONAL[2]"; 104 + case ASN_TAG_OPTIONAL_N(3): return L"OPTIONAL[3]"; 105 + case ASN_TAG_OPTIONAL_N(4): return L"OPTIONAL[4]"; 106 + case ASN_TAG_OPTIONAL_N(5): return L"OPTIONAL[5]"; 107 + case ASN_TAG_OPTIONAL_N(6): return L"OPTIONAL[6]"; 108 + case ASN_TAG_OPTIONAL_N(7): return L"OPTIONAL[7]"; 109 + case ASN_TAG_OPTIONAL_N(8): return L"OPTIONAL[8]"; 110 + case ASN_TAG_OPTIONAL_N(9): return L"OPTIONAL[9]"; 111 + /* Experiments show that Windows' certutil only goes up to 9 */ 112 + 113 + default: 114 + return L"???"; 115 + } 116 + } 117 + 118 + BOOL Move(DWORD dwLen, PBYTE& pData, DWORD& dwSize) 119 + { 120 + if (dwSize < dwLen) 121 + return FALSE; 122 + 123 + pData += dwLen; 124 + dwSize -= dwLen; 125 + 126 + return TRUE; 127 + } 128 + 129 + BOOL ParseTag(PBYTE& pData, DWORD& dwSize, DWORD& dwTagAndClass) 130 + { 131 + if (dwSize == 0) 132 + return FALSE; 133 + 134 + /* Is this a long form? */ 135 + if ((pData[0] & 0x1f) != 0x1f) 136 + { 137 + /* No, so extract the tag and class (in one identifier) */ 138 + dwTagAndClass = pData[0]; 139 + return Move(1, pData, dwSize); 140 + } 141 + 142 + DWORD dwClass = (pData[0] & 0xE0) >> 5; 143 + dwTagAndClass = 0; 144 + DWORD n; 145 + for (n = 1; n < dwSize; ++n) 146 + { 147 + dwTagAndClass <<= 7; 148 + dwTagAndClass |= (pData[n] & 0x7f); 149 + 150 + if (!(pData[n] & 0x80)) 151 + { 152 + break; 153 + } 154 + } 155 + 156 + Move(n, pData, dwSize); 157 + 158 + /* Any number bigger than this, we shift data out! */ 159 + if (n > 4) 160 + return FALSE; 161 + 162 + /* Just drop this in the hightest bits*/ 163 + dwTagAndClass |= (dwClass << (32-3)); 164 + 165 + return TRUE; 166 + } 167 + 168 + BOOL ParseLength(PBYTE& pData, DWORD& dwSize, DWORD& dwLength) 169 + { 170 + if (dwSize == 0) 171 + return FALSE; 172 + 173 + if (!(pData[0] & 0x80)) 174 + { 175 + dwLength = pData[0]; 176 + return Move(1, pData, dwSize); 177 + } 178 + 179 + DWORD dwBytes = pData[0] & 0x7f; 180 + if (dwBytes == 0 || dwBytes > 8 || dwBytes + 1 > dwSize) 181 + { 182 + return FALSE; 183 + } 184 + 185 + dwLength = 0; 186 + for (DWORD n = 0; n < dwBytes; ++n) 187 + { 188 + dwLength <<= 8; 189 + dwLength += pData[1 + n]; 190 + } 191 + 192 + return Move(dwBytes + 1, pData, dwSize); 193 + } 194 + 195 + 196 + DWORD HexDump(PBYTE pRoot, PBYTE pData, DWORD dwSize, PWSTR wszPrefix) 197 + { 198 + while (TRUE) 199 + { 200 + SIZE_T Address = pData - pRoot; 201 + ConPrintf(StdOut, L"%04x: ", Address); 202 + ConPuts(StdOut, wszPrefix); 203 + 204 + for (DWORD n = 0; n < min(dwSize, 0x10); ++n) 205 + { 206 + ConPrintf(StdOut, L"%02x ", pData[n]); 207 + } 208 + 209 + if (dwSize <= 0x10) 210 + break; 211 + 212 + Move(0x10, pData, dwSize); 213 + ConPuts(StdOut, L"\n"); 214 + } 215 + 216 + return 3 * dwSize; 217 + } 218 + 219 + void PrintTag(PBYTE pRoot, PBYTE pHeader, DWORD dwTag, DWORD dwTagLength, PBYTE pData, PWSTR wszPrefix) 220 + { 221 + DWORD dwRemainder = HexDump(pRoot, pHeader, pData - pHeader, wszPrefix); 222 + 223 + LPCWSTR wszTag = TagToName(dwTag); 224 + DWORD dwPadding = dwRemainder + wcslen(wszPrefix); 225 + while (dwPadding > 50) 226 + dwPadding -= 50; 227 + ConPrintf(StdOut, L"%*s; %s (%x Bytes)\n", 50 - dwPadding, L"", wszTag, dwTagLength); 228 + } 229 + 230 + struct OID_NAMES 231 + { 232 + CHAR* Oid; 233 + LPCWSTR Names[20]; 234 + DWORD NumberOfNames; 235 + }; 236 + 237 + BOOL WINAPI CryptOIDEnumCallback(_In_ PCCRYPT_OID_INFO pInfo, _Inout_opt_ void *pvArg) 238 + { 239 + OID_NAMES* Names = (OID_NAMES*)pvArg; 240 + 241 + if (pInfo && pInfo->pszOID && !_stricmp(pInfo->pszOID, Names->Oid)) 242 + { 243 + if (Names->NumberOfNames < RTL_NUMBER_OF(Names->Names)) 244 + { 245 + for (DWORD n = 0; n < Names->NumberOfNames; ++n) 246 + { 247 + // We already have this.. 248 + if (!_wcsicmp(Names->Names[n], pInfo->pwszName)) 249 + return TRUE; 250 + } 251 + 252 + Names->Names[Names->NumberOfNames++] = pInfo->pwszName; 253 + } 254 + } 255 + 256 + return TRUE; 257 + } 258 + 259 + void PrintOID(PBYTE pRoot, PBYTE pHeader, PBYTE pData, DWORD dwSize, PWSTR wszPrefix) 260 + { 261 + /* CryptFindOIDInfo expects the OID to be in ANSI.. */ 262 + CHAR szOID[250]; 263 + CHAR* ptr = szOID; 264 + size_t cchRemaining = RTL_NUMBER_OF(szOID); 265 + 266 + /* CryptFindOIDInfo just returns the first, we want multiple */ 267 + OID_NAMES Names = {0}; 268 + 269 + if (dwSize == 0) 270 + return; 271 + 272 + DWORD dwValue = 0, count = 0; 273 + for (DWORD n = 0; n < dwSize; ++n) 274 + { 275 + dwValue <<= 7; 276 + dwValue |= pData[n] & 0x7f; 277 + 278 + if (pData[n] & 0x80) 279 + { 280 + if (++count >= 4) 281 + break; 282 + continue; 283 + } 284 + count = 0; 285 + 286 + /* First & second octet have a special encoding */ 287 + if (ptr == szOID) 288 + { 289 + DWORD id1 = dwValue / 40; 290 + DWORD id2 = dwValue % 40; 291 + 292 + /* The first one can only be 0, 1 or 2, so handle special case: tc24.ber */ 293 + if (id1 > 2) 294 + { 295 + id2 += (id1 - 2) * 40; 296 + id1 = 2; 297 + } 298 + StringCchPrintfExA(ptr, cchRemaining, &ptr, &cchRemaining, 0, "%d.%d", id1, id2); 299 + } 300 + else 301 + { 302 + StringCchPrintfExA(ptr, cchRemaining, &ptr, &cchRemaining, 0, ".%d", dwValue); 303 + } 304 + 305 + dwValue = 0; 306 + } 307 + 308 + if (dwValue || count) 309 + { 310 + /* We cannot format this, so just add abort */ 311 + return; 312 + } 313 + 314 + SIZE_T Address = pData - pRoot; 315 + /* Pad with spaces instead of printing the address again */ 316 + DWORD addrDigits = (DWORD)log10((double)Address) + 1; 317 + ConPrintf(StdOut, L"%*s ", max(addrDigits, 4), L""); 318 + ConPrintf(StdOut, L"%s; %S", wszPrefix, szOID); 319 + 320 + Names.Oid = szOID; 321 + 322 + /* The order does not match a naive call with '0'... */ 323 + CryptEnumOIDInfo(0, 0, &Names, CryptOIDEnumCallback); 324 + 325 + for (DWORD n = 0; n < Names.NumberOfNames; ++n) 326 + { 327 + if (n == 0) 328 + ConPrintf(StdOut, L" %s", Names.Names[n]); 329 + else if (n == 1) 330 + ConPrintf(StdOut, L" (%s", Names.Names[n]); 331 + else 332 + ConPrintf(StdOut, L" / %s", Names.Names[n]); 333 + } 334 + 335 + ConPrintf(StdOut, L"%s\n", Names.NumberOfNames > 1 ? L")" : L""); 336 + } 337 + 338 + 339 + BOOL ParseAsn(PBYTE pRoot, PBYTE pData, DWORD dwSize, PWSTR wszPrefix, BOOL fPrint) 340 + { 341 + while (dwSize) 342 + { 343 + PBYTE pHeader = pData; 344 + DWORD dwTagAndClass; 345 + 346 + if (!ParseTag(pData, dwSize, dwTagAndClass)) 347 + { 348 + if (fPrint) 349 + ConPrintf(StdOut, L"CertUtil: -asn command failed to parse tag near 0x%x\n", pHeader - pRoot); 350 + return FALSE; 351 + } 352 + 353 + DWORD dwTagLength; 354 + if (!ParseLength(pData, dwSize, dwTagLength)) 355 + { 356 + if (fPrint) 357 + ConPrintf(StdOut, L"CertUtil: -asn command failed to parse tag length near 0x%x\n", pHeader - pRoot); 358 + return FALSE; 359 + } 360 + 361 + if (dwTagLength > dwSize) 362 + { 363 + if (fPrint) 364 + ConPrintf(StdOut, L"CertUtil: -asn command malformed tag length near 0x%x\n", pHeader - pRoot); 365 + return FALSE; 366 + } 367 + 368 + 369 + if (fPrint) 370 + PrintTag(pRoot, pHeader, dwTagAndClass, dwTagLength, pData, wszPrefix); 371 + 372 + size_t len = wcslen(wszPrefix); 373 + StringCchCatW(wszPrefix, MAX_PATH, dwTagLength != dwSize ? L"| " : L" "); 374 + 375 + if (dwTagAndClass & ASN_TAG_IS_CONSTRUCTED) 376 + { 377 + if (!ParseAsn(pRoot, pData, dwTagLength, wszPrefix, fPrint)) 378 + { 379 + return FALSE; 380 + } 381 + } 382 + else 383 + { 384 + if (fPrint) 385 + { 386 + /* Special case for a bit string / octet string */ 387 + if ((dwTagAndClass == ASN_TAG_BITSTRING || dwTagAndClass == ASN_TAG_OCTET_STRING) && dwTagLength) 388 + { 389 + if (dwTagAndClass == ASN_TAG_BITSTRING) 390 + { 391 + /* First, we print the 'unused bits' field of the bit string */ 392 + HexDump(pRoot, pData, 1, wszPrefix); 393 + ConPuts(StdOut, L"\n"); 394 + 395 + /* Move past it */ 396 + Move(1, pData, dwSize); 397 + dwTagLength--; 398 + } 399 + 400 + /* Do we have any data left? */ 401 + if (dwTagLength) 402 + { 403 + /* Try to parse this as ASN */ 404 + if (ParseAsn(pRoot, pData, dwTagLength, wszPrefix, FALSE)) 405 + { 406 + /* We succeeded, this _could_ be ASN, so display it as if it is */ 407 + if (!ParseAsn(pRoot, pData, dwTagLength, wszPrefix, TRUE)) 408 + { 409 + /* Uhhh, did someone edit the data? */ 410 + ConPrintf(StdOut, L"CertUtil: -asn command unexpected failure parsing tag near 0x%x\n", pData - pRoot); 411 + return FALSE; 412 + } 413 + 414 + /* Move past what we just parsed */ 415 + Move(dwTagLength, pData, dwSize); 416 + /* Lie about this so that we don't also print a hexdump */ 417 + dwTagLength = 0; 418 + } 419 + } 420 + } 421 + 422 + /* Is there any data (left) to print? */ 423 + if (dwTagLength) 424 + { 425 + HexDump(pRoot, pData, dwTagLength, wszPrefix); 426 + ConPuts(StdOut, L"\n"); 427 + 428 + StringCchCatW(wszPrefix, MAX_PATH, L" "); 429 + 430 + /* Do we have additional formatters? */ 431 + switch (dwTagAndClass) 432 + { 433 + case ASN_TAG_OBJECT_ID: 434 + PrintOID(pRoot, pHeader, pData, dwTagLength, wszPrefix); 435 + break; 436 + default: 437 + break; 438 + } 439 + } 440 + } 441 + } 442 + 443 + wszPrefix[len] = '\0'; 444 + 445 + if (!Move(dwTagLength, pData, dwSize)) 446 + { 447 + /* This should not be possible, it was checked before! */ 448 + return FALSE; 449 + } 450 + } 451 + 452 + return TRUE; 453 + } 454 + 455 + 456 + BOOL asn_dump(LPCWSTR Filename) 457 + { 458 + HANDLE hFile = CreateFileW(Filename, GENERIC_READ, FILE_SHARE_READ, NULL, 459 + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); 460 + 461 + if (hFile == INVALID_HANDLE_VALUE) 462 + { 463 + ConPrintf(StdOut, L"CertUtil: -asn command failed to open: %d\n", GetLastError()); 464 + return FALSE; 465 + } 466 + 467 + DWORD dwSize = GetFileSize(hFile, NULL); 468 + if (dwSize == INVALID_FILE_SIZE) 469 + { 470 + ConPrintf(StdOut, L"CertUtil: -asn command failed to get file size: %d\n", GetLastError()); 471 + CloseHandle(hFile); 472 + return FALSE; 473 + } 474 + 475 + if (dwSize == 0) 476 + { 477 + ConPrintf(StdOut, L"CertUtil: -asn command got an empty file\n"); 478 + CloseHandle(hFile); 479 + return FALSE; 480 + } 481 + 482 + PBYTE pData = (PBYTE)LocalAlloc(0, dwSize); 483 + if (!pData) 484 + { 485 + ConPrintf(StdOut, L"CertUtil: -asn command failed to allocate: %d\n", GetLastError()); 486 + CloseHandle(hFile); 487 + return FALSE; 488 + } 489 + 490 + DWORD cbRead; 491 + BOOL fRead = ReadFile(hFile, pData, dwSize, &cbRead, NULL); 492 + DWORD dwErr = GetLastError(); 493 + CloseHandle(hFile); 494 + 495 + if (!fRead || cbRead != dwSize) 496 + { 497 + ConPrintf(StdOut, L"CertUtil: -asn command failed to read: %d\n", dwErr); 498 + LocalFree(pData); 499 + return FALSE; 500 + } 501 + 502 + WCHAR Buffer[MAX_PATH] = {0}; 503 + BOOL fSucceeded = ParseAsn(pData, pData, dwSize, Buffer, TRUE); 504 + 505 + LocalFree(pData); 506 + return fSucceeded; 507 + } 508 +
-155
base/applications/cmdutils/certutil/certutil.c
··· 1 - /* 2 - * PROJECT: ReactOS certutil 3 - * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 - * PURPOSE: CertUtil stub 5 - * COPYRIGHT: Copyright 2020 Mark Jansen (mark.jansen@reactos.org) 6 - * 7 - * Note: Only -hashfile is implemented for now, the rest is not present! 8 - */ 9 - 10 - #include "precomp.h" 11 - #include <wincrypt.h> 12 - #include <stdlib.h> 13 - 14 - 15 - static BOOL hash_file(LPCWSTR Filename) 16 - { 17 - HCRYPTPROV hProv; 18 - BOOL bSuccess = FALSE; 19 - 20 - HANDLE hFile = CreateFileW(Filename, GENERIC_READ, FILE_SHARE_READ, NULL, 21 - OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); 22 - 23 - if (hFile == INVALID_HANDLE_VALUE) 24 - { 25 - ConPrintf(StdOut, L"CertUtil: -hashfile command failed: %d\n", GetLastError()); 26 - return bSuccess; 27 - } 28 - 29 - if (CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) 30 - { 31 - HCRYPTHASH hHash; 32 - 33 - if (CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) 34 - { 35 - BYTE Buffer[2048]; 36 - DWORD cbRead; 37 - 38 - while ((bSuccess = ReadFile(hFile, Buffer, sizeof(Buffer), &cbRead, NULL))) 39 - { 40 - if (cbRead == 0) 41 - break; 42 - 43 - if (!CryptHashData(hHash, Buffer, cbRead, 0)) 44 - { 45 - bSuccess = FALSE; 46 - ConPrintf(StdOut, L"CertUtil: -hashfile command failed to hash: %d\n", GetLastError()); 47 - break; 48 - } 49 - } 50 - 51 - if (bSuccess) 52 - { 53 - BYTE rgbHash[20]; 54 - DWORD cbHash, n; 55 - 56 - if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0)) 57 - { 58 - ConPrintf(StdOut, L"SHA1 hash of %s:\n", Filename); 59 - for (n = 0; n < cbHash; ++n) 60 - { 61 - ConPrintf(StdOut, L"%02x", rgbHash[n]); 62 - } 63 - ConPuts(StdOut, L"\n"); 64 - } 65 - else 66 - { 67 - ConPrintf(StdOut, L"CertUtil: -hashfile command failed to extract hash: %d\n", GetLastError()); 68 - bSuccess = FALSE; 69 - } 70 - } 71 - 72 - CryptDestroyHash(hHash); 73 - } 74 - else 75 - { 76 - ConPrintf(StdOut, L"CertUtil: -hashfile command no algorithm: %d\n", GetLastError()); 77 - } 78 - 79 - CryptReleaseContext(hProv, 0); 80 - } 81 - else 82 - { 83 - ConPrintf(StdOut, L"CertUtil: -hashfile command no context: %d\n", GetLastError()); 84 - } 85 - 86 - CloseHandle(hFile); 87 - return bSuccess; 88 - } 89 - 90 - 91 - static void print_usage() 92 - { 93 - ConPuts(StdOut, L"Verbs:\n"); 94 - ConPuts(StdOut, L" -hashfile -- Display cryptographic hash over a file\n"); 95 - ConPuts(StdOut, L"\n"); 96 - ConPuts(StdOut, L"CertUtil -? -- Display a list of all verbs\n"); 97 - ConPuts(StdOut, L"CertUtil -hashfile -? -- Display help text for the 'hashfile' verb\n"); 98 - } 99 - 100 - int wmain(int argc, WCHAR *argv[]) 101 - { 102 - int n; 103 - 104 - /* Initialize the Console Standard Streams */ 105 - ConInitStdStreams(); 106 - 107 - if (argc == 1) /* i.e. no commandline arguments given */ 108 - { 109 - print_usage(); 110 - return EXIT_SUCCESS; 111 - } 112 - 113 - for (n = 1; n < argc; ++n) 114 - { 115 - if (!_wcsicmp(argv[n], L"-?")) 116 - { 117 - print_usage(); 118 - return EXIT_SUCCESS; 119 - } 120 - else if (!_wcsicmp(argv[n], L"-hashfile")) 121 - { 122 - if (argc == 3) 123 - { 124 - if (!_wcsicmp(argv[n+1], L"-?")) 125 - { 126 - print_usage(); 127 - return EXIT_SUCCESS; 128 - } 129 - else 130 - { 131 - if (!hash_file(argv[n+1])) 132 - { 133 - /* hash_file prints the failure itself */ 134 - return EXIT_FAILURE; 135 - } 136 - 137 - ConPuts(StdOut, L"CertUtil: -hashfile command completed successfully\n"); 138 - return EXIT_SUCCESS; 139 - } 140 - } 141 - else 142 - { 143 - ConPrintf(StdOut, L"CertUtil: -hashfile expected 1 argument, got %d\n", argc - 2); 144 - return EXIT_FAILURE; 145 - } 146 - } 147 - else 148 - { 149 - ConPrintf(StdOut, L"CertUtil: Unknown verb: %s\n", argv[n]); 150 - return EXIT_FAILURE; 151 - } 152 - } 153 - 154 - return EXIT_SUCCESS; 155 - }
+107
base/applications/cmdutils/certutil/certutil.cpp
··· 1 + /* 2 + * PROJECT: ReactOS certutil 3 + * LICENSE: MIT (https://spdx.org/licenses/MIT) 4 + * PURPOSE: CertUtil commandline handling 5 + * COPYRIGHT: Copyright 2020 Mark Jansen (mark.jansen@reactos.org) 6 + * 7 + * Note: Only -hashfile and -asn are implemented for now, the rest is not present! 8 + */ 9 + 10 + #include "precomp.h" 11 + #include <wincrypt.h> 12 + #include <stdlib.h> 13 + 14 + typedef struct 15 + { 16 + LPCWSTR Name; 17 + BOOL (*pfn)(LPCWSTR Filename); 18 + } Verb; 19 + 20 + 21 + Verb verbs[] = { 22 + { L"hashfile", hash_file }, 23 + { L"asn", asn_dump }, 24 + }; 25 + 26 + static void print_usage() 27 + { 28 + ConPuts(StdOut, L"Verbs:\n"); 29 + ConPuts(StdOut, L" -hashfile -- Display cryptographic hash over a file\n"); 30 + ConPuts(StdOut, L" -asn -- Display ASN.1 encoding of a file\n"); 31 + ConPuts(StdOut, L"\n"); 32 + ConPuts(StdOut, L"CertUtil -? -- Display a list of all verbs\n"); 33 + ConPuts(StdOut, L"CertUtil -hashfile -? -- Display help text for the 'hashfile' verb\n"); 34 + } 35 + 36 + 37 + Verb* MatchVerb(LPCWSTR arg) 38 + { 39 + if (arg[0] != '-' && arg[0] != '/') 40 + return NULL; 41 + 42 + for (size_t n = 0; n < RTL_NUMBER_OF(verbs); ++n) 43 + { 44 + if (!_wcsicmp(verbs[n].Name, arg + 1)) 45 + { 46 + return verbs + n; 47 + } 48 + } 49 + 50 + return NULL; 51 + } 52 + 53 + int wmain(int argc, WCHAR *argv[]) 54 + { 55 + int n; 56 + 57 + /* Initialize the Console Standard Streams */ 58 + ConInitStdStreams(); 59 + 60 + if (argc == 1) /* i.e. no commandline arguments given */ 61 + { 62 + print_usage(); 63 + return EXIT_SUCCESS; 64 + } 65 + 66 + for (n = 1; n < argc; ++n) 67 + { 68 + if (!_wcsicmp(argv[n], L"-?")) 69 + { 70 + print_usage(); 71 + return EXIT_SUCCESS; 72 + } 73 + 74 + Verb* verb = MatchVerb(argv[n]); 75 + 76 + if (verb) 77 + { 78 + if (argc != 3) 79 + { 80 + ConPrintf(StdOut, L"CertUtil: -%s expected 1 argument, got %d\n", verb->Name, argc - 2); 81 + return EXIT_FAILURE; 82 + } 83 + 84 + if (!_wcsicmp(argv[n+1], L"-?")) 85 + { 86 + print_usage(); 87 + return EXIT_SUCCESS; 88 + } 89 + 90 + if (!verb->pfn(argv[n+1])) 91 + { 92 + /* The verb prints the failure */ 93 + return EXIT_FAILURE; 94 + } 95 + 96 + ConPrintf(StdOut, L"CertUtil: -%s command completed successfully\n", verb->Name); 97 + return EXIT_SUCCESS; 98 + } 99 + else 100 + { 101 + ConPrintf(StdOut, L"CertUtil: Unknown verb: %s\n", argv[n]); 102 + return EXIT_FAILURE; 103 + } 104 + } 105 + 106 + return EXIT_SUCCESS; 107 + }
+87
base/applications/cmdutils/certutil/hashfile.cpp
··· 1 + /* 2 + * PROJECT: ReactOS certutil 3 + * LICENSE: MIT (https://spdx.org/licenses/MIT) 4 + * PURPOSE: CertUtil hashfile implementation 5 + * COPYRIGHT: Copyright 2020 Mark Jansen (mark.jansen@reactos.org) 6 + */ 7 + 8 + #include "precomp.h" 9 + #include <wincrypt.h> 10 + #include <stdlib.h> 11 + 12 + 13 + BOOL hash_file(LPCWSTR Filename) 14 + { 15 + HCRYPTPROV hProv; 16 + BOOL bSuccess = FALSE; 17 + 18 + HANDLE hFile = CreateFileW(Filename, GENERIC_READ, FILE_SHARE_READ, NULL, 19 + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); 20 + 21 + if (hFile == INVALID_HANDLE_VALUE) 22 + { 23 + ConPrintf(StdOut, L"CertUtil: -hashfile command failed: %d\n", GetLastError()); 24 + return bSuccess; 25 + } 26 + 27 + if (CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) 28 + { 29 + HCRYPTHASH hHash; 30 + 31 + if (CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) 32 + { 33 + BYTE Buffer[2048]; 34 + DWORD cbRead; 35 + 36 + while ((bSuccess = ReadFile(hFile, Buffer, sizeof(Buffer), &cbRead, NULL))) 37 + { 38 + if (cbRead == 0) 39 + break; 40 + 41 + if (!CryptHashData(hHash, Buffer, cbRead, 0)) 42 + { 43 + bSuccess = FALSE; 44 + ConPrintf(StdOut, L"CertUtil: -hashfile command failed to hash: %d\n", GetLastError()); 45 + break; 46 + } 47 + } 48 + 49 + if (bSuccess) 50 + { 51 + BYTE rgbHash[20]; 52 + DWORD cbHash, n; 53 + 54 + if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0)) 55 + { 56 + ConPrintf(StdOut, L"SHA1 hash of %s:\n", Filename); 57 + for (n = 0; n < cbHash; ++n) 58 + { 59 + ConPrintf(StdOut, L"%02x", rgbHash[n]); 60 + } 61 + ConPuts(StdOut, L"\n"); 62 + } 63 + else 64 + { 65 + ConPrintf(StdOut, L"CertUtil: -hashfile command failed to extract hash: %d\n", GetLastError()); 66 + bSuccess = FALSE; 67 + } 68 + } 69 + 70 + CryptDestroyHash(hHash); 71 + } 72 + else 73 + { 74 + ConPrintf(StdOut, L"CertUtil: -hashfile command no algorithm: %d\n", GetLastError()); 75 + } 76 + 77 + CryptReleaseContext(hProv, 0); 78 + } 79 + else 80 + { 81 + ConPrintf(StdOut, L"CertUtil: -hashfile command no context: %d\n", GetLastError()); 82 + } 83 + 84 + CloseHandle(hFile); 85 + return bSuccess; 86 + } 87 +
+6 -2
base/applications/cmdutils/certutil/precomp.h
··· 7 7 8 8 #include <windef.h> 9 9 #include <winbase.h> 10 - #include <winreg.h> 11 - #include <winuser.h> 10 + #include <strsafe.h> 12 11 13 12 #include <conutils.h> 13 + 14 + 15 + BOOL hash_file(LPCWSTR Filename); 16 + BOOL asn_dump(LPCWSTR Filename); 17 + 14 18 15 19 16 20 #endif /* __CERTUTIL_PRECOMP_H */