I'm trying to create encrypt & decrypt functions using pyDES module.
Problem:
if I print the cipher text on the screen then copy it (CTRL+C) then put it as input for the decrypt() function I get the following exception:
ValueError: Invalid data length, data must be a multiple of 8 bytes
my code:
key = "12345678"
charset = "utf-8"
def encrypt():
msg = input("Enter message: ")
data = des(key, CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
cipherText = data.encrypt(msg.encode(charset))
print(cipherText)
def decrypt():
cipherText = input("Enter cipher text: ")
data = des(key, CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
decrypted = data.decrypt(cipherText).decode(charset)
print(decrypted)
While passing the cipherText directly to decrypt message (not by copying it manually from the screen) it works perfectly! like this:
key = "12345678"
charset = "utf-8"
msg = input("Enter message: ")
data = des(key, CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
cipherText = data.encrypt(msg.encode(charset))
print(cipherText)
decrypted = data.decrypt(cipherText).decode(charset)
print(decrypted)
fix/explanation would be much appreciated.
Related
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'm working on implementing a public key encryption from PyCryptodome on Python 3.6. When I try to create a symmetric encryption key and encrypt/decrypt variables, it all works fine. But the minute I introduce RSA (and PKCS1_OAEP), it all goes down the tubes - the session_key encrypts fine but when I try and decrypt it, I get the following error:
Traceback (most recent call last):
File "enctest.py", line 109, in <module>
deckey = decrypt_val(enckey)
File "enctest.py", line 77, in decrypt_val
session_key = cipher.decrypt(ciphertext)
File "/usr/lib/python3.6/site-packages/Crypto/Cipher/PKCS1_OAEP.py", line 187, in decrypt
modBits = Crypto.Util.number.size(self._key.n)
AttributeError: 'bytes' object has no attribute 'n'
My code is as follows. Can anyone take a look and tell me what I'm doing wrong?
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto import Random
from Crypto.Random import get_random_bytes
random_generator = Random.new().read
keys = RSA.generate(1024, random_generator)
pubkey = keys.publickey()
privkey = keys.exportKey()
pubcipher = PKCS1_OAEP.new(pubkey) # ciphertext = cipher.encrypt(message)
privcipher = PKCS1_OAEP.new(privkey) # message = cipher.decrypt(ciphertext)
privkeystr = keys.exportKey(format='PEM', passphrase=None, pkcs=1)
pubkeystr = keys.publickey().exportKey(format='PEM', passphrase=None, pkcs=1)
def encrypt_val(session_key, cipher = pubcipher):
try:
session_key = session_key.encode('utf8')
except:
pass
ciphertext = cipher.encrypt(session_key)
print("encrypted key : %s \n" % ciphertext)
return ciphertext
def decrypt_val(ciphertext, cipher = privcipher):
session_key = cipher.decrypt(ciphertext)
try:
session_key = session_key.decode('utf8')
except:
pass
return session_key
def aesenc(data):
try:
data = data.encode('utf8')
except:
pass
key = get_random_bytes(16)
cipher = AES.new(key, AES.MODE_EAX)
ciphertext, tag = cipher.encrypt_and_digest(data)
aesencdict = {'aesdict' : {'ciphertext' : ciphertext, 'tag' : tag, 'nonce' : cipher.nonce} , 'key' : key}
return(aesencdict)
def aesdec(aesdict, key):
cipher = AES.new(key, AES.MODE_EAX, aesdict['nonce'])
data = cipher.decrypt_and_verify(aesdict['ciphertext'], aesdict['tag'])
try:
data = data.decode('utf8')
except:
pass
return data
val = "hello"
encval = aesenc(val)
enckey = encrypt_val(encval['key'])
print(enckey)
deckey = decrypt_val(enckey)
print(deckey)
if deckey == encval['key']:
outval = aesdec(encval['aesdict'], encval['key'])
print(val, outval)
else:
print("oops\n")
It seems you do a spurious export, which translates a key into the encoding of a key:
privkey = keys.exportKey()
....
privcipher = PKCS1_OAEP.new(privkey) # message = cipher.decrypt(ciphertext)
after which it tries to find the modulus n from the encoded key instead of from the object instance that contains a member n.
Try:
privcipher = PKCS1_OAEP.new(keys)
instead.
I been trying to create a Caesar cypher program with the message 'my secret' and key 6 - all letters are shifted 6 positions to the right in the cypher alphabet, with letters “wrapping around” when falling off the end. Using the cypher, the message “my secret” would be encoded as “gsumzxlzn”. However, I keep the wrong encoded and decoded results. It's comes out as:
Encoded: sdfykixkz
Decoded: abcdefghi
please help!
import sys
ALPHABET= ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',' ']
def main(plaintext, key):
cypher = generate_cypher(key)
plaintext = sys.argv[1]
e_code = encode_message(plaintext, cypher)
mes s= decode_message(e_code, cypher)
def generate_cypher(key):
cipher = []
for i in range(len(ALPHABET)):
cipher.append(ALPHABET[i+key%27])
print(cipher)
return(cipher)
def encode_message(plaintext,cipher):
key = int(sys.argv[2])
en_code=''
for c in plaintext:
if c in cipher:
en_code+=cipher[(cipher.index(c)+key)%(len(cipher))]
print("Encoded: ", en_code)
return en_code
def decode_message(e_code,cipher):
key = int(sys.argv[2])
message = []
for i in range(len(e_code)):
message.append(cipher[i-key%27])
mess=''.join(message)
print("Decoded: ", mess)
return mess
Having a bit of trouble getting a AES cipher text to decrypt.
In this particular scenario, I am encrypting data on the client side with Crypto-JS and decrypting it back on a python server with PyCrypto.
encrypt.js:
var password = 'BJhtfRjKnTDTtPXUBnErKDxfkiMCOLyP';
var data = 'mytext';
var masterKey = CryptoJS.SHA256(password).toString();
// Derive keys for AES and HMAC
var length = masterKey.toString().length / 2
var encryptionKey = masterKey.substr(0, length);
var hmacKey = masterKey.substr(length);
var iv = CryptoJS.lib.WordArray.random(64/8);
var encrypted = CryptoJS.AES.encrypt(
data,
encryptionKey,
{
iv: iv,
mode: CryptoJS.mode.CFB
}
);
var concat = iv + encrypted;
// Calculate HMAC using iv and cipher text
var hash = CryptoJS.HmacSHA256(concat, hmacKey);
// Put it all together
var registrationKey = iv + encrypted + hash;
// Encode in Base64
var basemessage = btoa(registrationKey);
decrypt.py:
class AESCipher:
def __init__(self, key):
key_hash = SHA256.new(key).hexdigest()
# Derive keys
encryption_key = key_hash[:len(key_hash)/2]
self.key = encryption_key
self.hmac_key = key_hash[len(key_hash)/2:]
def verify_hmac(self, input_cipher, hmac_key):
# Calculate hash using inputted key
new_hash = HMAC.new(hmac_key, digestmod=SHA256)
new_hash.update(input_cipher)
digest = new_hash.hexdigest()
# Calculate hash using derived key from local password
local_hash = HMAC.new(self.hmac_key, digestmod=SHA256)
local_hash.update(input_cipher)
local_digest = local_hash.hexdigest()
return True if digest == local_digest else False
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:16]
hmac = enc[60:]
cipher_text = enc[16:60]
# Verify HMAC using concatenation of iv + cipher like in js
verified_hmac = self.verify_hmac((iv+cipher_text), self.hmac_key)
if verified_hmac:
cipher = AES.new(self.key, AES.MODE_CFB, iv)
return cipher.decrypt(cipher_text)
password = 'BJhtfRjKnTDTtPXUBnErKDxfkiMCOLyP'
input = 'long base64 registrationKey...'
cipher = AESCipher(password)
decrypted = cipher.decrypt(input)
I'm successful in re-calculating the HMAC but when I try and then decrypt the cipher I get something that seems encrypted with �'s in the result.
I was getting errors about input length of cipher text but when I switched to CFB mode that fixed it so I don't think it's a padding issue.
There are many problems with your code.
Client (JavaScript):
AES has a block size of 128 bit and CFB mode expects a full block for the IV. Use
var iv = CryptoJS.lib.WordArray.random(128/8);
The iv and hash variables are WordArray objects, but encrypted is not. When you force them to be converted to strings by concatenating them (+), iv and hash are Hex-encoded, but encrypted is formatted in an OpenSSL compatible format and Base64-encoded. You need to access the ciphertext property to get the encrypted WordArray:
var concat = iv + encrypted.ciphertext;
and
var registrationKey = iv + encrypted.ciphertext + hash;
registrationKey is hex-encoded. There is no need to encode it again with Base64 and bloat it even more:
var basemessage = registrationKey;
If you want to convert the hex encoded registrationKey to base64 encoding, use:
var basemessage = CryptoJS.enc.Hex.parse(registrationKey).toString(CryptoJS.enc.Base64);
concat is a hex-encoded string of the IV and ciphertext, because you forced the stringification by "adding" (+) iv and encrypted. The HmacSHA256() function takes either a WordArray object or a string. When you pass a string in, as you do, it will assume that the data is UTF-8 encoded and try to decode it as UTF-8. You need to parse the data yourself into a WordArray:
var hash = CryptoJS.HmacSHA256(CryptoJS.enc.Hex.parse(concat), hmacKey);
The CryptoJS.AES.encrypt() and CryptoJS.HmacSHA256() expect the key either as a WordArray object or as a string. As before, if the key is supplied as a string, a UTF-8 encoding is assumed which is not the case here. You better parse the strings into WordArrays yourself:
var encryptionKey = CryptoJS.enc.Hex.parse(masterKey.substr(0, length));
var hmacKey = CryptoJS.enc.Hex.parse(masterKey.substr(length));
Server (Python):
You're not verifying anything in verify_hmac(). You hash the same data with the same key twice. What you need to do is hash the IV+ciphertext and compare the result with the hash (called tag or HMAC-tag) that you slice off the full ciphertext.
def verify_hmac(self, input_cipher, mac):
# Calculate hash using derived key from local password
local_hash = HMAC.new(self.hmac_key, digestmod=SHA256)
local_hash.update(input_cipher)
local_digest = local_hash.digest()
return mac == local_digest
And later in decrypt():
verified_hmac = self.verify_hmac((iv+cipher_text), hmac)
You need to correctly slice off the MAC. The 60 that is hardcoded is a bad idea. Since you're using SHA-256 the MAC is 32 bytes long, so you do this
hmac = enc[-32:]
cipher_text = enc[16:-32]
The CFB mode is actually a set of similar modes. The actual mode is determined by the segment size. CryptoJS only supports segments of 128 bit. So you need tell pycrypto to use the same mode as in CryptoJS:
cipher = AES.new(self.key, AES.MODE_CFB, iv, segment_size=128)
If you want to use CFB mode with a segment size of 8 bit (default of pycrypto), you can use a modified version of CFB in CryptoJS from my project: Extension for CryptoJS
Full client code:
var password = 'BJhtfRjKnTDTtPXUBnErKDxfkiMCOLyP';
var data = 'mytext';
var masterKey = CryptoJS.SHA256(password).toString();
var length = masterKey.length / 2
var encryptionKey = CryptoJS.enc.Hex.parse(masterKey.substr(0, length));
var hmacKey = CryptoJS.enc.Hex.parse(masterKey.substr(length));
var iv = CryptoJS.lib.WordArray.random(128/8);
var encrypted = CryptoJS.AES.encrypt(
data,
encryptionKey,
{
iv: iv,
mode: CryptoJS.mode.CFB
}
);
var concat = iv + encrypted.ciphertext;
var hash = CryptoJS.HmacSHA256(CryptoJS.enc.Hex.parse(concat), hmacKey);
var registrationKey = iv + encrypted.ciphertext + hash;
console.log(CryptoJS.enc.Hex.parse(registrationKey).toString(CryptoJS.enc.Base64));
Full server code:
from Crypto.Cipher import AES
from Crypto.Hash import HMAC, SHA256
import base64
import binascii
class AESCipher:
def __init__(self, key):
key_hash = SHA256.new(key).hexdigest()
self.hmac_key = binascii.unhexlify(key_hash[len(key_hash)/2:])
self.key = binascii.unhexlify(key_hash[:len(key_hash)/2])
def verify_hmac(self, input_cipher, mac):
local_hash = HMAC.new(self.hmac_key, digestmod=SHA256)
local_hash.update(input_cipher)
local_digest = local_hash.digest()
return SHA256.new(mac).digest() == SHA256.new(local_digest).digest() # more or less constant-time comparison
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:16]
hmac = enc[-32:]
cipher_text = enc[16:-32]
verified_hmac = self.verify_hmac((iv+cipher_text), hmac)
if verified_hmac:
cipher = AES.new(self.key, AES.MODE_CFB, iv, segment_size=128)
return cipher.decrypt(cipher_text)
else:
return 'Bad Verify'
password = 'BJhtfRjKnTDTtPXUBnErKDxfkiMCOLyP'
input = "btu0CCFbvdYV4B/j7hezAra6Q6u6KB8n5QcyA32JFLU8QRd+jLGW0GxMQsTqxaNaNkcU2I9r1ls4QUPUpaLPQg=="
obj = AESCipher(password)
decryption = obj.decrypt(input)
print 'Decrypted message:', decryption
I am currently having an issue of not being able to decrypt the text provided after encryption. It returns:
Key: ンƚ!*!゙ᆱø}Qd`Dᆱd!Þxͦ}ᄚミᄀ>'U
Unpadded Text: Hello World
Padded Text: Hello World
Salt: h5eE0b814M
Encrypted Text: WxCž~¼!Ò]Cú´=P+
Encrypted Text with Salt: h5eE0b814MWxCž~¼!Ò]Cú´=P+
Key: ンƚ!*!゙ᆱø}Qd`Dᆱd!Þxͦ}ᄚミᄀ>'U
Unencrypted Text:
Where
Unencrypted Text:
Should be "Unencypted Text: Hello World"
Two programs are used in this, one a module and a master. You must run the master to run the module. Any adivce or help would be greatly appricated as I have been stuck for a while. Thank you for your time.
Here is the code:
Master.py
import Encryption as encrypt
#Place Holder Variables
SALT_SIZE = 16
padded_text = ''
ciphertext = ''
key = ''
ciphertext_with_salt = ''
#Adjustable Variables
text = "Hello World"
iterations = 62705
salt = 'h5eE0b814M'
password = 'pause232'
encrypt.key_generation(password, salt, iterations)
encrypt.encryption(text, password, SALT_SIZE, salt, iterations)
encrypt.decryption(ciphertext_with_salt, password, SALT_SIZE, salt, iterations)
Encryption.py
import Crypto.Random
from Crypto.Cipher import AES
import hashlib
#Key Generation(Used in encyption to create cipher)
def key_generation(password, salt, iterations):
global key
assert iterations > 0
key = password + salt #Combines [password] and [salt] to create a [key]
for i in range(iterations): #Hashes the [key]
key = hashlib.sha256(key).digest() #Using Sha256 it hashes the [key] based on amount of [iterations]
print '\nKey: ' + key #Debug Print
return key
#Text padding function to set text to a incerment of SALT_SIZE
def pad_text(text, SALT_SIZE):
print '\nUnpadded Text: ' + text #Debug Print
global padded_text
extra_bytes = len(text) % SALT_SIZE #Using the length of [text] it counts how many more characters is required to make an incerment of [SALT_SIZE]
pad_size = SALT_SIZE - extra_bytes #Subtracts the needed bytes from the [SALT_SIZE] and sets [pad_size] as the length of pading needed.
pad = chr(pad_size) * pad_size #Creates padding for the [text]
padded_text = text + pad #Adds the [pad] to the [text]
print '\nPadded Text: ' + padded_text #Debug Print
#Primary Encryption Function(using text and password)
def encryption(text, password, SALT_SIZE, salt, iterations):
global padded_text
global ciphertext
cipher = AES.new(key, AES.MODE_ECB)
padded_plaintext = pad_text(text, SALT_SIZE)
ciphertext = cipher.encrypt(padded_text)
ciphertext_with_salt = salt + ciphertext
#debug script
print '\nSalt: ' + salt #Debug Print
print '\nEncrypted Text: ' + ciphertext #Debug Print
print '\nEncrypted Text with Salt: ' + ciphertext_with_salt #Debug Print
return ciphertext_with_salt
#Primary Decryption Function(using the encrypted text and password)
def decryption(ciphertext_with_salt, password, SALT_SIZE, salt, iterations):
ciphertext_with_salt = ciphertext[SALT_SIZE:]
key = key_generation(password, salt, iterations)
cipher = AES.new(key, AES.MODE_ECB)
unencrypted_text = cipher.decrypt(ciphertext_with_salt)
print '\nUnencrypted Text: ' + unencrypted_text #Debug Print
return unencrypted_text
#Code to allow to use as outside module
if __name__ == '__main__':
key_generation(password, salt, iterations)
encryption(text, password, SALT_SIZE, salt, iterations)
decryption(ciphertext_with_salt, password, SALT_SIZE, salt, iterations)
You are transferring your ciphertext and key as strings. You should make sure that the bytes that make up the ciphertext and keys stay intact. If you want to transport them using character strings, please use either hexadecimal encoding or base 64. Hexadecimals are easier to read and check for length, base 64 is more efficient.
Note that the ciphertext will contain bytes with any value, including ones that are ASCII control characters (< 0x20) which also contain termination characters such as 0x00.