How to implement RSA encryption/decryption on python dictionary? - python-3.x

I am implementing RSA encryption/decryption. The data which is to be encrypted contains different data types. Hence I have used dictionary in python. However I am getting errors. I am attaching code snippet. Please provide solution !! Thank you.
import base64
from cryptography.fernet import Fernet
import os
import hashlib
from cryptography.hazmat.backends import default_backend
from Crypto.PublicKey import RSA
from cryptography.hazmat.primitives.asymmetric import padding
from Crypto import Random
class SplitStore:
....
def encrPayload(self, payload):
modulus_length = 256 * 13 # use larger value in production
privatekey = RSA.generate(modulus_length, Random.new().read)
public_key = privatekey.publickey()
private_pem = privatekey.exportKey().decode()
with open('private_pem.pem', 'w') as pr:
pr.write(private_pem)
byte_payload = payload.encode('utf-8', 'strict')
#print("Byte Payload:{} ".format(byte_payload))
encrypted_payload = public_key.encrypt(byte_payload, 32)[0]
#print("type:{}".format(type(encrypted_payload)))
#print("\nEncrpted METADATA: {}".format(encrypted_payload))
bchain_file = open("blockchain/file.dat", "wb")
bchain_file.write((encrypted_payload))
def createMetaData(self):
time_stamp = os.stat("/home/pc-01/sample.pdf").st_ctime
#print("\nTIMESTAMP: {}".format(time_stamp))
hash_data = hashlib.sha512(self.pdfReader).hexdigest()
#print("\nHASHDATA : {}".format(hash_data))
secret_keys = self.j #j is a list of keys
#print("List of secret keys: {}".format(self.j))
#print("String conv: " + str(secret_keys[0]))
#Creating a dictionary
payload = {
"time_stamp" : time_stamp, #int
"hash" : hash_data, #string
"keys" : secret_keys #list
}
#print("\nPAYLOAD: " + payload)
return payload
...
def main():
s1 = SplitStore()
s1.initializeData()
...
if __name__ == "__main__":
main()
secret_keys in payload is a list containing keys in bytes format. After running the code I get following error:
Traceback (most recent call last):
....... chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python3.5/json/encoder.py", line 256, in iterencode return _iterencode(o, 0)
File "/usr/lib/python3.5/json/encoder.py", line 179, in default raise TypeError(repr(o) + " is not JSON serializable")
TypeError: b'Vm3pb7XRJ4W_8M1ShKHAGiuDa2PT1DN_0ncjf0hmNJU=' is not JSON serializable
Is there any way to solve this issue?

If the problem isn't with the bytes as described below, you can narrow down on what causes the issue by following this answer and figuring out which of your variables are not serializable.
Your items in secret_keys are bytes, which are not JSON serializable. So you'll have to convert them to normal str objects. You can do so via the decode method as follows:
plainstr = bytestr.decode("utf-8")
# The encoding passed need not be "utf-8", but it is quite common
Similarly, you can encode a str to bytes as follows:
encodedstr = plainstr.encode("utf-8")

Related

Implementing Laravel crypt and decrypt functions in python

I was able to find the decrypt function with a few researches, and now
I am trying to write laravel encrypt function using python.
I can decrypt using it:
class decrypter:
def __init__(cls, key):
cls.key = key
def decrypt(cls, text):
decoded_text = json.loads(base64.b64decode(text))
iv = base64.b64decode(decoded_text['iv'])
crypt_object = AES.new(key=cls.key, mode=AES.MODE_CBC, IV=iv)
decoded = base64.b64decode(decoded_text['value'])
decrypted = crypt_object.decrypt(decoded)
return unpad(decrypted, 16).decode('utf-8')
def decrypt_string(str):
try:
key = b"xxxx+xxxxxx+x+xxxx+xxxxx"
key = base64.b64decode(key)
msg = str
obj = decrypter(key)
decrypted = obj.decrypt(msg)
return decrypted
except Exception as e:
logla.logla(e, "decrypt_string")
print(e)
But I couldn't find a source for the encrypt method. There is a source I could find, but I couldn't run it.
enter link description here
For encryption, proceed in the opposite direction:
Create an IV
Pad plaintext
Save IV and ciphertext to JSON
Encode JSON with Base64
For encryption as in the linked code, additionally the MAC has to be generated and the PHP serialization has to be used:
import json
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes
from Crypto.Hash import HMAC, SHA256
from phpserialize import loads, dumps
class encrypter:
def __init__(cls, key):
cls.key = key
def encrypt(cls, text):
text = dumps(text)
msg = pad(text, 16)
iv = get_random_bytes(16) # b'0123456789012345'
crypt_object = AES.new(key=cls.key, mode=AES.MODE_CBC, IV=iv)
encrypted = crypt_object.encrypt(msg)
ivB64 = base64.b64encode(iv)
encryptedB64 = base64.b64encode(encrypted)
mac = HMAC.new(cls.key, digestmod=SHA256).update(ivB64+encryptedB64).hexdigest()
json_string = json.dumps({'iv': ivB64.decode(), 'value': encryptedB64.decode(), 'mac': mac})
return base64.b64encode(json_string.encode())
def encrypt_string(str, key):
try:
msg = str.encode()
obj = encrypter(key)
encrypted = obj.encrypt(msg)
return encrypted
except Exception as e:
print(e)
# Test
keyB64 = b'MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE='
key = base64.b64decode(keyB64)
plaintext= 'This is a test plaintext'
encrypted = encrypt_string(plaintext, key)
decrypted = decrypt_string(encrypted, key)
print(encrypted)
print(base64.b64decode(encrypted))
For the test-IV b'0123456789012345' the output is:
b'eyJpdiI6ICJNREV5TXpRMU5qYzRPVEF4TWpNME5RPT0iLCAidmFsdWUiOiAiTlE1djFpaWU1QnFoTWNwRlhNdUFSZ2N3YVlrNG5CZlJyYmRKUGRna3FDcUN6NEZ6ZDhSOHhIUy95N1N3TWlQTyIsICJtYWMiOiAiYmYwNGJjMWEyN2NhNWUzMGFlYTdjZTI4Y2FkYTBlZGVjOGEwMzc3NWZhODVhMDc2MGRhODUzNDc1OTBmYmNmZCJ9'
b'{"iv": "MDEyMzQ1Njc4OTAxMjM0NQ==", "value": "NQ5v1iie5BqhMcpFXMuARgcwaYk4nBfRrbdJPdgkqCqCz4Fzd8R8xHS/y7SwMiPO", "mac": "bf04bc1a27ca5e30aea7ce28cada0edec8a03775fa85a0760da85347590fbcfd"}'
The linked code produces the same output using the same plaintext, key, and test-IV.

RSA Encryption and Decryption using 2 files

So I have created 2 files one for encryption and other for decryption. I am able to encrypt the text but when it comes to decrypting I get an error (See bottom of question). I am able to export the Private_key.pem, public_key.pem and even the secret.txt file. I am able to read the private_key.pem in my decrypt.py script but it errors out. I believe the script thinks that the private_key it is using is not correct. I know this is wrong as I generate the public and private key, use the public key to encrypt and private to decrypt. If anyone can shed some light on this it would be greatly appreciated.
Encrypt.py
from Crypto import Cipher
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto.PublicKey.pubkey import pubkey
def privKEY():
private_key = RSA.generate(4096)
return private_key
def pubKey():
public_Key = privKEY().publickey()
return public_Key
def convertPrivKey():
private_pem = privKEY().exportKey().decode()
with open('private_pem.pem','w') as pr:
pr.write(private_pem)
return private_pem
def convertPubKey():
public_pem = pubKey().exportKey().decode()
with open('public_pem.pem','w') as pu:
pu.write(public_pem)
return public_pem
def encrypt():
privKEY()
pubKey()
convertPrivKey()
convertPubKey()
message = 'Testing RSA Encryption'
message = message.encode()
pu_key = RSA.importKey(open('public_pem.pem','r').read())
print(pu_key)
cipher = PKCS1_OAEP.new(pu_key)
cipher_text = cipher.encrypt(message)
with open('secret.txt','w') as f:
f.write(str(cipher_text))
encrypt()
decrypt.py
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
def load_secure_file():
return open('secret.txt','r').read()
def pr_key_read():
pr_key = RSA.importKey(open('private_pem.pem','r').read())
return pr_key
def decrypt():
encrypted_text = load_secure_file()
pr_key = pr_key_read()
decrypt = PKCS1_OAEP.new(pr_key)
decrypted_message = decrypt.decrypt(encrypted_text)
return decrypted_message
decrypt()
ERROR:
PS Y:\Python\RSA> & C:/Anaconda/python.exe y:/Python/RSA/decrypt.py
Traceback (most recent call last):
File "y:/Python/RSA/decrypt.py", line 19, in <module>
decrypt()
File "y:/Python/RSA/decrypt.py", line 17, in decrypt
decrypted_message = decrypt.decrypt(encrypted_text)
File "C:\Anaconda\lib\site-packages\Crypto\Cipher\PKCS1_OAEP.py", line 195, in decrypt
raise ValueError("Ciphertext with incorrect length.")
ValueError: Ciphertext with incorrect length.
New Error:
Traceback (most recent call last):
File "y:/Python/RSA/decrypt.py", line 19, in <module>
decrypt()
File "y:/Python/RSA/decrypt.py", line 14, in decrypt
encrypted_text = load_secure_file()
File "y:/Python/RSA/decrypt.py", line 7, in load_secure_file
return open('secret.txt','r').read()
File "C:\Anaconda\lib\encodings\cp1252.py", line 23, in decode
return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 30: character maps to <undefined>
Topaco is right: you're actually generating four keypairs, throwing away the first two, writing the private key from the third and the public key from the fourth. These keys don't match and thus don't work. You should generate one keypair and use the private and public halves of that pair. The following slightly simplified code works for me:
from Crypto import Cipher
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
# privKEY,pubKey
private_key = RSA.generate(2048) # shortened for testing
public_key = private_key.publickey()
# 'convert' really store
with open('private.pem','w') as pr:
pr.write(private_key.exportKey().decode())
with open('public.pem','w') as pu:
pu.write(public_key.exportKey().decode())
# encrypt
message = 'Testing RSA'.encode()
pu_key = RSA.importKey(open('public.pem','r').read())
ciphertext = PKCS1_OAEP.new(pu_key).encrypt(message)
with open('secret.txt','wb') as f:
f.write(ciphertext)
# decrypt
ciphertext = open('secret.txt','rb').read()
pr_key = RSA.importKey(open('private.pem','r').read())
decrypted = PKCS1_OAEP.new(pr_key).decrypt(ciphertext)
print(decrypted)
-->
b'Testing RSA'
The caveat that this only works for limited amount of data remains.

Python - nested lists q

Pasted my code below. I'm trying to isolate the "name" value of a list inside the nested list the api replies with but its not detected as a nested list for some reason. My response or what the decode variable prints is:
[{"id":"a981fdae11a949b2bb3c78cc2c7f820d","name":"Juice"},{"id":"ec07182556b444d38e9af48bf74bde82","name":"Dog"},{"id":"2aca11f3fa364adf83183e10e8794c46","name":"csheim","legacy":true}]
From the above output I need to isolate / print the name "csheim" as its sub list contains "legacy : true" but as I said above, I get this when even try to detect sub/nested lists inside my list so I don't know how to go about this. Error: print(decode[1][1])
IndexError: string index out of range
import requests
import time
import socket
import json
import string
host_ip = socket.gethostname()
print("")
print(socket.gethostbyname(host_ip))
print("")
time.sleep(2)
payload = ["juice", "csheim", "dog"]
r = requests.post('https://api.example.com', json=payload)
decode = r.text
print(decode)
print(decode[1][1])
You should parse it with json library , then treat that like an array of dictionaries.
Try this :
import requests
import time
import socket
import json
import string
host_ip = socket.gethostname()
print("")
print(socket.gethostbyname(host_ip))
print("")
time.sleep(2)
payload = ["juice", "csheim", "dog"]
r = requests.post('https://api.example.com', json=payload)
decode = json.loads(r.text)
for item in decode :
if "legacy" in item :
print(item["name"])

Getting Exception: Object type <class 'str'> cannot be passed to C code

I install pip install pycryptodome on Python 3.7.2. I'm getting above exception for obj = AES.new(key, AES.MODE_CBC, iv) line. my code is:
from Crypto import Random
from Crypto.Cipher import AES
import random
def get_encryption():
try:
str = "This is input string"
key = b'abcdefghijklmnop'
iv = Random.new().read(AES.block_size)
obj = AES.new(key, AES.MODE_CBC, iv)
encrypted = obj.encrypt(str)
print(encrypted)
except Exception as e:
print(e)
I tried to all the way but not getting how to solve it.
After tried all the way I got solution. I converted key string into bytes.
code is:
from Crypto import Random
from Crypto.Cipher import AES
import random
def get_encryption():
try:
strmsg = "This is input string"
key = 'abcdefghijklmnop'
key1 = str.encode(key)
iv = Random.new().read(AES.block_size)
obj = AES.new(key1, AES.MODE_CBC, iv)
encrypted = obj.encrypt(str.encode(strmsg))
print(encrypted)
except Exception as e:
print(e)
//First pip install pycryptodome -- (pycrypto is obsolete and gives issues)
// pip install pkcs7
from Crypto import Random
from Crypto.Cipher import AES
import base64
from pkcs7 import PKCS7Encoder
from app_settings.views import retrieve_settings # my custom settings
app_secrets = retrieve_settings(file_name='secrets');
def encrypt_data(text_data):
#limit to 32 bytes because my encryption key was too long
#yours could just be 'abcdefghwhatever'
encryption_key = app_secrets['ENCRYPTION_KEY'][:32];
#convert to bytes. same as bytes(encryption_key, 'utf-8')
encryption_key = str.encode(encryption_key);
#pad
encoder = PKCS7Encoder();
raw = encoder.encode(text_data) # Padding
iv = Random.new().read(AES.block_size )
# no need to set segment_size=BLAH
cipher = AES.new( encryption_key, AES.MODE_CBC, iv )
encrypted_text = base64.b64encode( iv + cipher.encrypt( str.encode(raw) ) )
return encrypted_text;
The easiest way to convert the string to bytes is using the binascii lib:
from binascii import unhexlify, hexlify
def aes_encript(key, msg):
c = unhexlify(key)
m = unhexlify(msg)
cipher = AES.new(c, AES.MODE_ECB)
msg_en = cipher.encrypt(m)
return hexlify(msg_en)

NameError while calling module, declaring default values in a class function

I'm trying to understand Public Key encryption so I wrote this little module using PyCryptodome and the RSA/PKCS1_OAEP module on Python 3 to help me along. However, I keep getting an error:
NameError: name 'aesenc' is not defined
This is a two part question:
In standalone code (outside a class) the arg = default_val code will work, but I'm pretty sure this code will throw an error (assuming I fix question #2). I also know I can't use self.default_val as that needs an object to be created first. How do I assign a default value (in this case, the private/public key of the object?)
With regard to the error message, a vgrep reveals that the suite has been declared before its been called but I still get the NameError. Can someone please take a look and let me know what I'm doing wrong?
Modules: (Breaking into parts as SO keeps jumbling the code)
from passlib.context import CryptContext
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto import Random
from Crypto.Random import get_random_bytes
The Class:
class statEnc:
pubcipher = None
privcipher = None
pubkeystr = None
privkeystr = None
sessionkey = None
def __init__(self, pubkeystr = None, privkeystr = None, sessionkey = None):
self.pubkeystr = pubkeystr
self.privkeystr = privkeystr
self.sessionkey = sessionkey
if pubkeystr == None or privkeystr == None: #if blank, generate keys
self.random_generator = Random.new().read
self.keys = RSA.generate(1024, self.random_generator)
self.pubkey = self.keys.publickey()
self.pubkeystr = self.pubkey.exportKey(format='PEM',
passphrase=None,
pkcs=1)
self.pubcipher = PKCS1_OAEP.new(self.pubkey)
self.privcipher = PKCS1_OAEP.new(self.keys)
self.privkeystr = self.keys.exportKey(format='PEM',
passphrase=None,
pkcs=1)
self.privkey = self.keys.exportKey()
else: #import the keys
self.pubkey = RSA.importKey(pubkeystr)
self.pubcipher = PKCS1_OAEP.new(pubkey)
self.privkey = RSA.importKey(privkeystr)
self.pubcipher = PKCS1_OAEP.new(privkey)
if sessionkey == None:
sessionkey = get_random_bytes(16)
else:
self.sessionkey = sessionkey
def encrypt_val(self, session_key, cipher = pubcipher):
# a little ditty to hep encrypt the AES session key
try:
session_key = session_key.encode('utf8')
except:
pass
ciphertext = cipher.encrypt(session_key)
return ciphertext
def decrypt_val(self, ciphertext, cipher = privcipher):
# a little ditty to hep decrypt the AES session key
session_key = cipher.decrypt(ciphertext)
try:
session_key = session_key.decode('utf8')
except:
pass
return session_key
def aesenc(self, data, *args):
#encrypt the payload using AES
key = ''
if args:
if 'str' in str(type(args[0])):
try:
key = int(args[0])
except:
key = get_random_bytes(16)
else:
key = get_random_bytes(16)
try:
data = data.encode('utf8')
except:
pass
cipher = AES.new(key, AES.MODE_EAX)
ciphertext, tag = cipher.encrypt_and_digest(data)
aesencdict = {
'ciphertext' : ciphertext,
'tag' : tag,
'nonce' : cipher.nonce ,
'key' : key
}
return(aesencdict)
def aesdec(self, aesdict):
#decrypt the AES encrypted payload
cipher = AES.new(aesdict['key'], AES.MODE_EAX, aesdict['nonce'])
data = cipher.decrypt_and_verify(aesdict['ciphertext'], aesdict['tag'])
try:
data = data.decode('utf8')
except:
pass
return data
def end2enc(self, val, cipher = pubcipher):
# a master function to first encrypt the payload
val = str(val)
encval = aesenc(val)
# and then PK encrypt the key
encval['key'] = encrypt_val(encval['key'], cipher)
return encval
def end2dec(self, encval, cipher = privcipher):
encval['key'] = decrypt_val(encval['key'], cipher)
outval = aesdec(encval['aesdict'], encval['key'])
return outval
Test function and main:
def test():
val = { 'test' : "hello"}
keypair = statEnc()
print(str(type(keypair)))
encval = keypair.end2enc(val, keypair.pubcipher)
outval = keypair.end2dec(encval, keypair.privcipher)
print(val, outval)
if val == eval(outval):
return(val)
else:
return False
if __name__ == '__main__':
test()
[UPDATE]
The Traceback is as follows:
[guha#katana stat]$ python statenc.py
<class '__main__.statEnc'>
Traceback (most recent call last):
File "statenc.py", line 124, in <module>
test()
File "statenc.py", line 115, in test
encval = keypair.end2enc(val, keypair.pubcipher)
File "statenc.py", line 100, in end2enc
encval = aesenc(val)
NameError: name 'aesenc' is not defined
Slept over my question and woke up with a fresh mind, and voila! the answer presented itself.
The answer to the second question is as follows:
2. putting a simple 'self.' solves the problem - i was calling 'aesenc(params)' instead of 'self.aesenc(params)'. Stupid of me, really.
Question 1 is answered in this SO question.

Resources