Created
March 8, 2021 15:01
-
-
Save iamahuman/e7d3881c892c940ab52c0a5397698195 to your computer and use it in GitHub Desktop.
Parse Crypto API certificate blobs in Windows Registry
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import sys | |
import os | |
import struct | |
PROP_STRUCT = struct.Struct("<3I") | |
# {{{ constants | |
PROP_IDS = { | |
1: "KEY_PROV_HANDLE", | |
2: "KEY_PROV_INFO", | |
3: "SHA1_HASH", | |
4: "MD5_HASH", | |
5: "KEY_CONTEXT", | |
6: "KEY_SPEC", | |
7: "IE30_RESERVED", | |
8: "PUBKEY_HASH_RESERVED", | |
9: "ENHKEY_USAGE", | |
10: "NEXT_UPDATE_LOCATION", | |
11: "FRIENDLY_NAME", | |
12: "PVK_FILE", | |
13: "DESCRIPTION", | |
14: "ACCESS_STATE", | |
15: "SIGNATURE_HASH", | |
16: "SMART_CARD_DATA", | |
17: "EFS", | |
18: "FORTEZZA_DATA", | |
19: "ARCHIVED", | |
20: "KEY_IDENTIFIER", | |
21: "AUTO_ENROLL", | |
22: "PUBKEY_ALG_PARA", | |
23: "CROSS_CERT_DIST_POINTS", | |
24: "ISSUER_PUBLIC_KEY_MD5_HASH", | |
25: "SUBJECT_PUBLIC_KEY_MD5_HASH", | |
26: "ENROLLMENT", | |
27: "DATE_STAMP", | |
28: "ISSUER_SERIAL_NUMBER_MD5_HASH", | |
29: "SUBJECT_NAME_MD5_HASH", | |
30: "EXTENDED_ERROR_INFO", | |
64: "RENEWAL", | |
65: "ARCHIVED_KEY_HASH", | |
66: "AUTO_ENROLL_RETRY", | |
67: "AIA_URL_RETRIEVED", | |
68: "AUTHORITY_INFO_ACCESS", | |
69: "BACKED_UP", | |
70: "OCSP_RESPONSE", | |
71: "REQUEST_ORIGINATOR", | |
72: "SOURCE_LOCATION", | |
73: "SOURCE_URL", | |
74: "NEW_KEY", | |
75: "OCSP_CACHE_PREFIX", | |
76: "SMART_CARD_ROOT_INFO", | |
77: "NO_AUTO_EXPIRE_CHECK", | |
78: "NCRYPT_KEY_HANDLE", | |
79: "HCRYPTPROV_OR_NCRYPT_KEY_HANDLE", | |
80: "SUBJECT_INFO_ACCESS", | |
81: "CA_OCSP_AUTHORITY_INFO_ACCESS", | |
82: "CA_DISABLE_CRL", | |
83: "ROOT_PROGRAM_CERT_POLICIES", | |
84: "ROOT_PROGRAM_NAME_CONSTRAINTS", | |
85: "SUBJECT_OCSP_AUTHORITY_INFO_ACCESS", | |
86: "SUBJECT_DISABLE_CRL", | |
87: "CEP", | |
89: "SIGN_HASH_CNG_ALG", | |
90: "SCARD_PIN_ID", | |
91: "SCARD_PIN_INFO", | |
92: "SUBJECT_PUB_KEY_BIT_LENGTH", | |
93: "PUB_KEY_CNG_ALG_BIT_LENGTH", | |
94: "ISSUER_PUB_KEY_BIT_LENGTH", | |
95: "ISSUER_CHAIN_SIGN_HASH_CNG_ALG", | |
96: "ISSUER_CHAIN_PUB_KEY_CNG_ALG_BIT_LENGTH", | |
97: "NO_EXPIRE_NOTIFICATION", | |
98: "AUTH_ROOT_SHA256_HASH", | |
99: "NCRYPT_KEY_HANDLE_TRANSFER", | |
100: "HCRYPTPROV_TRANSFER", | |
101: "SMART_CARD_READER", | |
102: "SEND_AS_TRUSTED_ISSUER", | |
103: "KEY_REPAIR_ATTEMPTED", | |
104: "DISALLOWED_FILETIME", | |
105: "ROOT_PROGRAM_CHAIN_POLICIES", | |
106: "SMART_CARD_READER_NON_REMOVABLE", | |
107: "SHA256_HASH", | |
108: "SCEP_SERVER_CERTS", | |
109: "SCEP_RA_SIGNATURE_CERT", | |
110: "SCEP_RA_ENCRYPTION_CERT", | |
111: "SCEP_CA_CERT", | |
112: "SCEP_SIGNER_CERT", | |
113: "SCEP_NONCE", | |
114: "SCEP_ENCRYPT_HASH_CNG_ALG", | |
115: "SCEP_FLAGS", | |
116: "SCEP_GUID", | |
117: "SERIALIZABLE_KEY_CONTEXT", | |
118: "ISOLATED_KEY", | |
119: "SERIAL_CHAIN", | |
120: "KEY_CLASSIFICATION", | |
121: "OCSP_MUST_STAPLE", | |
122: "DISALLOWED_ENHKEY_USAGE", | |
123: "NONCOMPLIANT_ROOT_URL", | |
124: "PIN_SHA256_HASH", | |
125: "CLR_DELETE_KEY", | |
126: "NOT_BEFORE_FILETIME", | |
127: "NOT_BEFORE_ENHKEY_USAGE", | |
128: "FIRST_RESERVED", | |
0x00007FFF: "LAST_RESERVED", | |
0x00008000: "FIRST_USER", | |
0x0000FFFF: "LAST_USER", | |
0x1000: "STORE_LOCALIZED_NAME", | |
} | |
# }}} | |
PROP_IDS[32] = 'CERT' | |
def get_props(inpf): | |
while True: | |
hdr = inpf.read(12) | |
if not hdr: | |
break | |
try: | |
propid, reserved, size = PROP_STRUCT.unpack(hdr) | |
except struct.error: | |
raise IOError('short read') | |
if reserved != 1: | |
raise ValueError('reserved is not 1') | |
data = inpf.read(size) | |
if len(data) != size: | |
raise IOError('data short read') | |
yield propid, data | |
def main(args): | |
fil = sys.stdin | |
try: | |
fil = fil.buffer | |
except AttributeError: | |
pass | |
if args[0] == 'list': | |
for propid, _ in get_props(fil): | |
print(PROP_IDS.get(propid, propid)) | |
elif args[0] == 'get': | |
prop_str = args[1] | |
try: | |
dest_propid = int(prop_str) | |
except ValueError: | |
try: | |
dest_propid = next( | |
x for x, n in PROP_IDS.items() if n == prop_str) | |
except StopIteration: | |
raise ValueError('unrecognized property ID') from None | |
out = sys.stdout | |
pretty = os.isatty(out.fileno()) | |
if not pretty: | |
try: | |
out = out.buffer | |
except AttributeError: | |
pass | |
for propid, data in get_props(fil): | |
if propid == dest_propid: | |
if pretty: | |
print(repr(data)[1:-1]) | |
else: | |
out.write(data) | |
if __name__ == '__main__': | |
main(sys.argv[1:]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment