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

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.

Related

How to capture the output of openssl in 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.

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.

timestamp query with python

I want to create a .tsq file like openssl with command:
openssl ts -query -data <file> -no_nonce -sha512 -out <out.tsq>
I want to implement this with python, Anyone know how to do this, any module or something like that?
Right now I can think of three different approaches:
Use some premade python module of unknown quality like python-rfc3161 mentioned by #J.F.Sebastian in his comment.
Use hashlib module to compute SHA512 hash of the data you want to timestamp and then use pyasn1 module to construct and encode TimeStampReq request structure defined in RFC3161.
Use hashlib module to compute SHA512 hash of the data you want to timestamp and pre-pend these bytes 0x30 0x56 0x02 0x01 0x01 0x30 0x51 0x30 0x0D 0x06 0x09 0x60 0x86 0x48 0x01 0x65 0x03 0x04 0x02 0x03 0x05 0x00 0x04 0x40 to the hash value. This should work for you because OpenSSL command you have provided is creating TS request which does not contain any variable part (such as nonce or policy OID) so the first part of the request structure will not change no matter what input data you will use.
Here's a Python 3 implementation of the 3rd idea from #jariq's answer:
#!/usr/bin/env python3
"""Emulate `openssl ts -query -data <file> -no_nonce -sha512 -out <out.tsq>`
Usage: %(prog)s <file> [<out.tsq>]
If <out.tsq> is not given; use <file> name and append '.tsq' suffix
"""
import hashlib
import sys
from functools import partial
def hash_file(filename, hashtype, chunksize=2**15, bufsize=-1):
h = hashtype()
with open(filename, 'rb', bufsize) as file:
for chunk in iter(partial(file.read, chunksize), b''):
h.update(chunk)
return h
try: # parse command-line arguments
filename, *out_filename = sys.argv[1:]
out_filename.append(filename + '.tsq')
except ValueError:
sys.exit(__doc__ % dict(prog=sys.argv[0]))
h = hash_file(filename, hashlib.sha512) # find hash of the input file
with open(out_filename[0], 'wb') as file: # write timestamp query
file.write(b'0V\x02\x01\x010Q0\r\x06\t`\x86H\x01'
b'e\x03\x04\x02\x03\x05\x00\x04#')
file.write(h.digest())
To expand upon #j-f-sebastian's answer, if you want to hash using sha-256 (or any 256 bit hash function), use the following constant:
b'06\x02\x01\x01010\r\x06\t`\x86H\x01e\x03\x04\x02\x01\x05\x00\x04 '
(and yes, the last character is an empty space)

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)()

OpenSSL does not match Python hashlib

I am new to using the OpenSSL library and I cannot get its output to match the output from python's crypto libraries. Below is an example.
Python Code:
import hmac
secret = "asdf1234"
string = '{"request"}'
hmac.new(secret, string, hashlib.sha384).hexdigest()
'92904f1b3c210a4fb19f476c19f24452717e85329aa9ffaba4a5fbe1111c2e3fa7f5a5fb35fdc58b3d158f5f886c3d02'
OpenSSl:
echo -n {"request"} | openssl dgst -sha384 -hmac asdf1234 -hex
(stdin)= 4c3d525b8a7095b9063a3bd974e56f0a5872399365912297d6ee18e400d2b55d0142395ba5fb4f33655ceca209ba9570
What am I doing wrong? Is either implementation correct?
To match the openssl output, the Python string should be '{request}' rather than '{"request"}'.
import hmac
import hashlib
secret = "asdf1234"
string = '{request}'
hmac.new(secret, string, hashlib.sha384).hexdigest()
yields
'4c3d525b8a7095b9063a3bd974e56f0a5872399365912297d6ee18e400d2b55d0142395ba5fb4f33655ceca209ba9570'
Or, if you want the openssl command to match the Python output, use
echo -n '{"request"}' | openssl dgst -sha384 -hmac asdf1234 -hex
which yields
(stdin)= 92904f1b3c210a4fb19f476c19f24452717e85329aa9ffaba4a5fbe1111c2e3fa7f5a5fb35fdc58b3d158f5f886c3d02
After all, the inputs have to match for the outputs to have a chance at matching:
% echo -n {"request"}
{request}
and
>>> print('{"request"}')
{"request"}

Categories

Resources