mirror of
https://github.com/reactos/reactos.git
synced 2025-07-10 12:34:14 +00:00
[CERTUTIL] Add -asn verb
This commit is contained in:
parent
c35bb8dd73
commit
05d71fa69b
5 changed files with 628 additions and 75 deletions
|
@ -2,12 +2,14 @@
|
|||
include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
|
||||
|
||||
list(APPEND SOURCE
|
||||
certutil.c
|
||||
asn.cpp
|
||||
certutil.cpp
|
||||
hashfile.cpp
|
||||
precomp.h)
|
||||
|
||||
add_executable(certutil ${SOURCE})
|
||||
set_module_type(certutil win32cui UNICODE)
|
||||
target_link_libraries(certutil conutils ${PSEH_LIB})
|
||||
add_importlibs(certutil advapi32 msvcrt kernel32)
|
||||
add_importlibs(certutil crypt32 advapi32 msvcrt kernel32)
|
||||
add_pch(certutil precomp.h SOURCE)
|
||||
add_cd_file(TARGET certutil DESTINATION reactos/system32 FOR all)
|
||||
|
|
508
base/applications/cmdutils/certutil/asn.cpp
Normal file
508
base/applications/cmdutils/certutil/asn.cpp
Normal file
|
@ -0,0 +1,508 @@
|
|||
/*
|
||||
* PROJECT: ReactOS certutil
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: CertUtil asn implementation
|
||||
* COPYRIGHT: Copyright 2020 Mark Jansen (mark.jansen@reactos.org)
|
||||
*
|
||||
* NOTES:
|
||||
* To keep it simple, Tag and Class are combined in one identifier
|
||||
* See for more details:
|
||||
* https://en.wikipedia.org/wiki/X.690#BER_encoding
|
||||
* https://www.strozhevsky.com/free_docs/asn1_by_simple_words.pdf
|
||||
* http://mikk.net/~chris/asn1.pdf
|
||||
*
|
||||
* And for a test suite:
|
||||
* https://github.com/YuryStrozhevsky/asn1-test-suite
|
||||
*/
|
||||
|
||||
#include "precomp.h"
|
||||
#include <math.h>
|
||||
#include <wincrypt.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#define ASN_TAG_IS_CONSTRUCTED 0x20
|
||||
|
||||
|
||||
#define ASN_TAG_BITSTRING 0x03
|
||||
#define ASN_TAG_OCTET_STRING 0x04
|
||||
#define ASN_TAG_OBJECT_ID 0x06
|
||||
|
||||
#define ASN_TAG_SEQUENCE_RAW 0x10
|
||||
#define ASN_TAG_SET_RAW 0x11
|
||||
|
||||
#define ASN_TAG_SEQUENCE 0x30
|
||||
#define ASN_TAG_SET 0x31
|
||||
|
||||
|
||||
#define ASN_TAG_CONTEXT_SPECIFIC 0x80
|
||||
#define ASN_TAG_CONTEXT_SPECIFIC_N(n) (ASN_TAG_CONTEXT_SPECIFIC | (n))
|
||||
|
||||
#define ASN_TAG_OPTIONAL 0xA0
|
||||
#define ASN_TAG_OPTIONAL_N(n) (ASN_TAG_OPTIONAL | (n))
|
||||
|
||||
/* NOTE: These names are not the names listed in f.e. the wikipedia pages,
|
||||
they are made to look like MS's names for this */
|
||||
LPCWSTR TagToName(DWORD dwTag)
|
||||
{
|
||||
switch (dwTag)
|
||||
{
|
||||
case 0x0: return L"EOC";
|
||||
case 0x1: return L"BOOL";
|
||||
case 0x2: return L"INTEGER";
|
||||
case ASN_TAG_BITSTRING: return L"BIT_STRING";
|
||||
case ASN_TAG_OCTET_STRING: return L"OCTET_STRING";
|
||||
case 0x5: return L"NULL";
|
||||
case ASN_TAG_OBJECT_ID: return L"OBJECT_ID";
|
||||
case 0x7: return L"Object Descriptor";
|
||||
case 0x8: return L"EXTERNAL";
|
||||
case 0x9: return L"REAL";
|
||||
case 0xA: return L"ENUMERATED";
|
||||
case 0xB: return L"EMBEDDED PDV";
|
||||
case 0xC: return L"UTF8String";
|
||||
case 0xD: return L"RELATIVE-OID";
|
||||
case 0xE: return L"TIME";
|
||||
case 0xF: return L"Reserved";
|
||||
case ASN_TAG_SEQUENCE_RAW: __debugbreak(); return L"SEQUENCE_RAW";
|
||||
case ASN_TAG_SET_RAW: __debugbreak(); return L"SET_RAW";
|
||||
case 0x12: return L"NumericString";
|
||||
case 0x13: return L"PRINTABLE_STRING";
|
||||
case 0x14: return L"T61String";
|
||||
case 0x15: return L"VideotexString";
|
||||
case 0x16: return L"IA5String";
|
||||
case 0x17: return L"UTC_TIME";
|
||||
case 0x18: return L"GeneralizedTime";
|
||||
case 0x19: return L"GraphicString";
|
||||
case 0x1A: return L"VisibleString";
|
||||
case 0x1B: return L"GeneralString";
|
||||
case 0x1C: return L"UniversalString";
|
||||
case 0x1D: return L"CHARACTER STRING";
|
||||
case 0x1E: return L"BMPString";
|
||||
case 0x1F: return L"DATE";
|
||||
case 0x20: return L"CONSTRUCTED";
|
||||
|
||||
case ASN_TAG_SEQUENCE: return L"SEQUENCE";
|
||||
case ASN_TAG_SET: return L"SET";
|
||||
|
||||
|
||||
case ASN_TAG_CONTEXT_SPECIFIC_N(0): return L"CONTEXT_SPECIFIC[0]";
|
||||
case ASN_TAG_CONTEXT_SPECIFIC_N(1): return L"CONTEXT_SPECIFIC[1]";
|
||||
case ASN_TAG_CONTEXT_SPECIFIC_N(2): return L"CONTEXT_SPECIFIC[2]";
|
||||
case ASN_TAG_CONTEXT_SPECIFIC_N(3): return L"CONTEXT_SPECIFIC[3]";
|
||||
case ASN_TAG_CONTEXT_SPECIFIC_N(4): return L"CONTEXT_SPECIFIC[4]";
|
||||
case ASN_TAG_CONTEXT_SPECIFIC_N(5): return L"CONTEXT_SPECIFIC[5]";
|
||||
case ASN_TAG_CONTEXT_SPECIFIC_N(6): return L"CONTEXT_SPECIFIC[6]";
|
||||
case ASN_TAG_CONTEXT_SPECIFIC_N(7): return L"CONTEXT_SPECIFIC[7]";
|
||||
case ASN_TAG_CONTEXT_SPECIFIC_N(8): return L"CONTEXT_SPECIFIC[8]";
|
||||
case ASN_TAG_CONTEXT_SPECIFIC_N(9): return L"CONTEXT_SPECIFIC[9]";
|
||||
/* Experiments show that Windows' certutil only goes up to 9 */
|
||||
|
||||
|
||||
case ASN_TAG_OPTIONAL_N(0): return L"OPTIONAL[0]";
|
||||
case ASN_TAG_OPTIONAL_N(1): return L"OPTIONAL[1]";
|
||||
case ASN_TAG_OPTIONAL_N(2): return L"OPTIONAL[2]";
|
||||
case ASN_TAG_OPTIONAL_N(3): return L"OPTIONAL[3]";
|
||||
case ASN_TAG_OPTIONAL_N(4): return L"OPTIONAL[4]";
|
||||
case ASN_TAG_OPTIONAL_N(5): return L"OPTIONAL[5]";
|
||||
case ASN_TAG_OPTIONAL_N(6): return L"OPTIONAL[6]";
|
||||
case ASN_TAG_OPTIONAL_N(7): return L"OPTIONAL[7]";
|
||||
case ASN_TAG_OPTIONAL_N(8): return L"OPTIONAL[8]";
|
||||
case ASN_TAG_OPTIONAL_N(9): return L"OPTIONAL[9]";
|
||||
/* Experiments show that Windows' certutil only goes up to 9 */
|
||||
|
||||
default:
|
||||
return L"???";
|
||||
}
|
||||
}
|
||||
|
||||
BOOL Move(DWORD dwLen, PBYTE& pData, DWORD& dwSize)
|
||||
{
|
||||
if (dwSize < dwLen)
|
||||
return FALSE;
|
||||
|
||||
pData += dwLen;
|
||||
dwSize -= dwLen;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ParseTag(PBYTE& pData, DWORD& dwSize, DWORD& dwTagAndClass)
|
||||
{
|
||||
if (dwSize == 0)
|
||||
return FALSE;
|
||||
|
||||
/* Is this a long form? */
|
||||
if ((pData[0] & 0x1f) != 0x1f)
|
||||
{
|
||||
/* No, so extract the tag and class (in one identifier) */
|
||||
dwTagAndClass = pData[0];
|
||||
return Move(1, pData, dwSize);
|
||||
}
|
||||
|
||||
DWORD dwClass = (pData[0] & 0xE0) >> 5;
|
||||
dwTagAndClass = 0;
|
||||
DWORD n;
|
||||
for (n = 1; n < dwSize; ++n)
|
||||
{
|
||||
dwTagAndClass <<= 7;
|
||||
dwTagAndClass |= (pData[n] & 0x7f);
|
||||
|
||||
if (!(pData[n] & 0x80))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Move(n, pData, dwSize);
|
||||
|
||||
/* Any number bigger than this, we shift data out! */
|
||||
if (n > 4)
|
||||
return FALSE;
|
||||
|
||||
/* Just drop this in the hightest bits*/
|
||||
dwTagAndClass |= (dwClass << (32-3));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ParseLength(PBYTE& pData, DWORD& dwSize, DWORD& dwLength)
|
||||
{
|
||||
if (dwSize == 0)
|
||||
return FALSE;
|
||||
|
||||
if (!(pData[0] & 0x80))
|
||||
{
|
||||
dwLength = pData[0];
|
||||
return Move(1, pData, dwSize);
|
||||
}
|
||||
|
||||
DWORD dwBytes = pData[0] & 0x7f;
|
||||
if (dwBytes == 0 || dwBytes > 8 || dwBytes + 1 > dwSize)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dwLength = 0;
|
||||
for (DWORD n = 0; n < dwBytes; ++n)
|
||||
{
|
||||
dwLength <<= 8;
|
||||
dwLength += pData[1 + n];
|
||||
}
|
||||
|
||||
return Move(dwBytes + 1, pData, dwSize);
|
||||
}
|
||||
|
||||
|
||||
DWORD HexDump(PBYTE pRoot, PBYTE pData, DWORD dwSize, PWSTR wszPrefix)
|
||||
{
|
||||
while (TRUE)
|
||||
{
|
||||
SIZE_T Address = pData - pRoot;
|
||||
ConPrintf(StdOut, L"%04x: ", Address);
|
||||
ConPuts(StdOut, wszPrefix);
|
||||
|
||||
for (DWORD n = 0; n < min(dwSize, 0x10); ++n)
|
||||
{
|
||||
ConPrintf(StdOut, L"%02x ", pData[n]);
|
||||
}
|
||||
|
||||
if (dwSize <= 0x10)
|
||||
break;
|
||||
|
||||
Move(0x10, pData, dwSize);
|
||||
ConPuts(StdOut, L"\n");
|
||||
}
|
||||
|
||||
return 3 * dwSize;
|
||||
}
|
||||
|
||||
void PrintTag(PBYTE pRoot, PBYTE pHeader, DWORD dwTag, DWORD dwTagLength, PBYTE pData, PWSTR wszPrefix)
|
||||
{
|
||||
DWORD dwRemainder = HexDump(pRoot, pHeader, pData - pHeader, wszPrefix);
|
||||
|
||||
LPCWSTR wszTag = TagToName(dwTag);
|
||||
DWORD dwPadding = dwRemainder + wcslen(wszPrefix);
|
||||
while (dwPadding > 50)
|
||||
dwPadding -= 50;
|
||||
ConPrintf(StdOut, L"%*s; %s (%x Bytes)\n", 50 - dwPadding, L"", wszTag, dwTagLength);
|
||||
}
|
||||
|
||||
struct OID_NAMES
|
||||
{
|
||||
CHAR* Oid;
|
||||
LPCWSTR Names[20];
|
||||
DWORD NumberOfNames;
|
||||
};
|
||||
|
||||
BOOL WINAPI CryptOIDEnumCallback(_In_ PCCRYPT_OID_INFO pInfo, _Inout_opt_ void *pvArg)
|
||||
{
|
||||
OID_NAMES* Names = (OID_NAMES*)pvArg;
|
||||
|
||||
if (pInfo && pInfo->pszOID && !_stricmp(pInfo->pszOID, Names->Oid))
|
||||
{
|
||||
if (Names->NumberOfNames < RTL_NUMBER_OF(Names->Names))
|
||||
{
|
||||
for (DWORD n = 0; n < Names->NumberOfNames; ++n)
|
||||
{
|
||||
// We already have this..
|
||||
if (!_wcsicmp(Names->Names[n], pInfo->pwszName))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Names->Names[Names->NumberOfNames++] = pInfo->pwszName;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void PrintOID(PBYTE pRoot, PBYTE pHeader, PBYTE pData, DWORD dwSize, PWSTR wszPrefix)
|
||||
{
|
||||
/* CryptFindOIDInfo expects the OID to be in ANSI.. */
|
||||
CHAR szOID[250];
|
||||
CHAR* ptr = szOID;
|
||||
size_t cchRemaining = RTL_NUMBER_OF(szOID);
|
||||
|
||||
/* CryptFindOIDInfo just returns the first, we want multiple */
|
||||
OID_NAMES Names = {0};
|
||||
|
||||
if (dwSize == 0)
|
||||
return;
|
||||
|
||||
DWORD dwValue = 0, count = 0;
|
||||
for (DWORD n = 0; n < dwSize; ++n)
|
||||
{
|
||||
dwValue <<= 7;
|
||||
dwValue |= pData[n] & 0x7f;
|
||||
|
||||
if (pData[n] & 0x80)
|
||||
{
|
||||
if (++count >= 4)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
count = 0;
|
||||
|
||||
/* First & second octet have a special encoding */
|
||||
if (ptr == szOID)
|
||||
{
|
||||
DWORD id1 = dwValue / 40;
|
||||
DWORD id2 = dwValue % 40;
|
||||
|
||||
/* The first one can only be 0, 1 or 2, so handle special case: tc24.ber */
|
||||
if (id1 > 2)
|
||||
{
|
||||
id2 += (id1 - 2) * 40;
|
||||
id1 = 2;
|
||||
}
|
||||
StringCchPrintfExA(ptr, cchRemaining, &ptr, &cchRemaining, 0, "%d.%d", id1, id2);
|
||||
}
|
||||
else
|
||||
{
|
||||
StringCchPrintfExA(ptr, cchRemaining, &ptr, &cchRemaining, 0, ".%d", dwValue);
|
||||
}
|
||||
|
||||
dwValue = 0;
|
||||
}
|
||||
|
||||
if (dwValue || count)
|
||||
{
|
||||
/* We cannot format this, so just add abort */
|
||||
return;
|
||||
}
|
||||
|
||||
SIZE_T Address = pData - pRoot;
|
||||
/* Pad with spaces instead of printing the address again */
|
||||
DWORD addrDigits = (DWORD)log10((double)Address) + 1;
|
||||
ConPrintf(StdOut, L"%*s ", max(addrDigits, 4), L"");
|
||||
ConPrintf(StdOut, L"%s; %S", wszPrefix, szOID);
|
||||
|
||||
Names.Oid = szOID;
|
||||
|
||||
/* The order does not match a naive call with '0'... */
|
||||
CryptEnumOIDInfo(0, 0, &Names, CryptOIDEnumCallback);
|
||||
|
||||
for (DWORD n = 0; n < Names.NumberOfNames; ++n)
|
||||
{
|
||||
if (n == 0)
|
||||
ConPrintf(StdOut, L" %s", Names.Names[n]);
|
||||
else if (n == 1)
|
||||
ConPrintf(StdOut, L" (%s", Names.Names[n]);
|
||||
else
|
||||
ConPrintf(StdOut, L" / %s", Names.Names[n]);
|
||||
}
|
||||
|
||||
ConPrintf(StdOut, L"%s\n", Names.NumberOfNames > 1 ? L")" : L"");
|
||||
}
|
||||
|
||||
|
||||
BOOL ParseAsn(PBYTE pRoot, PBYTE pData, DWORD dwSize, PWSTR wszPrefix, BOOL fPrint)
|
||||
{
|
||||
while (dwSize)
|
||||
{
|
||||
PBYTE pHeader = pData;
|
||||
DWORD dwTagAndClass;
|
||||
|
||||
if (!ParseTag(pData, dwSize, dwTagAndClass))
|
||||
{
|
||||
if (fPrint)
|
||||
ConPrintf(StdOut, L"CertUtil: -asn command failed to parse tag near 0x%x\n", pHeader - pRoot);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD dwTagLength;
|
||||
if (!ParseLength(pData, dwSize, dwTagLength))
|
||||
{
|
||||
if (fPrint)
|
||||
ConPrintf(StdOut, L"CertUtil: -asn command failed to parse tag length near 0x%x\n", pHeader - pRoot);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (dwTagLength > dwSize)
|
||||
{
|
||||
if (fPrint)
|
||||
ConPrintf(StdOut, L"CertUtil: -asn command malformed tag length near 0x%x\n", pHeader - pRoot);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
if (fPrint)
|
||||
PrintTag(pRoot, pHeader, dwTagAndClass, dwTagLength, pData, wszPrefix);
|
||||
|
||||
size_t len = wcslen(wszPrefix);
|
||||
StringCchCatW(wszPrefix, MAX_PATH, dwTagLength != dwSize ? L"| " : L" ");
|
||||
|
||||
if (dwTagAndClass & ASN_TAG_IS_CONSTRUCTED)
|
||||
{
|
||||
if (!ParseAsn(pRoot, pData, dwTagLength, wszPrefix, fPrint))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fPrint)
|
||||
{
|
||||
/* Special case for a bit string / octet string */
|
||||
if ((dwTagAndClass == ASN_TAG_BITSTRING || dwTagAndClass == ASN_TAG_OCTET_STRING) && dwTagLength)
|
||||
{
|
||||
if (dwTagAndClass == ASN_TAG_BITSTRING)
|
||||
{
|
||||
/* First, we print the 'unused bits' field of the bit string */
|
||||
HexDump(pRoot, pData, 1, wszPrefix);
|
||||
ConPuts(StdOut, L"\n");
|
||||
|
||||
/* Move past it */
|
||||
Move(1, pData, dwSize);
|
||||
dwTagLength--;
|
||||
}
|
||||
|
||||
/* Do we have any data left? */
|
||||
if (dwTagLength)
|
||||
{
|
||||
/* Try to parse this as ASN */
|
||||
if (ParseAsn(pRoot, pData, dwTagLength, wszPrefix, FALSE))
|
||||
{
|
||||
/* We succeeded, this _could_ be ASN, so display it as if it is */
|
||||
if (!ParseAsn(pRoot, pData, dwTagLength, wszPrefix, TRUE))
|
||||
{
|
||||
/* Uhhh, did someone edit the data? */
|
||||
ConPrintf(StdOut, L"CertUtil: -asn command unexpected failure parsing tag near 0x%x\n", pData - pRoot);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Move past what we just parsed */
|
||||
Move(dwTagLength, pData, dwSize);
|
||||
/* Lie about this so that we don't also print a hexdump */
|
||||
dwTagLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Is there any data (left) to print? */
|
||||
if (dwTagLength)
|
||||
{
|
||||
HexDump(pRoot, pData, dwTagLength, wszPrefix);
|
||||
ConPuts(StdOut, L"\n");
|
||||
|
||||
StringCchCatW(wszPrefix, MAX_PATH, L" ");
|
||||
|
||||
/* Do we have additional formatters? */
|
||||
switch (dwTagAndClass)
|
||||
{
|
||||
case ASN_TAG_OBJECT_ID:
|
||||
PrintOID(pRoot, pHeader, pData, dwTagLength, wszPrefix);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wszPrefix[len] = '\0';
|
||||
|
||||
if (!Move(dwTagLength, pData, dwSize))
|
||||
{
|
||||
/* This should not be possible, it was checked before! */
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
BOOL asn_dump(LPCWSTR Filename)
|
||||
{
|
||||
HANDLE hFile = CreateFileW(Filename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
ConPrintf(StdOut, L"CertUtil: -asn command failed to open: %d\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD dwSize = GetFileSize(hFile, NULL);
|
||||
if (dwSize == INVALID_FILE_SIZE)
|
||||
{
|
||||
ConPrintf(StdOut, L"CertUtil: -asn command failed to get file size: %d\n", GetLastError());
|
||||
CloseHandle(hFile);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (dwSize == 0)
|
||||
{
|
||||
ConPrintf(StdOut, L"CertUtil: -asn command got an empty file\n");
|
||||
CloseHandle(hFile);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PBYTE pData = (PBYTE)LocalAlloc(0, dwSize);
|
||||
if (!pData)
|
||||
{
|
||||
ConPrintf(StdOut, L"CertUtil: -asn command failed to allocate: %d\n", GetLastError());
|
||||
CloseHandle(hFile);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD cbRead;
|
||||
BOOL fRead = ReadFile(hFile, pData, dwSize, &cbRead, NULL);
|
||||
DWORD dwErr = GetLastError();
|
||||
CloseHandle(hFile);
|
||||
|
||||
if (!fRead || cbRead != dwSize)
|
||||
{
|
||||
ConPrintf(StdOut, L"CertUtil: -asn command failed to read: %d\n", dwErr);
|
||||
LocalFree(pData);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
WCHAR Buffer[MAX_PATH] = {0};
|
||||
BOOL fSucceeded = ParseAsn(pData, pData, dwSize, Buffer, TRUE);
|
||||
|
||||
LocalFree(pData);
|
||||
return fSucceeded;
|
||||
}
|
||||
|
107
base/applications/cmdutils/certutil/certutil.cpp
Normal file
107
base/applications/cmdutils/certutil/certutil.cpp
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* PROJECT: ReactOS certutil
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: CertUtil commandline handling
|
||||
* COPYRIGHT: Copyright 2020 Mark Jansen (mark.jansen@reactos.org)
|
||||
*
|
||||
* Note: Only -hashfile and -asn are implemented for now, the rest is not present!
|
||||
*/
|
||||
|
||||
#include "precomp.h"
|
||||
#include <wincrypt.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LPCWSTR Name;
|
||||
BOOL (*pfn)(LPCWSTR Filename);
|
||||
} Verb;
|
||||
|
||||
|
||||
Verb verbs[] = {
|
||||
{ L"hashfile", hash_file },
|
||||
{ L"asn", asn_dump },
|
||||
};
|
||||
|
||||
static void print_usage()
|
||||
{
|
||||
ConPuts(StdOut, L"Verbs:\n");
|
||||
ConPuts(StdOut, L" -hashfile -- Display cryptographic hash over a file\n");
|
||||
ConPuts(StdOut, L" -asn -- Display ASN.1 encoding of a file\n");
|
||||
ConPuts(StdOut, L"\n");
|
||||
ConPuts(StdOut, L"CertUtil -? -- Display a list of all verbs\n");
|
||||
ConPuts(StdOut, L"CertUtil -hashfile -? -- Display help text for the 'hashfile' verb\n");
|
||||
}
|
||||
|
||||
|
||||
Verb* MatchVerb(LPCWSTR arg)
|
||||
{
|
||||
if (arg[0] != '-' && arg[0] != '/')
|
||||
return NULL;
|
||||
|
||||
for (size_t n = 0; n < RTL_NUMBER_OF(verbs); ++n)
|
||||
{
|
||||
if (!_wcsicmp(verbs[n].Name, arg + 1))
|
||||
{
|
||||
return verbs + n;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int wmain(int argc, WCHAR *argv[])
|
||||
{
|
||||
int n;
|
||||
|
||||
/* Initialize the Console Standard Streams */
|
||||
ConInitStdStreams();
|
||||
|
||||
if (argc == 1) /* i.e. no commandline arguments given */
|
||||
{
|
||||
print_usage();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
for (n = 1; n < argc; ++n)
|
||||
{
|
||||
if (!_wcsicmp(argv[n], L"-?"))
|
||||
{
|
||||
print_usage();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
Verb* verb = MatchVerb(argv[n]);
|
||||
|
||||
if (verb)
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
ConPrintf(StdOut, L"CertUtil: -%s expected 1 argument, got %d\n", verb->Name, argc - 2);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!_wcsicmp(argv[n+1], L"-?"))
|
||||
{
|
||||
print_usage();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (!verb->pfn(argv[n+1]))
|
||||
{
|
||||
/* The verb prints the failure */
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ConPrintf(StdOut, L"CertUtil: -%s command completed successfully\n", verb->Name);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
ConPrintf(StdOut, L"CertUtil: Unknown verb: %s\n", argv[n]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -1,10 +1,8 @@
|
|||
/*
|
||||
* PROJECT: ReactOS certutil
|
||||
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
||||
* PURPOSE: CertUtil stub
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: CertUtil hashfile implementation
|
||||
* COPYRIGHT: Copyright 2020 Mark Jansen (mark.jansen@reactos.org)
|
||||
*
|
||||
* Note: Only -hashfile is implemented for now, the rest is not present!
|
||||
*/
|
||||
|
||||
#include "precomp.h"
|
||||
|
@ -12,7 +10,7 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
|
||||
static BOOL hash_file(LPCWSTR Filename)
|
||||
BOOL hash_file(LPCWSTR Filename)
|
||||
{
|
||||
HCRYPTPROV hProv;
|
||||
BOOL bSuccess = FALSE;
|
||||
|
@ -87,69 +85,3 @@ static BOOL hash_file(LPCWSTR Filename)
|
|||
return bSuccess;
|
||||
}
|
||||
|
||||
|
||||
static void print_usage()
|
||||
{
|
||||
ConPuts(StdOut, L"Verbs:\n");
|
||||
ConPuts(StdOut, L" -hashfile -- Display cryptographic hash over a file\n");
|
||||
ConPuts(StdOut, L"\n");
|
||||
ConPuts(StdOut, L"CertUtil -? -- Display a list of all verbs\n");
|
||||
ConPuts(StdOut, L"CertUtil -hashfile -? -- Display help text for the 'hashfile' verb\n");
|
||||
}
|
||||
|
||||
int wmain(int argc, WCHAR *argv[])
|
||||
{
|
||||
int n;
|
||||
|
||||
/* Initialize the Console Standard Streams */
|
||||
ConInitStdStreams();
|
||||
|
||||
if (argc == 1) /* i.e. no commandline arguments given */
|
||||
{
|
||||
print_usage();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
for (n = 1; n < argc; ++n)
|
||||
{
|
||||
if (!_wcsicmp(argv[n], L"-?"))
|
||||
{
|
||||
print_usage();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else if (!_wcsicmp(argv[n], L"-hashfile"))
|
||||
{
|
||||
if (argc == 3)
|
||||
{
|
||||
if (!_wcsicmp(argv[n+1], L"-?"))
|
||||
{
|
||||
print_usage();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!hash_file(argv[n+1]))
|
||||
{
|
||||
/* hash_file prints the failure itself */
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ConPuts(StdOut, L"CertUtil: -hashfile command completed successfully\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ConPrintf(StdOut, L"CertUtil: -hashfile expected 1 argument, got %d\n", argc - 2);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ConPrintf(StdOut, L"CertUtil: Unknown verb: %s\n", argv[n]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -7,10 +7,14 @@
|
|||
|
||||
#include <windef.h>
|
||||
#include <winbase.h>
|
||||
#include <winreg.h>
|
||||
#include <winuser.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#include <conutils.h>
|
||||
|
||||
|
||||
BOOL hash_file(LPCWSTR Filename);
|
||||
BOOL asn_dump(LPCWSTR Filename);
|
||||
|
||||
|
||||
|
||||
#endif /* __CERTUTIL_PRECOMP_H */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue