So I am trying to make two programs, one to encode a message using rsa encryption, and one to decode it. In the encryption file I have:
import rsa
def generateKeys():
(publicKey, privateKey) = rsa.newkeys(1024)
with open('keys/publicKey.pem', 'wb') as p:
p.write(publicKey.save_pkcs1('PEM'))
with open('keys/privateKey.pem', 'wb') as p:
p.write(privateKey.save_pkcs1('PEM'))
def loadKeys():
with open('keys/publicKey.pem', 'rb') as p:
publicKey = rsa.PublicKey.load_pkcs1(p.read())
with open('keys/privateKey.pem', 'rb') as p:
privateKey = rsa.PrivateKey.load_pkcs1(p.read())
return privateKey, publicKey
def encrypt(message, key):
return rsa.encrypt(message.encode('ascii'), key)
def sign(message, key):
return rsa.sign(message.encode('ascii'), key, 'SHA-1')
#generateKeys()
privateKey, publicKey = loadKeys()
print(f"public key: {publicKey}, private key: {privateKey}")
encryptme = input('Write your message here:')
ciphertext = encrypt(encryptme, publicKey)
signature = sign(encryptme, privateKey)
print(str(ciphertext))
#print(signature)
and in the decryption file I have:
import rsa
def loadKeys():
with open('keys/publicKey.pem', 'rb') as p:
publicKey = rsa.PublicKey.load_pkcs1(p.read())
with open('keys/privateKey.pem', 'rb') as p:
privateKey = rsa.PrivateKey.load_pkcs1(p.read())
return privateKey, publicKey
def decrypt(ciphertext, key):
try:
print(ciphertext)
return rsa.decrypt(ciphertext, key).decode('ascii')
except:
return False
def verify(message, signature, key):
try:
return rsa.verify(message, signature, key, ) == 'SHA-1'
except:
return False
privateKey, publicKey = loadKeys()
ciphertext = input("message to decipher: ")
print(ciphertext)
text = decrypt(ciphertext, privateKey)
if text:
print(f'Message text: {text}')
else:
print(f'Unable to decrypt the message.')
whenever I encode text and then paste it into the input of the decryption program, it returns cannot decode message. If anyone knows why this is, I would love some help. Thanks!
The byte string ciphertext is converted to a character string by str() or at the latest by input().
Example: The 2 byte string b'\x11\xed' is converted by str() or by input() into an 11 bytes character string like a UTF8 encoding reveals: b"b'\\x11\\xed'".
This character string must be converted back to the original byte string before decryption, which does not happen (or happens incorrectly) in the posted implementation (s. also the comments).
The problem can be easily solved if the ciphertext is Base64 encoded. This string can then be passed to input() via copy/paste and Base64 decoded before decryption. If it is necessary to store the ciphertext, it is recommended to store the raw ciphertext because of the Base64 overhead.
The following code implements encryption and decryption using Base64 encoding/decoding:
import rsa
import base64
def generateKeys():
return rsa.newkeys(1024)
def encrypt(message, key):
return rsa.encrypt(message.encode('ascii'), key)
def decrypt(ciphertext, key):
try:
return rsa.decrypt(ciphertext, key).decode('ascii')
except:
return False
(publicKey, privateKey) = generateKeys()
encryptme = 'The quick brown fox jumps over the lazy dog'
# Encryption
ciphertext = encrypt(encryptme, publicKey) # store raw ciphertext in file system
ciphertextB64 = base64.b64encode(ciphertext).decode('utf8')
print(ciphertextB64)
ciphertextB64Input = input("message to decipher: ") # Enter the ciphertext with copy/paste
decrypted = decrypt(base64.b64decode(ciphertextB64Input), privateKey)
print(decrypted)
The same applies to signing:
def sign(message, key):
return rsa.sign(message.encode('ascii'), key, 'SHA-1')
def verify(message, signature, key):
try:
return rsa.verify(message.encode('ascii'), signature, key) == 'SHA-1'
except:
return False
# Signing
signme = 'The quick brown fox jumps over the lazy dog'
signature = sign(signme, privateKey) # store raw signature in file system
signatureB64 = base64.b64encode(signature).decode('utf8')
print(signatureB64)
signatureB64Input = input("signature to verify: ")
verified = verify(signme, base64.b64decode(signatureB64Input), publicKey)
print(verified)
Note that there is another bug here: in rsa.verify() message must be replaced by message.encode('ascii').
Related
in python I implementing AES Encrypting
that encrypt string or binary string with AES-128 and encode the output to regular hex literals like \xhh I want to implementing this function with lua
class AESCipher(object):
def __init__(self, key):
self.bs = 16
self.key = key
def encrypt(self, raw, use_base64=True):
if Crypto:
raw = self._pad(raw)
cipher = AES.new(self.key, mode=AES.MODE_ECB)
crypted_text = cipher.encrypt(raw)
else:
_ = self._pad(raw)
cipher = pyaes.blockfeeder.Encrypter(
pyaes.AESModeOfOperationECB(self.key)) # no IV, auto pads to 16
crypted_text = cipher.feed(raw)
crypted_text += cipher.feed() # flush final block
if use_base64:
return base64.b64encode(crypted_text)
else:
return crypted_text
def decrypt(self, enc, use_base64=True):
if use_base64:
enc = base64.b64decode(enc)
if Crypto:
cipher = AES.new(self.key, AES.MODE_ECB)
raw = cipher.decrypt(enc)
return self._unpad(raw).decode('utf-8')
else:
cipher = pyaes.blockfeeder.Decrypter(
pyaes.AESModeOfOperationECB(self.key)) # no IV, auto pads to 16
plain_text = cipher.feed(enc)
plain_text += cipher.feed() # flush final block
return plain_text
def _pad(self, s):
padnum = self.bs - len(s) % self.bs
return s + padnum * chr(padnum).encode()
#staticmethod
def _unpad(s):
return s[:-ord(s[len(s)-1:])]
localkey = "30a91c993c721aa9"
localkey = localkey.encode('latin1')
print(localkey)
test_cipher = AESCipher(localkey)
payload = b'{"gwId":"315716865002915c2717","devId":"315716865002915c2717","uid":"315716865002915c2717","t":"1633628358"}'
#payload = payload.encode("utf-8")
encrypted = test_cipher.encrypt(payload,False)
print(encrypted)
The Output is:
b'30a91c993c721aa9'
b'\xe15\xf8\xc3\xc1)eW\x19\x0b/\x8d\xaf\xa8\xe0\xb6\xd4\xb7\xf7\xcf\xe4\x02\xa4\xab\xfft\x00]B\xea\xf5\r\xcbI\xef\xf9\xd8\xd6v\x02\xa6&\x0e\xe7\x19\x14\xde\xa3\xd0\xc4\xfb\xcb\xfa\x8c\xb8\x9fd\xa6v#\xdf\x03\xc7\xb8\xe5fe\xaf_\xdc\x931\xff\xfc\xfc\x85\x0b3\x92w\xe7k\x1c~}\xb2"\xe9\xc1\xe0\x9a\x12\xb3\xcak\x8en<t\xdd\x12n9Q\x8eX!\xaba2\x16\x9c'
How can i implemneting this encrypting function in lua with this encoding
i tried some ways and many libraries but the output encoding is not like above
what can i do?
I'm really not sure what the problem is. I've tried encoding the cyphertext in the decrypt() method before actually decrypting it, as well. Instead of displaying the proper plaintext message, it displays a garbled mess of characters different from the original cyphertext.
This is all embedded in a Flask app web API, which is why I need to decode the bytes:
########CALLING DECRYPT FROM HERE########
def get(self):
parser = reqparse.RequestParser()
parser.add_argument('mode', required= True)
parser.add_argument('key', required= False)
parser.add_argument('text', required= False)
args = parser.parse_args() # parse args to dict
mode = args['mode']
if mode == 'keygen':
return self.keygen()
elif mode == 'encrypt' and args['key'] != None and args['text'] != None:
return self.encrypt(args['key'], args['text'])
elif mode == 'decrypt' and args['key'] != None and args['text'] != None:
return self.decrypt(args['key'], args['text'])
###########################################
def keygen(self):
key = urandom(16)
dkey = b16encode(key).decode('utf-8')
return {'key': dkey} # return cryptographic key
def encrypt(self, key, raw):
crypt = AES.new(key, AES.MODE_CFB, iv)
ciphertext = crypt.encrypt(raw) # type() yields 'bytes'
# have to decode, flask wont allow bytes in json
ciph = b16encode(ciphertext).decode('utf-8')
return {'secret': ciph}
def decrypt(self, key, cipher):
# TODO not decrypting properly
# have a suspicion that the failed decryption has to do with cipher being a str input
crypt = AES.new(key, AES.MODE_CFB, iv)
plaintext = crypt.decrypt(cipher)
raw = b16encode(plaintext).decode('utf-8')
return {'message': raw}
Any help would be appreciated! I'm not sure if I'm making a stupid mistake here.
I need to sent my friend Bob the number 42, my code will use a pre-shared key generate a random number to encrypt the message (42) and decrypt it on Bobs end. the only problem is, I have no clue how to generate a repeatable encryption key in python.
You can use Crypto to encrypt a message using AES.
import hashlib, base64
from Crypto import Random
from Crypto.Cipher import AES
class AESCipher():
def __init__(self, key):
self.private_key = hashlib.sha256(key.encode()).digest()
self.bs = AES.block_size
def encrypt(self, data):
# generate public key
public_key = Random.new().read(self.bs)
# setup AES Cipher using public key and private key
cipher = AES.new(self.private_key, AES.MODE_CBC, public_key)
# enrpyt the data and convert to base64
return base64.b64encode(public_key + cipher.encrypt(self.pad(data).encode()))
def decrypt(self, enc):
# convert encrypted data to base 64
enc = base64.b64decode(enc)
# get public key
public_key = enc[:AES.block_size]
# setup AES Cipher using public and private key
cipher = AES.new(self.private_key, AES.MODE_CBC, public_key)
# decrypt data using the public key
return self.unpad(cipher.decrypt(enc[AES.block_size:])).decode("utf-8")
def pad(self, s):
# pads data so that it's a multiple of 16
return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)
def unpad(self, s):
# removes padding
return s[:-ord(s[len(s)-1:])]
cipher = AESCipher("your secret key")
Here I encrypt some text, the public key with the encrypted text is joined and returned from the encrypt method, you can then send the returned text to Bob, to decrypt the text bob then just needs to run the decrypt method.
>>> cipher.encrypt("your message")
b'HYfUkcd//CaRquG9AhReR8bJYdVQdcGWRAjcp9AstLs='
>>> output = cipher.encrypt("your message")
>>> output
b'RVTK7L7ZDw9DzvuXuj8zYPZJjBO/A0N3l5N1hp9LY6U='
>>> cipher.decrypt(output)
'your message'
>>>
I have this Python method on the server to encrypt a string into bytes (AES/CBC).
class AESCipher(object, key):
def __init__(self, key):
self.bs = AES.block_size
self.key = hashlib.sha256(key.encode()).digest()
def encrypt(self, raw):
raw = self._pad(raw)
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw.encode()))
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')
def _pad(self, s):
return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)
The output of encrypt() is in bytes like this: b'PMgMOkBkciIKfWy/DfntVMyAcKtVsM8LwEwnTYE5IXY='
I would like to store this into database, and send it as string via API to Kotlin. And in there I would like to decrypt it via the same shared secret key.
In what format do I save the bytes above into database?
Once arrived in Kotlin client, how do I convert that string into ByteArray?
My theory is that I have to store the bytes as base64 string in the database.
And on the other side I have to decode the string as base64 into bytes. Is this approach correct? Will the encryption/decryption work like this end-to-end with the code below?
fun decrypt(context:Context, dataToDecrypt: ByteArray): ByteArray {
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
val ivSpec = IvParameterSpec(getSavedInitializationVector(context))
cipher.init(Cipher.DECRYPT_MODE, getSavedSecretKey(context), ivSpec)
val cipherText = cipher.doFinal(dataToDecrypt)
val sb = StringBuilder()
for (b in cipherText) {
sb.append(b.toChar())
}
return cipherText
}
fun getSavedSecretKey(context: Context): SecretKey {
val sharedPref = PreferenceManager.getDefaultSharedPreferences(context)
val strSecretKey = sharedPref.getString("secret_key", "")
val bytes = android.util.Base64.decode(strSecretKey, android.util.Base64.DEFAULT)
val ois = ObjectInputStream(ByteArrayInputStream(bytes))
val secretKey = ois.readObject() as SecretKey
return secretKey
}
fun getSavedInitializationVector(context: Context) : ByteArray {
val sharedPref = PreferenceManager.getDefaultSharedPreferences(context)
val strInitializationVector = sharedPref.getString("initialization_vector", "")
val bytes = android.util.Base64.decode(strInitializationVector, android.util.Base64.DEFAULT)
val ois = ObjectInputStream(ByteArrayInputStream(bytes))
val initializationVector = ois.readObject() as ByteArray
return initializationVector
}
UPDATE
I have tried to remove the Base64 to remove the memory overhead as suggested.
Python:
def encrypt(self, raw):
raw = self._pad(raw)
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return iv + cipher.encrypt(raw.encode())
So this is no longer possible.
enc = AESCipher('abc').encrypt("myLife")
value_to_save_in_db = enc.decode("utf8")
So I need to find a way to store the byte array directly in the database. I think I should be able to do this as blob. But some challenges remain as how to send the bytearray as part of JSON over the API to the android device. I think I have to convert it to Base64 string again. Not sure if I have gained anything in that case...
The following Kotlin code:
val decrypted = decrypt("blEOKMQtUbNOzJbvEkL2gNhjF+qQ/ZK84f2ADu8xyUFme6uBhNYqvEherF/RRO9YRImz5Y04/ll+T07kqv+ExQ==");
println(decrypted);
decrypts a ciphertext of the Python code. Here decrypt() is:
fun decrypt(dataToDecryptB64 : String) : String {
// Base64 decode Python data
val dataToDecrypt = Base64.getDecoder().decode(dataToDecryptB64)
// Separate IV and Ciphertext
val ivBytes = ByteArray(16)
val cipherBytes = ByteArray(dataToDecrypt.size - ivBytes.size)
System.arraycopy(dataToDecrypt, 0, ivBytes, 0, ivBytes.size)
System.arraycopy(dataToDecrypt, ivBytes.size, cipherBytes, 0, cipherBytes.size)
// Derive key
val keyBytes = MessageDigest.getInstance("SHA256").digest("abc".toByteArray(Charsets.UTF_8))
// Decrypt
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(keyBytes, "AES"), IvParameterSpec(ivBytes))
val cipherText = cipher.doFinal(cipherBytes)
return String(cipherText, Charsets.ISO_8859_1)
}
For this, the ciphertext was generated using the posted Python class AESCipher as follows:
plaintext = 'The quick brown fox jumps over the lazy dog'
cipher = AESCipher('abc')
ciphertext = cipher.encrypt(plaintext)
print(ciphertext.decode('utf8')) # Base64 string, which can be stored e.g. in a DB
I applied the originally posted Python implementation that derives the key using SHA256. However, if the key is derived from a password, for security reasons not SHA256 but a reliable key derivation function, e.g. Argon2 or PBKDF2, should be used.
The Kotlin code first Base64 decodes the Python data and then separates IV and the actual ciphertext. Then, the key is derived by generating the SHA256 hash of the password. Finally the data is decrypted.
The current Python code Base64 encodes the data so that it can be stored as a string in the DB. Alternatively, the Python code could be modified so that no Base64 encoding is performed, and the raw data can be stored (which requires less memory, Base64 overhead: 33%).
Depending on the solution chosen, the Kotlin code may or may not need to Base64 decode the data.
I am using following implementation of AES cipher :-
import hashlib
from Crypto.Cipher import AES
class AESCipher:
def __init__(self, key):
self.BS = 128
try:
self.key = hashlib.sha256(key.encode()).digest()[:self.BS]
except:
self.key = hashlib.sha256(key).digest()[:self.BS]
self.iv = Random.new().read(AES.block_size)
def encrypt(self, raw):
raw = self._pad(raw)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
return base64.b64encode(self.iv + cipher.encrypt(raw))
def decrypt(self, enc):
enc = base64.b64decode(enc)
self.iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode()
def _pad(self, s):
return s + (self.BS - len(s) % self.BS) * chr(self.BS - len(s) % self.BS).encode()
#staticmethod
def _unpad(s):
return s[:-ord(s[len(s)-1:])]
Encryption for a binary encoded dictionary object causes no errors but when I try to decrypt the same encrypted object, following exception is raised :-
return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode()
builtins.UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte
I tried to use 'ISO' and 'latin' encoding and decoding functions. But after that the socket on other side of the LAN recognizes it as a string and not as a dictionary object.
My question :- What I am doing wrong here ?
Additional information :-
key = 'SHSJDS-DSJBSJDS-DSKNDS' # some thing following this pattern
bin_json_object = pickle.dumps(dict_object)
enc_json_object = AESenc(bin_json_object, key)
def AESenc(self, data, key):
return AESCipher(key).encrypt(data)
def AESdec(self, data, key):
return AESCipher(key).decrypt(data)
For example If I use "ISO-8859-1" encoding in the above code :-
binary encoded representation of dictionary object :-
b'\x80\x03}q\x00(X\x02\x00\x00\x00idq\x01X$\x00\x00\x0096e09f6c-1e80-4cd1-9225-159e35bcacb4q\x02X\x0c\x00\x00\x00request_codeq\x03K\x01X\x0e\x00\x00\x00payload_lengthq\x04K!X\x0b\x00\x00\x00session_keyq\x05Nu.'
encrypted representation of binary encoded dictionary object :-
b'cZi+L4Wi51B5oDGQKlFb9bioxKH3TFRO1piECklafwTe6GYm/VeVjJaCDKiI+o6f6CcUnMvx+2EfEwcHCH/KDDeHTivIUou7WGVrd1P++HxfYNutY/aOn30Y/yiICvwWRHBn/3zU3xXvr/4XrtoVddM2cQEgXupIcC99TIxurrr8CCZd74ZnWj6QB8quCtHD'
But if I now try to decrypt the same on other node on same LAN via socket. I get following decrypted representation :-
}q(XidqX$96e09f6c-1e80-4cd1-9225-159e35bcacb4qX
request_codeqKXpayload_lengthqK!X
session_keyqNu.
which is completely different from original binary representation of the same dictionary object. And produces the following exception :-
data = pickle.loads(data)
builtins.TypeError: 'str' does not support the buffer interface
Finally after hours of debugging I came with a working code, but I am not able to understand, why this is working. Please if someone could explain this in comments.
Modified version AES cipher code :-
class AESCipher:
def __init__(self, key):
self.BS = AES.block_size
try:
self.key = hashlib.sha256(key.encode('ISO-8859-1')).digest()[:self.BS]
except:
self.key = hashlib.sha256(key).digest()[:self.BS]
self.iv = Random.new().read(AES.block_size)
def encrypt(self, raw):
raw = self._pad(raw)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
return base64.b64encode(self.iv + cipher.encrypt(raw))
def decrypt(self, enc):
enc = base64.b64decode(enc)
self.iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('ISO-8859-1')
def _pad(self, s):
return s + (self.BS - len(s) % self.BS) * chr(self.BS - len(s) % self.BS).encode('ISO-8859-1')
#staticmethod
def _unpad(s):
print('returning : ', s[:-ord(s[len(s)-1:])])
return s[:-ord(s[len(s)-1:])]
Now without modifying the AES encryption and decryption functions. I introduced a following variation in the code. Whenever another node receives a binary stream it first decrypts it with the AES decrypt function. But after decryption encoded dictionary object has to be encoded again with 'ISO-8859-1' as shown below :-
dict_object = self.AESdecryption(binary_stream, self.session_key)
dict = pickle.loads(dict_object.encode('ISO-8859-1'))
print(dict)
The above produces correct dictionary object. But what I don't understand is when a dictionary object was encrypted in 'ISO-8859-1' encoding, and and then decrypted on other node in 'ISO-8859-1' encoding, then why before passing it to the pickle.loads() I have to encode it again to get the original dictionary object. Please if someone could explain why it is happening ?