Implementing SHA1-HMAC with Python - python

I am implementing SHA1-HMAC generation for python (v 3.7) to be able to create HMAC code.
I have used an online generator to create SHA1-HMAC with the following data:
string: '123'
Secret Key: 'secret'
Digest algorithm: SHA1
I am getting this result:
b14e92eb17f6b78ec5a205ee0e1ab220fb7f86d7
However when I try to do this same with Python I am getting different results which are wrong.
import hashlib
import hmac
import base64
def make_digest(message, key):
key = bytes(key, 'UTF-8')
message = bytes(message, 'UTF-8')
digester = hmac.new(key, message, hashlib.sha1)
signature1 = digester.digest()
signature2 = base64.urlsafe_b64encode(signature1)
return str(signature2, 'UTF-8')
result = make_digest('123', 'secret')
print(result)
This code gives result:
sU6S6xf2t47FogXuDhqyIPt_htc=
What could be wrong with this code?

You should not use Base64 here. The site you link to gives you the hex values of the digest bytes. Use the HMAC.hexdigest() method to get the same value in hex in Python:
>>> key = b'secret'
>>> message = b'123'
>>> digester = hmac.new(key, message, hashlib.sha1)
>>> digester.hexdigest()
'b14e92eb17f6b78ec5a205ee0e1ab220fb7f86d7'
put differently, your code outputs the correct value, but as Base64-encoded data:
>>> digester.digest()
b'\xb1N\x92\xeb\x17\xf6\xb7\x8e\xc5\xa2\x05\xee\x0e\x1a\xb2 \xfb\x7f\x86\xd7'
>>> base64.urlsafe_b64encode(digester.digest())
b'sU6S6xf2t47FogXuDhqyIPt_htc='
and the value you generated online contains the exact same bytes as the hex digest, so we can generate the same base64 output for that:
>>> bytes.fromhex('b14e92eb17f6b78ec5a205ee0e1ab220fb7f86d7')
b'\xb1N\x92\xeb\x17\xf6\xb7\x8e\xc5\xa2\x05\xee\x0e\x1a\xb2 \xfb\x7f\x86\xd7'
>>> base64.urlsafe_b64encode(bytes.fromhex('b14e92eb17f6b78ec5a205ee0e1ab220fb7f86d7'))
b'sU6S6xf2t47FogXuDhqyIPt_htc='

Related

DSA key format not supported

I am trying to do a P2MS script.
For my script, I am saving the keys into a text file with DER format instead of the usual PEM file. Both key and signatures are saved in a text file and hexlify. Below is my code for the P2MS execution.
from Crypto.PublicKey import DSA
from Crypto.Hash import SHA256
from Crypto.Signature import DSS
from binascii import hexlify, unhexlify
import binascii
message = b"helpsmepls"
# Read scriptPubKey and scriptSig from files
with open('scriptPubKey.txt', 'r') as f:
readscriptPubKey = f.read().strip()
with open('scriptSig.txt', 'r') as f:
scriptSig = f.read().strip()
print(type(readscriptPubKey))
tempholder = readscriptPubKey.split()
# Removing the first character and last character
removeend = tempholder[1:-1]
scriptPubKey = []
# Removing front extra headings
for count, x in enumerate(removeend):
w = bytes(removeend[count][1:-1], encoding = 'utf-8')
#print(w)
scriptPubKey.append(w)
# Splitting the Signatures
signatures = scriptSig.split()
hash_obj = SHA256.new(message)
# Going through the pubkeys based on the number of signatures generated
for o, sig in enumerate(signatures):
pub_key = DSA.import_key(bytes.fromhex(scriptPubKey[o].decode("utf-8")))
hash_obj = SHA256.new(message)
verifier = DSS.new(pub_key, 'fips-183-3')
# Verifying if the Public key and signatures match, loop will break if False is encountered
if verifier.verify(hash_obj, sig):
d = True
else:
d = False
break
break
if d == True:
print("The message is authentic.")
else: print("The message is not authentic.")
Unfortunately before my code can reach the verification, it encountered an error.
Full traceback
It seems my DSA key format has an error, but I am not too sure why is it giving me that error.
I have also tried unhexlifying my input from the public key text file, but it also did not work. I have tried to hex decode the input to get the DER format of the input, but my type is still just bytes. I am not so sure how to properly import the key with the appropriate DSA key format from a txt file. I am able to do that with a PEM file but would just like to find out how to execute it with a txt file.
My expected outcome is the DSA key is imported properly and i am able to verify the public key with the signatures.

Decoding a fetched email header or subject into a readable manner

I get emails with unique subjects, and I want to save them.
I tried this (stage with credentials input is omitted)
import email
import imaplib
suka.select('Inbox')
key = 'FROM'
value = 'TBD'
_, data = suka.search(None, key, value)
mail_id_list = data[0].split()
msgs = []
for num in mail_id_list:
typ, data = suka.fetch(num, '(RFC822)')
msgs.append(data)
for msg in msgs[::-1]:
for response_part in msg:
if type(response_part) is tuple:
my_msg=email.message_from_bytes((response_part[1]))
print ("subj:", my_msg['subject'])
for part in my_msg.walk():
#print(part.get_content_type())
if part.get_content_type() == 'text/plain':
print (part.get_payload())
I do get the subjects, but in a form of "subj: =?utf-8?B?0LfQsNGP0LLQutCwIDIxXzE0MTIyMg==?=". Thus, a decoding is required. The secret seems to be, which variable needs to be decoded?
Also tried the other way:
yek, do = suka.uid('fetch', govno,('RFC822'))
, where govno is the latest email in the inbox. The output is "can't concat int to bytes".
Thus, is there a way to decode the subjects as they appear in the email client? Thank you.
There is a built-in decode_header() method.
Decode a message header value without converting the character set.
The header value is in header.
This function returns a list of (decoded_string, charset) pairs
containing each of the decoded parts of the header. charset is None
for non-encoded parts of the header, otherwise a lower case string
containing the name of the character set specified in the encoded
string.
>>> from email.header import decode_header
>>> decoded_headers = decode_header("=?utf-8?B?0LfQsNGP0LLQutCwIDIxXzE0MTIyMg==?=")
>>> decoded_headers
[(b'\xd0\xb7\xd0\xb0\xd1\x8f\xd0\xb2\xd0\xba\xd0\xb0 21_141222', 'utf-8')]
>>> first = decoded_headers[0]
>>> first[0].decode(first[1])
'заявка 21_141222'
You can decode the actual value returned by decode_header using the charset returned by it.
For follow-up question, here's a helper function to get the header value in case of multiline header value which handlers errors -
from email.header import decode_header
def get_header(header_text, default='utf8'):
try:
headers = decode_header(header_text)
except:
print('Error while decoding header, using the header without decoding')
return header_text
header_sections = []
for text, charset in headers:
try:
# if charset does not exist, try to decode with utf-8
header_section = text.decode(charset or 'utf-8')
except:
# if we fail to decode the text(header is incorrectly encoded)
# Try to do decode, and ignore the decoding errors
header_section = text.decode('utf-8', errors='ignore')
if header_section:
header_sections.append(header_section)
return ' '.join(header_sections)
print(get_header("=?utf-8?B?0LfQsNGP0LLQutCwIDIxXzE0MTIyMg==?="))

How to fix 'str' object has no attribute 'encrypt'?

I'm trying to to encrypt a message using a given public key pubkey that i read off a file. When I try to run my program I get an error message stating that
'str' object has no attribute 'encrypt'.
I have already tried encoding the public key, but I am still getting the same error. Since I need to use these specific public key I cannot generate another random public key.
Following is my code:
from Crypto.PublicKey import RSA
import math
def text_to_int(text):
"""
Converts text into an integer for use in encryption.
input: text - a plaintext message
output: integer - an integer encoding text
"""
integer = 0
for char in text:
if integer > 0:
integer = integer*256
integer = integer + ord(char)
return integer
#Read public key
with open('C:\\Users\\alan9\\Downloads\\Assignement2\\Supporting_Files\\HLand_Key.pub', 'rb') as f:
read_key = f.read()
# use the PUBLIC KEY to encrypt a message:
message = "attack early next week"
message_int = text_to_int(message)
ciphertext = read_key.encrypt(message_int, None)
#Write ciphertext to file
with open('C:\\Users\\alan9\\Downloads\\Assignement2\\Supporting_Files\\cipher.txt', 'w') as f_write:
f_write.write(ciphertext)
I expect the output to return the encrypted message in a text file named cipher using the specified public key.
Any help will be appreciated!!!

Generate temporary URLs in Django 2 Python 3

There is one same question in StackOverflow with this link:
How to generate temporary URLs in Django
But the accepted answer code is for Python 2 and I converted it to Python 3:
import hashlib, zlib
import pickle as pickle
import urllib.request, urllib.parse, urllib.error
my_secret = "michnorts"
def encode_data(data):
"""Turn `data` into a hash and an encoded string, suitable for use with `decode_data`."""
text = zlib.compress(pickle.dumps(data, 0)).encode('base64').replace('\n', '')
m = hashlib.md5(my_secret + text).hexdigest()[:12]
return m, text
def decode_data(hash, enc):
"""The inverse of `encode_data`."""
text = urllib.parse.unquote(enc)
m = hashlib.md5(my_secret + text).hexdigest()[:12]
if m != hash:
raise Exception("Bad hash!")
data = pickle.loads(zlib.decompress(text.decode('base64')))
return data
hash, enc = encode_data(['Hello', 'Goodbye'])
print(hash, enc)
print(decode_data(hash, enc))
But it have error :
text = zlib.compress(pickle.dumps(data, 0)).encode('base64').replace('\n', '')
AttributeError: 'bytes' object has no attribute 'encode'
How should I fix this?
Trying to adapt your code to Python 3, I came up with this:
import hashlib, zlib
import pickle as pickle
import urllib.request, urllib.parse, urllib.error
import base64
my_secret = "michnorts"
def encode_data(data):
"""Turn `data` into a hash and an encoded string, suitable for use with `decode_data`."""
compressed_text = zlib.compress(pickle.dumps(data, 0))
text = base64.b64encode(compressed_text).decode().replace('\n', '')
m = hashlib.md5(str.encode('{}{}'.format(my_secret, text))).hexdigest()[:12]
return m, text
def decode_data(hash, enc):
"""The inverse of `encode_data`."""
text = urllib.parse.unquote(enc)
m = hashlib.md5(str.encode('{}{}'.format(my_secret, text))).hexdigest()[:12]
if m != hash:
raise Exception("Bad hash!")
data = pickle.loads(zlib.decompress(base64.b64decode(text)))
return data
hash, enc = encode_data(['Hello', 'Goodbye'])
print(hash, enc)
print(decode_data(hash, enc))
There are some things that I needed to take into account:
in Python 3, the way to encode/decode into base64 is by using the base64 module
to cast a bytes object into a string, I used the bytes.decode method
to cast a string object into a bytes object, I used the str.encode function
the hashlib.md5 function accepts a bytesobject (strings need to be previously encoded)
I changed the way you concatenate strings (i.e. str1 + str2) with a more pythonic construction: '{}{}'.format(str1, str2)
I hope this will be helpful ;)
I recommend to use the built-in secrets modules, especially secrets.token_urlsafe.

Generating a Temporary url in Python

I've been trying to generate a temporary url in python, the url will some data that i need to make sure isn't changed so i'll add a hash in the end but i keep ending with a bString no matter what i try, can anyone point out what i'm doing wrong?
Here's a sample of my code
oh and i know that maybe changing the algorithms/encoding might solve the problem but i can't find a suitable one, can any downvoter explain why he downvoted
import hashlib
import datetime
from Crypto.Cipher import AES
def checkTemp(tempLink):
encrypter = AES.new('1234567890123456', AES.MODE_CBC, 'this is an iv456')
decryption = encrypter.decrypt(tempLink)
length = len(decryption)
hash_code = decryption[length-32:length]
data= decryption[:length-32].strip()
hasher = hashlib.sha256()
hasher.update(data)
hashCode = hasher.digest()
if(hash_code==hashCode):
array = data.decode().split(",",5)
print("expiry date is :"+ str(array[5]))
return array[0],array[1],array[2],array[3],array[4]
else:
return "","","","",""
def createTemp(inviter,email,role,pj_name,cmp_name):
delim = ','
data = inviter+delim+email+delim+role+delim+pj_name+delim+cmp_name+delim+str(datetime.datetime.now().time())
data = data.encode(encoding='utf_8', errors='strict')
hasher = hashlib.sha256()
hasher.update(data)
hashCode = hasher.digest()
encrypter = AES.new('1234567890123456', AES.MODE_CBC, 'this is an iv456')
# to make the link a multiple of 16 by adding for AES with the addition of spaces
newData = data+b' '*(len(data)%16)
result = encrypter.encrypt(newData+hashCode)
return result
#print(str(link).split(",",5))
link = createTemp("name","email#homail.com","Designer","Project Name","My Company")
print(link)
inviter,email,role,project,company = checkTemp(link)
The problem was not being able to putout a normal string because the encryption will result in characters that are almost impossible to encode, so the solution is to use binascii to encode the bStrings for us and decode them
import binascii
then we encode the resulting usable string for the link
hexedLink = binascii.hexlify(link).decode()
and we unhexify it before using it in the method
inviter,email,role,project,company = checkTemp(binascii.unhexlify(hexedLink))

Categories