I've generated a public and private key with pycrypto, and I save them to a file using export key:
from Crypto.PublicKey import RSA
bits=2048
new_key = RSA.generate(bits, e=65537)
prv = open('keymac.pem','w')
prv.write(new_key.exportKey('PEM'))
prv.close()
pub = open('pubmac.pem', 'w')
pub.write(new_key.publickey().exportKey('PEM'))
pub.close()
I use the public key to encrypt a file (following http://insiderattack.blogspot.com/2014/07/encrypted-file-transfer-utility-in.html#comment-form)
When I read the file to decrypt it, I get "Ciphertext with incorrect length."
I added a try-except block around the decryption code on Deepal Jayasekara example:
try:
encryptedonetimekey = filetodecrypt.read(512)
privatekey = open("keymac.pem", 'r').read()
rsaofprivatekey = RSA.importKey(privatekey)
pkcs1ofprivatekey = PKCS1_OAEP.new(rsaofprivatekey)
aesonetimekey = pkcs1ofprivatekey.decrypt(encryptedonetimekey)
except Exception as decrypprivkeyerr:
print "Decryption of the one time key using the private key failed!!"
print "Key error == %s" %decrypprivkeyerr
raise Exception("Decryption using Private key failed error = %s" %decrypprivkeyerr)
Am I missing something? Should I save the private key differently? Am I not reading the private key correctly?
The error message, "Ciphertext with incorrect length", has told us all. That means, cipher text exceeded the limit length which can be calculated by (length of key, 1024.2048..)/8. to solve this problem, you can separate the cipher text and decrypt them within a loop, then assemble all the decrypted byte string. My code in Python 3.6 for reference:
# 1024/8
default_length = 128
encrypt_str = str(data["content"])
sign_str = str(data["sign"])
try:
rsa_private_key = RSA.importKey(private_key)
encrypt_byte = base64.b64decode(encrypt_str.encode())
length = len(encrypt_byte)
cipher = PKCS115_Cipher(rsa_private_key)
if length < default_length:
decrypt_byte = cipher.decrypt(encrypt_byte, 'failure')
else:
offset = 0
res = []
while length - offset > 0:
if length - offset > default_length:
res.append(cipher.decrypt(encrypt_byte[offset: offset +
default_length], 'failure'))
else:
res.append(cipher.decrypt(encrypt_byte[offset:], 'failure'))
offset += default_length
decrypt_byte = b''.join(res)
decrypted = decrypt_byte.decode()
This doesnt answer your question directly but it may give you some clues to the problem. Im using two functions for encrypting content to a file rather than encrypting a file directly. One for encrypting (in my case username and password) to a file then another to decrypt that data to use as needed.
Note the need for the padding
Creat Encrypted Content In File:
from Crypto.Cipher import AES
import base64
import os
import argparse
parser = argparse.ArgumentParser(description='Arguments used to generate new credentials file, Use: -u for username, -p for password')
parser.add_argument('-u', help='Specify username', required=True)
parser.add_argument('-p', help='Specify password', required=True)
parser.add_argument('-b', help='Specify debug', required=False, action='store_true')
args = vars(parser.parse_args())
def encrypt(username, password):
#Encrypt Credentials To '.creds' file, including 'secret' for username and password
dir_path = os.path.dirname(os.path.realpath(__file__))
# the block size for the cipher object; must be 16 per FIPS-197
BLOCK_SIZE = 16
# the character used for padding--with a block cipher such as AES, the value
# you encrypt must be a multiple of BLOCK_SIZE in length. This character is
# used to ensure that your value is always a multiple of BLOCK_SIZE
PADDING = '{'
# one-liner to sufficiently pad the text to be encrypted
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
# generate a random secret key
user_secret = os.urandom(BLOCK_SIZE)
pass_secret = os.urandom(BLOCK_SIZE)
# one-liners to encrypt/encode and decrypt/decode a string
# encrypt with AES, encode with base64
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
# create a cipher object using the random secret
user_cipher = AES.new(user_secret)
pass_cipher = AES.new(pass_secret)
# encode a string
user_encoded = EncodeAES(user_cipher, username)
pass_encoded = EncodeAES(pass_cipher, password)
try:
with open('.creds', 'w') as filename:
filename.write(user_encoded + '\n')
filename.write(user_secret + '\n')
filename.write(pass_encoded + '\n')
filename.write(pass_secret + '\n')
filename.close()
print '\nFile Written To: ', dir_path + '/.creds'
except Exception, e:
print e
if args['b']:
print((user_encoded, user_secret), (pass_encoded, pass_secret))
username = args['u']
password = args['p']
encrypt(username, password)
Decrypt The Data
def decrypt(dir_path, filename):
#Read '.creds' file and return unencrypted credentials (user_decoded, pass_decoded)
lines = [line.rstrip('\n') for line in open(dir_path + filename)]
user_encoded = lines[0]
user_secret = lines[1]
pass_encoded = lines[2]
pass_secret = lines[3]
# the character used for padding--with a block cipher such as AES, the value
# you encrypt must be a multiple of BLOCK_SIZE in length. This character is
# used to ensure that your value is always a multiple of BLOCK_SIZE
PADDING = '{'
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
# create a cipher object using the random secret
user_cipher = AES.new(user_secret)
pass_cipher = AES.new(pass_secret)
# decode the encoded string
user_decoded = DecodeAES(user_cipher, user_encoded)
pass_decoded = DecodeAES(pass_cipher, pass_encoded)
return (user_decoded, pass_decoded)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With