More doc + fix sample on windows.crypto

This commit is contained in:
hakril
2017-04-11 01:24:27 +02:00
committed by Clement Rouault
parent 1550c5ae3e
commit ab01eba54f
7 changed files with 173 additions and 21 deletions
-2
View File
@@ -22,8 +22,6 @@ TODO:
- registry
- test !
- Clean pe_parse.py
Documentation
* verif samples
+89
View File
@@ -0,0 +1,89 @@
``windows.crypto`` -- CryptoAPI
*******************************
.. module:: windows.crypto
The :mod:`windows.crypto` module offers some wrappers arround the CryptoAPI_.
The main goal of this module (for now) is providing simple encryption/decryption methods.
Encryption
""""""""""
.. note::
See sample :ref:`sample_crypto_encryption`
encrypt
'''''''
.. autofunction:: encrypt
decrypt
'''''''
.. autofunction:: decrypt
import_pfx
''''''''''
.. autofunction:: import_pfx
Certificate
"""""""""""
.. note::
See sample :ref:`sample_crypto_certificate`
CertificateContext
''''''''''''''''''
.. autoclass:: CertificateContext
EHCERTSTORE
'''''''''''
.. autoclass:: EHCERTSTORE
CryptContext
''''''''''''
.. autoclass:: CryptContext
Generating componants
"""""""""""""""""""""
.. module:: windows.crypto.generation
This module is used to generate selfsigned-certificates / keypair and pfx file.
.. note::
See ``genkeys()`` in the sample :ref:`sample_crypto_encryption`
generate_selfsigned_certificate
'''''''''''''''''''''''''''''''
.. autofunction:: generate_selfsigned_certificate
generate_key
''''''''''''
.. autofunction:: generate_key
generate_pfx
''''''''''''
.. autofunction:: generate_pfx
.. _CryptoAPI: https://msdn.microsoft.com/en-us/library/windows/desktop/aa380252(v=vs.85).aspx
+38 -5
View File
@@ -525,7 +525,7 @@ This sample is a working POC able to generate key-pair, encrypt and decrypt file
Ouput::
(cmd λ) python ..\samples\encryption_demo.py genkey YOLOCERTIF mykey --pfxpassword MYPASSWORD
(cmd λ) python samples\encryption_demo.py genkey YOLOCERTIF mykey --pfxpassword MYPASSWORD
<CertificatContext "YOLOCERTIF" serial="1b a4 3e 17 f7 ed ec ab 4f f8 11 46 48 e9 29 25">
(cmd λ) ls
@@ -533,7 +533,7 @@ Ouput::
(cmd λ) echo|set /p="my secret message" > message.txt
(cmd λ) python ..\samples\encryption_demo.py crypt message.txt message.crypt mykey.cer
(cmd λ) python samples\encryption_demo.py crypt message.txt message.crypt mykey.cer
Encryption done. Result:
bytearray(b'0\x82\x01\x19\x06\t*\x86H\x86\xf7\r\x01\x07\x03\xa0\x82\x01\n0\x82\x01\x06\x02\x01\x001\x81\xc30\x81
\xc0\x02\x01\x000)0\x151\x130\x11\x06\x03U\x04\x03\x13\nYOLOCERTIF\x02\x10\x1b\xa4>\x17\xf7\xed\xec\xabO\xf8\x11
@@ -543,7 +543,7 @@ Ouput::
\xc6\x12x\x1am\xc8\x01t\xac\xa6\xf3#\x02\xd4J \x8eZ\xbb\x10W\xe1 0;\x06\t*\x86H\x86\xf7\r\x01\x07\x010\x14\x06\x08*
\x86H\x86\xf7\r\x03\x07\x04\x08\x14F\x04\xad\xed9\xed<\x80\x18\x80]6\xccTV\xbc\xb8*\x84QY!~\xb3\n\x1aV\xd4\rf\xd1n:')
(cmd λ) python ..\samples\encryption_demo.py decrypt message.crypt mykey.pfx BADPASS
(cmd λ) python samples\encryption_demo.py decrypt message.crypt mykey.pfx BADPASS
Traceback (most recent call last):
File "..\samples\encryption_demo.py", line 103, in <module>
res.func(**res.__dict__)
@@ -559,5 +559,38 @@ Ouput::
raise Kernel32Error(func_name)
windows.winproxy.Kernel32Error: PFXImportCertStore: [Error 86] The specified network password is not correct.
(cmd λ) python ..\samples\encryption_demo.py decrypt message.crypt mykey.pfx MYPASSWORD
Result = <my secret message>
(cmd λ) python samples\encryption_demo.py decrypt message.crypt mykey.pfx MYPASSWORD
Result = <my secret message>
.. _sample_crypto_certificate:
Certificate demo
''''''''''''''''
.. literalinclude:: ..\..\samples\certificate.py
Ouput::
(cmd λ) python .\samples\certificate.py
Analysing certificate: <CertificateContext "Microsoft Windows" serial="33 00 00 01 06 6e c3 25 c4 31 c9 18 0e 00 00 00 00 01 06">
* name: <Microsoft Windows>
* issuer: <Microsoft Windows Production PCA 2011>
* raw_serial: <[51, 0, 0, 1, 6, 110, 195, 37, 196, 49, 201, 24, 14, 0, 0, 0, 0, 1, 6]>
* serial: <33 00 00 01 06 6e c3 25 c4 31 c9 18 0e 00 00 00 00 01 06>
* encoded start: <bytearray(b'0\x82\x05\x040\x82\x03\xec\xa0\x03\x02\x01\x02\x02\x133\x00\x00\x01\x06')>
This certificate has 1 certificate chain
Chain 0:
<CertificateContext "Microsoft Windows" serial="33 00 00 01 06 6e c3 25 c4 31 c9 18 0e 00 00 00 00 01 06">:
* issuer: <Microsoft Windows Production PCA 2011>
<CertificateContext "Microsoft Windows Production PCA 2011" serial="61 07 76 56 00 00 00 00 00 08">:
* issuer: <Microsoft Root Certificate Authority 2010>
<CertificateContext "Microsoft Root Certificate Authority 2010" serial="28 cc 3a 25 bf ba 44 ac 44 9a 9b 58 6b 43 39 aa">:
* issuer: <Microsoft Root Certificate Authority 2010>
Looking for <Microsoft Root Certificate Authority 2010> in trusted certificates
matches = [<CertificateContext "Microsoft Root Certificate Authority 2010" serial="28 cc 3a 25 bf ba 44 ac 44 9a 9b 58 6b 43 39 aa">]
Found it !
+1 -5
View File
@@ -63,8 +63,4 @@ print("matches = {0}".format(matchs))
if matchs:
print("Found it !")
else:
print("Not found :(")
# TODO: add store enumeration
print("Not found :(")
+7 -3
View File
@@ -26,12 +26,13 @@ def decrypt(src, pfxfile, password, **kwargs):
pfx = crypto.import_pfx(pfxfile.read(), password)
# Decrypt the content of the file
decrypted = crypto.decrypt(pfx, src.read())
print(u"Result = <{0}>".format(decrypted.decode("utf8")))
print(u"Result = <{0}>".format(decrypted))
return decrypted
PFW_TMP_KEY_CONTAINER = "PythonForWindowsTMPContainer"
def genkeys(common_name, pfxpassword, outname, **kwargs):
"""Generate a SHA256/RSA key pair. A self-signed certificate with 'common_name' is store as 'outname'.cer.
"""Generate a SHA256/RSA key pair. A self-signed certificate with 'common_name' is stored as 'outname'.cer.
The private key is stored in 'outname'.pfx protected with 'pfxpassword'"""
cert_store = crypto.EHCERTSTORE.new_in_memory()
# Create a TMP context that will hold our newly generated key-pair
@@ -39,6 +40,9 @@ def genkeys(common_name, pfxpassword, outname, **kwargs):
key = HCRYPTKEY()
# Generate a key-pair that is exportable
winproxy.CryptGenKey(ctx, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, key)
# It does NOT destroy the key-pair from the container,
# It only release the key handle
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa379918(v=vs.85).aspx
winproxy.CryptDestroyKey(key)
# Descrption of the key-container that will be used to generate the certificate
@@ -68,7 +72,7 @@ def genkeys(common_name, pfxpassword, outname, **kwargs):
# Dump the certif (public key) and pfx (public + private keys)
with open(outname + ".cer", "wb") as f:
# The encoded certif only contains the public key
f.write(certif.encoded())
f.write(certif.encoded)
with open(outname + ".pfx", "wb") as f:
f.write(pfx)
print(certif)
+24 -6
View File
@@ -112,9 +112,13 @@ class CryptObject(object):
class EHCERTSTORE(HCERTSTORE):
"""A certificate store"""
@property
def certs(self):
"Based on CertEnumCertificatesInStore"
"""The certificates in the store
:type: [:class:`CertificateContext`] -- A list of Certificate
"""
res = []
last = None
while True:
@@ -131,10 +135,12 @@ class EHCERTSTORE(HCERTSTORE):
raise RuntimeError("Out of infinit loop")
def add_certificate(self, certificate):
"""Add a certificate to the store"""
winproxy.CertAddCertificateContextToStore(self, certificate, CERT_STORE_ADD_NEW, None)
@classmethod
def from_file(cls, filename):
"""Create a new :class:`EHCERTSTORE` from ``filename``"""
res = winproxy.CertOpenStore(CERT_STORE_PROV_FILENAME_A, DEFAULT_ENCODING, None, CERT_STORE_OPEN_EXISTING_FLAG, filename)
return ctypes.cast(res, cls)
@@ -142,11 +148,15 @@ class EHCERTSTORE(HCERTSTORE):
# See https://msdn.microsoft.com/en-us/library/windows/desktop/aa388136(v=vs.85).aspx
@classmethod
def from_system_store(cls, store_name):
"""Create a new :class:`EHCERTSTORE` from system store``store_name``
(see https://msdn.microsoft.com/en-us/library/windows/desktop/aa388136(v=vs.85).aspx)
"""
res = winproxy.CertOpenStore(CERT_STORE_PROV_SYSTEM_A, DEFAULT_ENCODING, None, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG, store_name)
return ctypes.cast(res, cls)
@classmethod
def new_in_memory(cls):
"""Create a new temporary :class:`EHCERTSTORE` in memory"""
res = winproxy.CertOpenStore(CERT_STORE_PROV_MEMORY, DEFAULT_ENCODING, None, 0, None)
return ctypes.cast(res, cls)
@@ -155,6 +165,14 @@ class EHCERTSTORE(HCERTSTORE):
# PKCS12_NO_PERSIST_KEY -> do not save it in a key container on disk
# Without it, a key container is created at 'C:\Users\USERNAME\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-3241049326-165485355-1070449050-1001'
def import_pfx(pfx, password=None, flags=CRYPT_USER_KEYSET | PKCS12_NO_PERSIST_KEY):
"""Import the file ``pfx`` with the ``password``.
``default flags = PKCS12_NO_PERSIST_KEY | CRYPT_USER_KEYSET``.
``PKCS12_NO_PERSIST_KEY`` tells ``CryptoAPI`` to NOT save the keys in a on-disk container.
:return: :class:`EHCERTSTORE`
"""
if isinstance(pfx, basestring):
pfx = ECRYPT_DATA_BLOB.from_string(pfx)
cert_store = winproxy.PFXImportCertStore(pfx, password, flags)
@@ -164,7 +182,7 @@ def import_pfx(pfx, password=None, flags=CRYPT_USER_KEYSET | PKCS12_NO_PERSIST_K
# Why PCCERT_CONTEXT (pointer type) and not _CERT_CONTEXT ?
class CertificateContext(PCCERT_CONTEXT):
"""Represent a Certificate.

note: It is a pointer ctypes structure (``PCCERT_CONTEXT``)
"""
_type_ = PCCERT_CONTEXT._type_ # Not herited from PCCERT_CONTEXT
@@ -185,7 +203,7 @@ class CertificateContext(PCCERT_CONTEXT):
def serial(self):
"""The string representation of the certificate's serial.
:type: :class:``str``
:type: :class:`str`
"""
serial_number = self[0].pCertInfo[0].SerialNumber
serial_bytes = self.raw_serial
@@ -218,7 +236,7 @@ class CertificateContext(PCCERT_CONTEXT):
def store(self):
"""The certificate store that contains the certificate
:type: :class:``EHCERTSTORE``
:type: :class:`EHCERTSTORE`
"""
return EHCERTSTORE(self[0].hCertStore)
@@ -301,7 +319,7 @@ class CertificateContext(PCCERT_CONTEXT):
@classmethod
def from_file(cls, filename):
"""Create a :class:``CertificateContext`` for the file ``filename``
"""Create a :class:`CertificateContext` for the file ``filename``
:return: :class:`CertificateContext`
"""
@@ -313,7 +331,7 @@ class CertificateContext(PCCERT_CONTEXT):
@classmethod
def from_buffer(cls, data):
"""Create a :class:``CertificateContext`` from the buffer ``data``
"""Create a :class:`CertificateContext` from the buffer ``data``
:return: :class:`CertificateContext`
"""
+14
View File
@@ -6,6 +6,12 @@ from windows.crypto import DEFAULT_ENCODING, EHCERTSTORE
def generate_selfsigned_certificate(name="CN=DEFAULT", prov=None, key_info=None, flags=0, signature_algo=None):
"""Generate a selfsigned certificate.
See https://msdn.microsoft.com/en-us/library/windows/desktop/aa376039(v=vs.85).aspx
:return: :class:`windows.crypto.CertificateContext`
"""
size = ULONG(len(name) + 0x100)
buffer = (ctypes.c_ubyte * size.value)()
winproxy.CertStrToNameA(X509_ASN_ENCODING, name, CERT_OID_NAME_STR, None, buffer, size, None)
@@ -15,6 +21,10 @@ def generate_selfsigned_certificate(name="CN=DEFAULT", prov=None, key_info=None,
def generate_key(prov, keytype=AT_KEYEXCHANGE, flags=CRYPT_EXPORTABLE):
"""Generate a keypair if type ``keytype``.
:return: :class:`HCRYPTKEY`
"""
key = HCRYPTKEY()
winproxy.CryptGenKey(prov, keytype, flags , key)
return key
@@ -33,6 +43,10 @@ def generate_key(prov, keytype=AT_KEYEXCHANGE, flags=CRYPT_EXPORTABLE):
# return key
def generate_pfx(hstore, password=None):
"""Generate a pfx protected by ``password`` contaning the certificates in ``hstore``
:return: :class:`bytearray` -- The raw PFX
"""
blob = ECRYPT_DATA_BLOB(0, None)
winproxy.PFXExportCertStoreEx(hstore, blob, password, None, EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY)
blob.pbData = (ctypes.c_ubyte * blob.cbData)()