mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
More doc + fix sample on windows.crypto
This commit is contained in:
@@ -22,8 +22,6 @@ TODO:
|
||||
- registry
|
||||
- test !
|
||||
|
||||
- Clean pe_parse.py
|
||||
|
||||
Documentation
|
||||
* verif samples
|
||||
|
||||
|
||||
@@ -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
@@ -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 !
|
||||
@@ -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 :(")
|
||||
@@ -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)
|
||||
|
||||
@@ -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`
|
||||
"""
|
||||
|
||||
@@ -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)()
|
||||
|
||||
Reference in New Issue
Block a user