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