33
44import windows
55from windows import winproxy
6- # from windows.generated_def import *
76import windows .generated_def as gdef
87
98from windows .crypto import DEFAULT_ENCODING
10- # from windows.crypto.helper import ECRYPT_DATA_BLOB
9+
10+ import windows .crypto .cryptmsg
1111
1212
1313CRYPT_OBJECT_FORMAT_TYPE = [
@@ -44,17 +44,16 @@ class CryptObject(object):
4444 def __init__ (self , filename , content_type = gdef .CERT_QUERY_CONTENT_FLAG_ALL ):
4545 # No other API than filename for now..
4646 self .filename = filename
47- if filename is None :
48- return # TMP !
4947
50- dwEncoding = DWORD ()
51- dwContentType = DWORD ()
52- dwFormatType = DWORD ()
53- hStore = PVOID ()
54- hMsg = PVOID ()
48+ dwEncoding = gdef . DWORD ()
49+ dwContentType = gdef . DWORD ()
50+ dwFormatType = gdef . DWORD ()
51+ hStore = EHCERTSTORE ()
52+ hMsg = windows . crypto . cryptmsg . CryptMessage ()
5553
5654 winproxy .CryptQueryObject (gdef .CERT_QUERY_OBJECT_FILE ,
57- LPWSTR (filename ),
55+ gdef .LPWSTR (filename ),
56+ # filename,
5857 content_type ,
5958 gdef .CERT_QUERY_FORMAT_FLAG_BINARY ,
6059 0 ,
@@ -65,84 +64,24 @@ def __init__(self, filename, content_type=gdef.CERT_QUERY_CONTENT_FLAG_ALL):
6564 hMsg ,
6665 None )
6766
68- self .hstore = hStore
69- self .hmsg = hMsg
67+ self .cert_store = hStore
68+ self .crypt_msg = hMsg
7069 self .encoding = dwEncoding
7170 self .content_type = CRYPT_OBJECT_FORMAT_TYPE_DICT .get (dwContentType .value , dwContentType )
7271
73- def msg_get_param (self , param_type , index = 0 ):
74- signer_info = DWORD ()
75- # https://msdn.microsoft.com/en-us/library/windows/desktop/aa380227(v=vs.85).aspx
76- winproxy .CryptMsgGetParam (self .hmsg , param_type , index , None , signer_info )
77- buffer = ctypes .c_buffer (signer_info .value )
78- winproxy .CryptMsgGetParam (self .hmsg , param_type , index , buffer , signer_info )
79-
80- if param_type in self .MSG_PARAM_KNOW_TYPES :
81- buffer = self .MSG_PARAM_KNOW_TYPES [param_type ].from_buffer (buffer )
82- return buffer
72+ def _signers_and_certs_generator (self ):
73+ for signer in self .crypt_msg .signers :
74+ cert = self .cert_store .find (signer .Issuer , signer .SerialNumber )
75+ yield signer , cert
8376
8477 @property
85- def nb_signer (self ):
86- """The number of signers for the CryptObject
87-
88- :type: :class:`int`
89- """
90- return self .msg_get_param (CMSG_SIGNER_COUNT_PARAM ).value
91-
92- def get_signer_data (self , index = 0 ):
93- """Returns the signer informations for signer nb ``index``
94-
95- :return: :class:`CMSG_SIGNER_INFO`
96- """
97- return self .msg_get_param (CMSG_SIGNER_INFO_PARAM , index )
98-
99- def get_signer_certificate (self , index = 0 ):
100- """Return the certificate used for signer nb ``index``
101-
102- :return: :class:`CertificateContext`
103- """
104- data = self .get_signer_data (index )
105- cert_info = CERT_INFO ()
106- cert_info .Issuer = data .Issuer
107- cert_info .SerialNumber = data .SerialNumber
108- rawcertcontext = winproxy .CertFindCertificateInStore (self .hstore , self .encoding , 0 , CERT_FIND_SUBJECT_CERT , byref (cert_info ), None )
109- #return rawcertcontext
110- return CertificateContext (rawcertcontext [0 ])
111-
112- def get_raw_cert (self , index = 0 ):
113- return self .msg_get_param (CMSG_CERT_PARAM , index )
114-
115- def get_cert (self , index = 0 ):
116- """Return embded certificate number ``index``.
117-
118- note: not all embded certificate are directly used to sign the :class:`CryptObject`.
119-
120- :return: :class:`CertificateContext`
121- """
122- return CertificateContext .from_buffer (self .get_raw_cert (index ))
123-
124- cert = property (get_cert )
125-
126- @property
127- def nb_cert (self ):
128- """The number of certificate embded in the :class:`CryptObject`
129-
130- :type: :class:`int`
131- """
132- return self .msg_get_param (CMSG_CERT_COUNT_PARAM ).value
133-
134- @property
135- def signers (self ):
136- return [self .get_signer_data (i ) for i in range (self .nb_signer )]
137-
138- @property
139- def certs (self ):
140- return [self .get_cert (i ) for i in range (self .nb_cert )]
78+ def signers_and_certs (self ):
79+ return list (self ._signers_and_certs_generator ())
14180
14281 def __repr__ (self ):
14382 return '<{0} "{1}" content_type={2}>' .format (type (self ).__name__ , self .filename , self .content_type )
14483
145-
84+ # TODO: rename to CertificateStore ?
14685class EHCERTSTORE (gdef .HCERTSTORE ):
14786 """A certificate store"""
14887 @property
@@ -192,7 +131,21 @@ def new_in_memory(cls):
192131 res = winproxy .CertOpenStore (CERT_STORE_PROV_MEMORY , DEFAULT_ENCODING , None , 0 , None )
193132 return ctypes .cast (res , cls )
194133
195- # Add API arround 'CertFindCertificateInStore' ?
134+
135+ # TODO: a more complete search API ?
136+ def find (self , issuer , serialnumber ):
137+ """Return the certificate that match `issuer` and `serialnumber`
138+
139+ :return: :class:`CertificateContext`
140+ """
141+ # data = self.get_signer_data(index)
142+ cert_info = gdef .CERT_INFO ()
143+ cert_info .Issuer = issuer
144+ cert_info .SerialNumber = serialnumber
145+ rawcertcontext = winproxy .CertFindCertificateInStore (self , DEFAULT_ENCODING , 0 , gdef .CERT_FIND_SUBJECT_CERT , ctypes .byref (cert_info ), None )
146+ # return rawcertcontext
147+ return CertificateContext (rawcertcontext [0 ])
148+
196149
197150# PKCS12_NO_PERSIST_KEY -> do not save it in a key container on disk
198151# Without it, a key container is created at 'C:\Users\USERNAME\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-3241049326-165485355-1070449050-1001'
@@ -275,19 +228,19 @@ def store(self):
275228 def get_raw_certificate_chains (self ): # Rename to all_chains ?
276229 chain_context = EPCCERT_CHAIN_CONTEXT ()
277230
278- enhkey_usage = CERT_ENHKEY_USAGE ()
231+ enhkey_usage = gdef . CERT_ENHKEY_USAGE ()
279232 enhkey_usage .cUsageIdentifier = 0
280233 enhkey_usage .rgpszUsageIdentifier = None
281234
282- cert_usage = CERT_USAGE_MATCH ()
283- cert_usage .dwType = USAGE_MATCH_TYPE_AND
235+ cert_usage = gdef . CERT_USAGE_MATCH ()
236+ cert_usage .dwType = gdef . USAGE_MATCH_TYPE_AND
284237 cert_usage .Usage = enhkey_usage
285238
286- chain_para = CERT_CHAIN_PARA ()
287- chain_para .cbSize = sizeof (chain_para )
239+ chain_para = gdef . CERT_CHAIN_PARA ()
240+ chain_para .cbSize = ctypes . sizeof (chain_para )
288241 chain_para .RequestedUsage = cert_usage
289242
290- winproxy .CertGetCertificateChain (None , self , None , self [0 ].hCertStore , byref (chain_para ), 0 , None , byref (chain_context ))
243+ winproxy .CertGetCertificateChain (None , self , None , self [0 ].hCertStore , ctypes . byref (chain_para ), 0 , None , ctypes . byref (chain_context ))
291244 #return CertficateChain(chain_context)
292245 return chain_context
293246
@@ -349,6 +302,12 @@ def encoded(self):
349302 :type: :class:`bytearray`"""
350303 return bytearray (self [0 ].pbCertEncoded [:self [0 ].cbCertEncoded ])
351304
305+ @property
306+ def version (self ):
307+ "TODO: doc"
308+ return self [0 ].pbCertInfo .dwVersion
309+
310+
352311 @classmethod
353312 def from_file (cls , filename ):
354313 """Create a :class:`CertificateContext` from the file ``filename``
@@ -392,15 +351,14 @@ def __eq__(self, other):
392351
393352
394353# Those classes are more of a POC than anything else
395-
396354class EPCCERT_CHAIN_CONTEXT (gdef .PCCERT_CHAIN_CONTEXT ):
397355 _type_ = gdef .PCCERT_CHAIN_CONTEXT ._type_
398356
399357 @property
400358 def chains (self ):
401359 res = []
402360 for i in range (self [0 ].cChain ):
403- simple_chain = ctypes .cast (self [0 ].rgpChain [i ], gdef . EPCCERT_SIMPLE_CHAIN )
361+ simple_chain = ctypes .cast (self [0 ].rgpChain [i ], EPCCERT_SIMPLE_CHAIN )
404362 res .append (simple_chain )
405363 return res
406364
0 commit comments