How to capture the output of openssl in python - python

I am trying to run the following openssl command in python:
cmd = "openssl x509 -sha1 -in esx.crt -noout -fingerprint"
tmp = os.popen(cmd)
tmp_sha1 = tmp.readline()
This command is supposed to generate a fingerprint of the certificate. I am trying to capture the output through the file object. But when I read this file object, there's nothing in it. I have executed this command on the command line and it runs fine, generates the fingerprint. Could you tell me how can I get the fingerprint?

You achieve this natively within Python using the OpenSSL module.
from OpenSSL.crypto import load_certificate, FILETYPE_PEM
cert_file_string = open("esx.crt", "rb").read()
cert = load_certificate(FILETYPE_PEM, cert_file_string)
sha1_fingerprint = cert.digest("sha1")
print sha1_fingerprint

You can use two modules to establish what you want: subprocess and os.
Using subprocess you can check for the output for the process using communicate(), which reads data from stdout and stderr until EOF.
>>> import subprocess
>>> p = subprocess.Popen("openssl x509 -sha1 -in 17.cert -noout -fingerprint", stdout=subprocess.PIPE)
>>> out, _ = p.communicate() #return a tuple (stdout, stderr)
>>> out
b'SHA1 Fingerprint=87:68:8B:B0:6A:E2:DF:A3:E2:63:76:97:A9:2B:B4:F4:82:4E:0B:D1\n'
Using os module works fine as well, using both read() and readline() methods: (please note that os.popen() is deprecated)
>>> import os
>>> p = os.popen("openssl x509 -sha1 -in 17.cert -noout -fingerprint")
>>> p.read()
'SHA1 Fingerprint=87:68:8B:B0:6A:E2:DF:A3:E2:63:76:97:A9:2B:B4:F4:82:4E:0B:D1\n'
>>> p = os.popen("openssl x509 -sha1 -in 17.cert -noout -fingerprint")
>>> out = p.readline()
'SHA1 Fingerprint=87:68:8B:B0:6A:E2:DF:A3:E2:63:76:97:A9:2B:B4:F4:82:4E:0B:D1\n'
If you want to write the value to a file, that works as well, which you can verify by opening a file output.txt in your current working directory:
>>> with open('./output.txt', 'w') as f:
... f.write(out)
...
77
The 77 notifies us that 77 bytes were written to the file, which you can verify by opening it in your favourite text editor.

Related

M2crypto Signature vs OpenSSL Signature

I have a pair of ECDSA keys and using the following code, I am trying to compare the signatures for a 'hello' string computed using OpenSSL and M2Crypto library in python.
Here is the code:
import subprocess
from hashlib import sha256
public_key_filename = 'ca_pu.pem'
private_key_filename = 'ca_pr.pem'
signature_filename = 'signature'
sigoutput = open(signature_filename, 'w')
cmd = 'openssl dgst -sha256 -sign'.split()
cmd.append(private_key_filename)
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=sigoutput)
p.stdin.write('hello')
x = p.communicate()[0]
p.stdin.close()
cmd = 'openssl dgst -sha256 -sign'.split()
cmd.append(private_key_filename)
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.stdin.write('hello')
x = p.communicate()[0]
p.stdin.close()
print "OpenSSL Stdout:", x.encode('hex_codec')
cmd = 'openssl dgst -sha256 -verify'.split()
cmd.append(public_key_filename)
cmd.append('-signature')
cmd.append(signature_filename)
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.stdin.write('hello')
x = p.communicate()[0]
p.stdin.close()
with open(signature_filename, 'rb') as f:
signature = f.read()
print "OpenSSL Signaure file:", signature.encode('hex_codec')
from M2Crypto import EC
pkey = EC.load_pub_key(public_key_filename)
prkey = EC.load_key(private_key_filename)
dgst = sha256("hello").digest()
s = prkey.sign_dsa_asn1(dgst)
print "M2C Signature:", s.encode('hex_codec')
if pkey.verify_dsa_asn1(dgst,s):
print "ok"
And here is the output:
OpenSSL Stdout: 30818702415efdc431f684fad778dc2d45997ab9433cf9a94a657f194b11d4b47a379ba4a208be12577245b8ce3bf8d6367f6fb5814e7000c5daa8aa5cb1e74e8940033416240242015f57e2329fe294b9693ead6bb911bdb7f8a3244dc05b36ac8016eb33721a3a6d7fc71972688c51e3b6b5ab3be3598aa1032ed715f7ca0d152eedb342322bfaae1b
OpenSSL Signaure file: 308188024200aabe47fa154f28f143df920135b000aa318bd37a7241bd6b735890d5d2b090cdc9c01ee152b681dc3b9c556fbfae26256d7c20b7a8e915bb9e8dc1355afd8cb29b02420178d780b6b7218dc88afbfc99c8a7ccab4303f70dd72a826009d9dd1ac0baccef56c8a1364edbb76ca294162790f4ca99a86478659cfb20332416a4a55324d333e7
M2C Signature: 30818702414362f629560d740248ce7a863a070a51720cb8a3f42a014b66798edabf00df1e7cb8c7a4c1dbf6d9a3c4106ecd43b2acea713fe0b3246a04bb8484846b74c8af81024200e8d119921b07bf43d4ea5d30a0e8b29b56da27ca4b53045ce994059df9c5a66e1bc3d07b08ac1122d18afe0602493dce9004a9695f57a8ca482c095d4f66d0bb9d
ok
I am wondering why the signatures are all different. Any help is much appreciated.
After some digging, it turns out that the signatures are different using DSA key and algorithm, due to having a random k when the signature is created.
I just post that to save you all some time in case you are having a same question.
For more info, checkout this link: https://security.stackexchange.com/questions/46939/dsa-generates-different-signatures-with-the-same-data

Why is my OpenSSL-generated signature different than the signature from PyCryptodome?

I'm trying to validate a signature from OpenSSL's rsautl command with PyCryptodome. The Bash command I use to sign the data (data is "38b..da1" string) is:
echo 38b2be8850bbf6e52bee4d4c5889df436285479f5472efe856787d7068a50da1 | openssl rsautl -inkey another_guys_priv_key.pem -sign | base64
I have converted this command into a Python script so I can compare things in Python:
from subprocess import Popen, PIPE
from shlex import split
# Use openssl to sign the data ("38b2..0da1") with another_guys private key
p1 = Popen(split('echo 38b2be8850bbf6e52bee4d4c5889df436285479f5472efe856787d7068a50da1'), stdout=PIPE)
p2 = Popen(split('openssl rsautl -inkey another_guys_priv_key.pem -sign'), stdin=p1.stdout, stdout=PIPE)
output, _ = p2.communicate()
Then I use PyCryptodome to sign the same data and compare the signatures:
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
# Use PyCrypto to sign the data with another_guys private key
key = RSA.importKey(open('another_guys_priv_key.pem').read())
signer = PKCS1_v1_5.new(key)
h = SHA256.new()
h.update('38b2be8850bbf6e52bee4d4c5889df436285479f5472efe856787d7068a50da1'.encode('utf-8'))
signature = signer.sign(h)
# Compare signature with OpenSSL signature
output == signature # False
and the results aren't the same.
So I was thinking that maybe OpenSSL automatically converts hexadecimal data into bytes and signs that, so I tried it with Python:
from binascii import unhexlify
# Use PyCryptodome to sign the hex-decoded data with another_guys private key
hh = SHA256.new()
hh.update(unhexlify('38b2be8850bbf6e52bee4d4c5889df436285479f5472efe856787d7068a50da1'))
hex_signature = signer.sign(hh)
# Compare signature with OpenSSL signature
output == hex_signature # False
but those results aren't the same either.
I'm obviously doing something wrong, but I'm not familiar enough with the Python crypto libraries to figure out what it is. What am I doing wrong?
The echo command (or shell built-in) adds a new line to the output by default, so signatures never match. Try disabling it.
$ echo -n foo | wc -c # hint - this counts number of bytes
3
$ echo foo | wc -c
4
But personally, I would just pass the string as standard input to the second process directly.

Call a shell command in python

I need to execute a shell command in python and need to store the result to a variable. How can I perform this.
I need to execute openssl rsautl -encrypt -inkey key and get the result to a variable.
---edit---
How can I execute
perl -e 'print "hello world"' | openssl rsautl -encrypt -inkey key
in python and get the output..
You can use subprocess.check_output
from subprocess import check_output
out = check_output(["openssl", "rsautl", "-encrypt", "-inkey", "key"])
The output will be stored in out.
A Simple way to execute a shell command is os.popen:
import os
cmdOutput1 = os.popen("openssl rsautl -encrypt -inkey key").readlines()
cmdOutput2 = os.popen("perl -e 'print \"hello world\"' | openssl rsautl -encrypt -inkey key").readlines()
All it takes is the command you want to run in the form of one String. It will return you an open file object. By using .readlines() this open file object will be converted to a list, where an Item in the List will correspond to a single line of Output from your command.

Using subprocess with openssl in a python script

I have thoroughly confused myself with Python subprocess syntax!
I would like to decrypt a string using openssl from within a Python script.
Here is the bash script snippet that works:
readable_code=$(echo "$encrypted_code"| openssl enc -aes-128-cbc -a -d -salt -pass pass:$key)
So in a python script - I understand that to run this same bash command I should use subprocess.
I need to Pipe the echo to the openssl command and as well pass in the encrypted_code and key variables dynamically(its in a loop).
Anyone out there know the correct syntax for this ?
Below's snippet should give the background to what i'm trying to do.
thank-you
import subprocess
key = "my-secret-key"
file = list_of_ips #format ip:long-encrypted-code
with open(file_read) as f:
#read in all connecion requests
content=f.readlines()
#create list that will hold all ips whose decrypted codes have passed test
elements = []
for ip_code in content:
#grab the ip address before the colon
ip = ip_code.split(':', 1)[0]
#grab the encrypted code after the colon
code = ip_code.split(':',1)[1]
#here is where I want to run the bash command and assign to a python variable
decrypted_code = subprocess....using code and key variables
...on it goes....
To emulate the shell command:
$ readable_code=$(echo "$encrypted_code"| openssl enc -aes-128-cbc -a -d -salt -pass "pass:$key")
using subprocess module in Python:
from subprocess import Popen, PIPE
cmd = 'openssl enc -aes-128-cbc -a -d -salt -pass'.split()
p = Popen(cmd + ['pass:' + key], stdin=PIPE, stdout=PIPE)
readable_code = p.communicate(encrypted_code)[0]
I highly recommend you to use Plumbum Python library to write shell scripts.
Particularly it has a convenient way to do piping and redirection.
I don't really understood what exact task you trying to solve, but your code could look approximately like this:
from plubum.cmd import openssl
with open('file') as f:
for ip_code in f:
(openssl['whatever', 'params'] << ip_code)()

How do I do symmetric encryption with the python gnupg module vers. 1.2.5?

I'm trying to do symmetric encryption with python and gnupg.
This code snippet works on my windows vista machine, on which the python gnupg module is version 0.3.2:
import gnupg
gpg = gnupg.GPG()
data = 'the quick brown fow jumps over the laxy dog.'
passphrase='12345'
crypt = gpg.encrypt(data, recipients=None,
symmetric='AES256',
passphrase=passphrase,
armor=False)
When I try to run it on my linux machine with the version 1.2.5 python gnupg module I get this error:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
File "/usr/local/lib/python2.7/dist-packages/gnupg/gnupg.py", line 961, in encrypt
result = self._encrypt(stream, recipients, **kwargs)
TypeError: _encrypt() got multiple values for keyword argument 'recipients'
I have done a number of searches and can't find anything on this.
This is an old ask, but I came upon this in a Google search and was unhappy with the provided answers. I found the real answer in python-gnupg's GitHub issues:
gpg.encrypt(data, symmetric='AES256', passphrase=passphrase, armor=False, encrypt=False)
So, drop the recipients=None and add an encrypt=False. Your crypt.data will then contain the encrypted data. Unintuitive, but it works.
(src: https://github.com/isislovecruft/python-gnupg/issues/110)
I think it's just broken (correct me if I'm wrong). It appears to be fixed in gnupg 0.3.6-1 for Python 3.x. You're probably better off using that or trying a workaround, such as one similar to what I'm about to describe.
This workaround uses the command-line gpg in Python instead of the gnupg module. You could do a similar workaround with gpg.encrypt_file, of course (which wouldn't have the temporarily stored passphrase I talk about in the next paragraph; that would most likely be a better choice).
If you're concerned about the passphrase showing up in the task manager, I took care of that by putting the password in a temporary file and using cat to pipe it into gpg. However, you might have to worry about malware snatching the password from the temporary file, if it can do it in time.
Note that this code is in Python 3.x, since that's what I use the most (to recreate it in 2.x you'll have to learn how temporary files work in it, and maybe a few other things).
import subprocess, tempfile, os, shutil
def gpg_encrypt(data, passphrase, alg="AES256", hash="SHA512", compress_alg="BZIP2", compress_lvl="9", iterations="1000000"):
#This is for symmetric encryption.
with tempfile.TemporaryDirectory() as directory:
filepath=os.path.join(directory, "tmp")
with open(filepath, "w") as FILE:
FILE.write(data)
iterations=str(iterations)
compress_level="--compress-level "+compress_lvl
if compress_alg.upper()=="BZIP2":
compress_level="--bzip2-compress-level "+compress_lvl
tmp_filename="tmp"
with tempfile.TemporaryDirectory() as DIR:
tmpfilepath=os.path.join(DIR, tmp_filename)
with open(tmpfilepath, "w") as FILE:
FILE.write(passphrase)
subprocess.Popen("cat "+tmpfilepath+"|gpg --batch --yes --passphrase-fd 0 --force-mdc --s2k-mode 3 --s2k-count "+iterations+" --s2k-cipher-algo "+alg+" --s2k-digest-algo "+hash+" --compress-algo='"+compress_alg+"' --compress-level "+compress_lvl+" -ac '"+filepath+"'", stdin=subprocess.PIPE, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True).communicate()[0]
result=None
with open(filepath+".asc", "r") as FILE:
result=FILE.read()
return result
def gpg_decrypt(data, passphrase):
#This is for decryption.
with tempfile.TemporaryDirectory() as directory:
filepath=os.path.join(directory, "tmp")
with open(filepath, "w") as FILE:
FILE.write(data)
decrypted=None
tmp_filename="tmp"
with tempfile.TemporaryDirectory() as DIR:
tmpfilepath=os.path.join(DIR, tmp_filename)
with open(tmpfilepath, "w") as FILE:
FILE.write(passphrase)
decrypted=subprocess.Popen("cat "+tmpfilepath+"|gpg --batch --yes --passphrase-fd 0 --output='"+filepath+".gpg"+"' '"+filepath+"'", stdin=subprocess.PIPE, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0]
result=None
decrypted=not "decryption failed:" in str(decrypted)
if decrypted==True:
with open(filepath+".gpg", "r") as FILE:
result=FILE.read()
return decrypted, result #If it worked, return True. If not, return False. (And, return the decrypted data.)
test=gpg_encrypt(data="This is a test!", passphrase="enter")
print("Here is the encrypted message:\n"+test)
decrypted, data=gpg_decrypt(data=test, passphrase="enter")
if decrypted:
print("Here is the decrypted message:\n"+data)
else:
print("Incorrect passphrase to decrypt the message.")
Here's the code for encrypting/decrypting symmetrically encrypted files (just for good measure):
import subprocess, tempfile, os, shutil
def gpg_encrypt_file(filepath, passphrase, output=None, alg="AES256", hash="SHA512", compress_alg="BZIP2", compress_lvl="9", iterations="1000000"):
#This is for symmetric encryption.
filepath=filepath.replace("'", "'\\''") #This makes it so you can have apostrophes within single quotes.
iterations=str(iterations)
compress_level="--compress-level "+compress_lvl
if compress_alg.upper()=="BZIP2":
compress_level="--bzip2-compress-level "+compress_lvl
result=None
tmp_filename="tmp"
with tempfile.TemporaryDirectory() as DIR:
tmpfilepath=os.path.join(DIR, tmp_filename)
with open(tmpfilepath, "w") as FILE:
FILE.write(passphrase)
if output:
if output[0]!=os.sep and filepath[0]==os.sep:
output=os.path.join(os.path.dirname(filepath), output)
result=subprocess.Popen("cat "+tmpfilepath+"|gpg --batch --yes --passphrase-fd 0 --force-mdc --s2k-mode 3 --s2k-count "+iterations+" --s2k-cipher-algo "+alg+" --s2k-digest-algo "+hash+" --compress-algo='"+compress_alg+"' "+compress_level+" --output='"+output+"' -ac '"+filepath+"'", stdin=subprocess.PIPE, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True).communicate()[0]
else:
result=subprocess.Popen("cat "+tmpfilepath+"|gpg --batch --yes --passphrase-fd 0 --force-mdc --s2k-mode 3 --s2k-count "+iterations+" --s2k-cipher-algo "+alg+" --s2k-digest-algo "+hash+" --compress-algo='"+compress_alg+"' --compress-level "+compress_lvl+" -ac '"+filepath+"'", stdin=subprocess.PIPE, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True).communicate()[0]
return result.strip()
def gpg_decrypt_file(filepath, passphrase, output=None):
#This is for decryption.
filepath=filepath.replace("'", "'\\''")
result=None
tmp_filename="tmp"
with tempfile.TemporaryDirectory() as DIR:
tmpfilepath=os.path.join(DIR, tmp_filename)
with open(tmpfilepath, "w") as FILE:
FILE.write(passphrase)
if output:
if output[0]!=os.sep and filepath[0]==os.sep:
output=os.path.join(os.path.dirname(filepath), output)
result=subprocess.Popen("cat "+tmpfilepath+"|gpg --batch --yes --passphrase-fd 0 --output='"+output+"' '"+filepath+"'", stdin=subprocess.PIPE, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0]
else:
result=subprocess.Popen("cat "+tmpfilepath+"|gpg --batch --yes --passphrase-fd 0 '"+filepath+"'", stdin=subprocess.PIPE, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0]
return not "decryption failed:" in str(result) #If it worked, return True. If not, return False.
gpg_encrypt_file(filepath="test.txt", passphrase="myPassphrase", output="test.asc")
if gpg_decrypt_file(filepath="test.asc", passphrase="myPassphrase", output="output.txt"):
print("Successfully decrypted!")
else:
print("Incorrect passphrase.")
The "standard" python-gnupg module's (instead of pretty-bad-protocol's module) symmetric-encrypt() call makes much more sense to me:
encrypted_data = gpg.encrypt(data=msg, symmetric=True, passphrase='1234', recipients=None)
Details: https://stackoverflow.com/a/72355824/605356
Double-check the documentation, the argument isn't recipient, is is recipients (note the plural)
Docs (emphasis mine):
symmetric (defaults to False) If specified, symmetric encryption is
used. In this case, specify recipients as None. If True is specified,
then the default cipher algorithm (CAST5) is used. Starting with
version 0.3.5, you can also specify the cipher-algorithm to use (for
example, 'AES256'). Check your gpg command line help to see what
symmetric cipher algorithms are supported. Note that the default
(CAST5) may not be the best available.
This should work with python-gnupg 0.3.5 and above (adjust as necessary):
import gnupg
gpg_home = "~/.gnupg"
gpg = gnupg.GPG(gnupghome=gpg_home)
data = raw_input("Enter full path of file to encrypt: ")
phrase = raw_input("Enter the passphrase to decrypt the file: ")
cipher = raw_input("Enter symmetric encryption algorithm to use: ")
savefile = data+".asc"
afile = open(data, "rb")
encrypted_ascii_data = gpg.encrypt_file(afile, None, passphrase=phrase, symmetric=cipher.upper(), output=savefile)
afile.close()
Not sure about versions prior to 0.3.5.
The "None" for recipients does not (with current versions of the module) require "recipients=None" when the gpg.encrypt_file() is called.
gpg = gnupg.GPG()
data = 'the quick brown fow jumps over the laxy dog.'
passphrase='12345'
crypt = gpg.encrypt(
data,
recipients=None,
symmetric='AES256',
passphrase=passphrase,
armor=False,
)
print(crypt.data)
clear = gpg.decrypt(
crypt.data,
passphrase=passphrase,
)
print(clear)
As of July 28, 2022, The above works just fine for me.
Python 3.9.2 --
python-gnupg 0.4.9

Categories

Resources