mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
162 lines
6.8 KiB
Python
162 lines
6.8 KiB
Python
import argparse
|
|
import getpass
|
|
|
|
import windows.crypto as crypto
|
|
from windows import winproxy
|
|
from windows.generated_def import *
|
|
|
|
import windows.crypto.generation as gencrypt
|
|
|
|
# http://stackoverflow.com/questions/1461272/basic-questions-on-microsoft-cryptoapi
|
|
|
|
def crypt(src, dst, certs, **kwargs):
|
|
"""Encrypt the content of 'src' file with the certifacts in 'certs' into 'dst'"""
|
|
# Open every certificates in the certs list
|
|
certlist = [crypto.Certificate.from_file(x) for x in certs]
|
|
# Encrypt the content of 'src' with all the public keys(certs)
|
|
res = crypto.encrypt(certlist, src.read())
|
|
print("Encryption done. Result:")
|
|
print(repr(res))
|
|
# Write the result in 'dst'
|
|
dst.write(res)
|
|
dst.close()
|
|
src.close()
|
|
|
|
def decrypt(src, pfxfile, password, outfile=None, **kwargs):
|
|
"""Decrypt the content of 'src' with the private key in 'pfxfile'. the 'pfxfile' is open using the 'password'"""
|
|
# Open the 'pfx' with the given password
|
|
if password is None:
|
|
password = getpass.getpass()
|
|
pfx = crypto.import_pfx(pfxfile.read(), password)
|
|
# Decrypt the content of the file
|
|
decrypted = crypto.decrypt(pfx, src.read())
|
|
if outfile is None:
|
|
print(u"Result = <{0}>".format(decrypted))
|
|
else:
|
|
with open(outfile, "wb") as f:
|
|
f.write(decrypted)
|
|
return decrypted
|
|
|
|
|
|
def sign(src, pfxfile, password, outfile=None, **kwargs):
|
|
if password is None:
|
|
password = getpass.getpass()
|
|
pfx = crypto.import_pfx(pfxfile.read(), password)
|
|
# Decrypt the content of the file
|
|
certs = pfx.certs
|
|
if len(certs) != 1:
|
|
raise ValueError("PFX containing exactly one certificate expected for signature")
|
|
cert = certs[0]
|
|
signed = crypto.sign(cert, src.read())
|
|
if outfile is None:
|
|
print(u"Result = <{0}>".format(signed))
|
|
else:
|
|
with open(outfile, "wb") as f:
|
|
f.write(signed)
|
|
return signed
|
|
|
|
PFW_TMP_KEY_CONTAINER = "PythonForWindowsTMPContainer"
|
|
|
|
def genkeys(common_name, pfxpassword, outname, keysize=2048, **kwargs):
|
|
"""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.CertificateStore.new_in_memory()
|
|
# Create a TMP context that will hold our newly generated key-pair
|
|
with crypto.CryptContext(PFW_TMP_KEY_CONTAINER, None, PROV_RSA_FULL, 0, retrycreate=True) as ctx:
|
|
key = HCRYPTKEY()
|
|
keysize_flags = keysize << 16
|
|
# Generate a key-pair that is exportable
|
|
winproxy.CryptGenKey(ctx, AT_KEYEXCHANGE, CRYPT_EXPORTABLE | keysize_flags, 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
|
|
KeyProvInfo = CRYPT_KEY_PROV_INFO()
|
|
KeyProvInfo.pwszContainerName = PFW_TMP_KEY_CONTAINER
|
|
KeyProvInfo.pwszProvName = None
|
|
KeyProvInfo.dwProvType = PROV_RSA_FULL
|
|
KeyProvInfo.dwFlags = 0
|
|
KeyProvInfo.cProvParam = 0
|
|
KeyProvInfo.rgProvParam = None
|
|
#KeyProvInfo.dwKeySpec = AT_SIGNATURE
|
|
KeyProvInfo.dwKeySpec = AT_KEYEXCHANGE
|
|
|
|
crypt_algo = CRYPT_ALGORITHM_IDENTIFIER()
|
|
crypt_algo.pszObjId = szOID_RSA_SHA256RSA
|
|
|
|
certif_name = "CN={0}".format(common_name)
|
|
# Generate a self-signed certificate based on the given key-container and signature algorithme
|
|
certif = gencrypt.generate_selfsigned_certificate(certif_name, key_info=KeyProvInfo, signature_algo=crypt_algo)
|
|
# Add the newly created certificate to our TMP cert-store
|
|
cert_store.add_certificate(certif)
|
|
# Generate a pfx from the TMP cert-store
|
|
print("Password is <{0}>".format(pfxpassword))
|
|
pfx = gencrypt.generate_pfx(cert_store, pfxpassword)
|
|
if outname is None:
|
|
outname = common_name.lower()
|
|
|
|
# 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)
|
|
with open(outname + ".pfx", "wb") as f:
|
|
f.write(pfx)
|
|
print(certif)
|
|
# Destroy the TMP key container
|
|
prov = HCRYPTPROV()
|
|
winproxy.CryptAcquireContextW(prov, PFW_TMP_KEY_CONTAINER, None, PROV_RSA_FULL, CRYPT_DELETEKEYSET)
|
|
|
|
# Openssl commands to check ce certif/pfx
|
|
|
|
## Read certificate info (.cer)
|
|
### openssl x509 -inform der -in {certif} -text -noout
|
|
|
|
## Read pfx info (ask to another password to encrypt Private key before print/export)
|
|
### openssl pkcs12 -info -in {pfx} -nokeys
|
|
## Read pfx info !!!! PRINT PRIVATE KEY !!!!
|
|
### openssl pkcs12 -info -in {pfx} -nodes
|
|
|
|
## Read ASN1 data
|
|
### openssl asn1parse -inform DER -in {file}
|
|
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(prog=__file__, formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
subparsers = parser.add_subparsers(description='valid subcommands')
|
|
|
|
cryptparse = subparsers.add_parser('crypt')
|
|
cryptparse.set_defaults(func=crypt)
|
|
|
|
cryptparse.add_argument('src', type=argparse.FileType('rb'), help='File to encrypt')
|
|
cryptparse.add_argument('dst', type=argparse.FileType('wb'), help='The encrypted file')
|
|
cryptparse.add_argument('certs', type=str, nargs='+',
|
|
help='List of certfile used to encrypt the src')
|
|
|
|
decryptparse = subparsers.add_parser('decrypt')
|
|
decryptparse.set_defaults(func=decrypt)
|
|
decryptparse.add_argument('src', type=argparse.FileType('rb'), help='File to decrypt')
|
|
decryptparse.add_argument('pfxfile', type=argparse.FileType('rb'), help='PFX file to use')
|
|
decryptparse.add_argument('--password', help='Password of the PFX')
|
|
decryptparse.add_argument('--outfile', default=None, help='The outputfile default is print')
|
|
|
|
|
|
decryptparse = subparsers.add_parser('sign')
|
|
decryptparse.set_defaults(func=sign)
|
|
decryptparse.add_argument('src', type=argparse.FileType('rb'), help='File to sign')
|
|
decryptparse.add_argument('pfxfile', type=argparse.FileType('rb'), help='PFX file to use')
|
|
decryptparse.add_argument('--password', help='Password of the PFX')
|
|
decryptparse.add_argument('--outfile', default=None, help='The outputfile default is print')
|
|
|
|
|
|
genkeysparse = subparsers.add_parser('genkey', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
genkeysparse.set_defaults(func=genkeys)
|
|
genkeysparse.add_argument('common_name', nargs='?', metavar='CommonName', default='DEFAULT', help='the common name of the certificate')
|
|
genkeysparse.add_argument('outname', nargs='?',help='The filename base for the generated files')
|
|
genkeysparse.add_argument('--pfxpassword', nargs='?', help='Password to protect the PFX')
|
|
genkeysparse.add_argument('--keysize', nargs='?', type=lambda x: int(x, 0), default=2048, help='The size of the RSA key')
|
|
|
|
res = parser.parse_args()
|
|
res.func(**res.__dict__) |