urlsafe_b64decode: return binascii.a2b_base64(s) - python

I'm working on python cryptography by following tutorial.
here is the code:
import os
import base64
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
BASE_DESTINATION = os.path.dirname(os.path.abspath(__file__))
KEY_DESTINATION = os.path.join(BASE_DESTINATION, 'keys/')
def generate_key_from_password(path = KEY_DESTINATION):
passwordProvided = str(input("Enter Password: ")) # enter your password
password = passwordProvided.encode()
salt = os.urandom(16)
# salt = b'aeroplane'
kdf = PBKDF2HMAC(
algorithm= hashes.SHA256(),
length= 32,
salt= salt,
iterations= 100_000,
backend = default_backend()
)
key = base64.urlsafe_b64decode(kdf.derive(password)) # generated successed!
print("key: ", key)
generate_key_from_password()
when key is generated with base64.urlsafe_b64decode(kdf.derive(password)) I get error: binascii.Error: Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4
I visited many answers but nothing quite really fit for my code. ie: I added '=' and '==' to password string to solve padding problem and adding padding by calculation password = str(password) + ('=' * len(password)) and but it didn't work either.
Here is the full error:
Enter Password: pass
Traceback (most recent call last):
File "gfp.py", line 33, in <module>
generate_key_from_password()
File "gfp.py", line 28, in generate_key_from_password
key = base64.urlsafe_b64decode(kdf.derive(password)) # generated successed!
File "C:\Users\user\Anaconda3\envs\IRIS-REC\lib\base64.py", line 133, in urlsafe_b64decode
return b64decode(s)
File "C:\Users\user\Anaconda3\envs\IRIS-REC\lib\base64.py", line 87, in b64decode
return binascii.a2b_base64(s)
binascii.Error: Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4

The solution is provided by #Topaco in the comments. (Thank you!)
The problem was in the syntax.
it should be base64.urlsafe_b64encode(...) instead of base64.urlsafe_b64decode(kdf.derive(password)). I've seen the code multiple times but somehow I missed this everytime.
Thank you #topaco

Related

What would be an acceptable bit size for RSA encryption of an image? (PYTHON)

I'm coding a encryption module using pycryptodomex and I have run into an issue regarding the plaintext being to long for image encryption (ValueError). For my image encryption I convert the image into bytes and then encrypt the bytes. Currently I am using a key-pair with a bit size of 2048. I have tried using a key size of 4096 which takes a significantly longer amount of time to generate and use (≈100s). My question is is there a more efficient algorithms for encrypting images apart from RSA or AES (I use both however I am only getting this error whilst testing the asymmetric image encryption) and how would I encrypt images using these algorithms. I have also though of alternatives to increasing the key size these are using a if statement to prevent the user from trying to encrypt an image that is larger than the limit of encrypting an image using a 2048-bit RSA key, in this case what is the image size limit for encrypting using this kind of key? Another alternative I have though of is using compression to make the image size smaller however I would like to avoid this as this may mean loosing image quality, if nothing else works would encrypting compressed images work in the same was as encrypting standard images.
Here is the traceback for the error:
$ python -m unittest Test_TIENC_REVISED.py
EEEE....
======================================================================
ERROR: test_decrypt_failure (Test_TIENC_REVISED.TestImageEncA)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\probm\Desktop\tienc\tests\Test_TIENC_REVISED.py", line 58, in test_decrypt_failure
ciphertext = self.image_enc.encrypt(self.image_path)
File "C:\Users\probm\Desktop\tienc\tests\tienc_revised.py", line 71, in encrypt
ciphertext = cipher.encrypt(img_bytes)
File "C:\Users\probm\Desktop\tienc\venv\lib\site-packages\Cryptodome\Cipher\PKCS1_OAEP.py", line 115, in encrypt
raise ValueError("Plaintext is too long.")
ValueError: Plaintext is too long.
======================================================================
ERROR: test_encrypt_decrypt (Test_TIENC_REVISED.TestImageEncA)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\probm\Desktop\tienc\tests\Test_TIENC_REVISED.py", line 50, in test_encrypt_decrypt
ciphertext = self.image_enc.encrypt(self.image_path)
File "C:\Users\probm\Desktop\tienc\tests\tienc_revised.py", line 71, in encrypt
ciphertext = cipher.encrypt(img_bytes)
File "C:\Users\probm\Desktop\tienc\venv\lib\site-packages\Cryptodome\Cipher\PKCS1_OAEP.py", line 115, in encrypt
raise ValueError("Plaintext is too long.")
ValueError: Plaintext is too long.
Here is the code I use to test the encryption (commented out code is what I previously had):
class TestImageEncA(unittest.TestCase):
def setUp(self):
self.image_path = imgpath
self.private_key, self.public_key = tienc_revised.generate_key_a_4096() #self.private_key, self.public_key = tienc_revised.generate_key_a()
self.image_enc = tienc_revised.ImageEncA(self.public_key)
def test_encrypt_decrypt(self):
ciphertext = self.image_enc.encrypt(self.image_path)
output_path = "decrypted_image.jpg"
self.image_enc.decrypt(ciphertext, self.private_key, output_path)
with open(self.image_path, "rb") as f1, open(output_path, "rb") as f2:
self.assertEqual(f1.read(), f2.read())
def test_decrypt_failure(self):
wrong_key = RSA.generate(4096).export_key() #wrong_key = RSA.generate(2048).export_key()
ciphertext = self.image_enc.encrypt(self.image_path)
output_path = "decrypted_image.jpg"
self.assertRaises(Exception, self.image_enc.decrypt, ciphertext, wrong_key, output_path)
Here is the main code for encryption:
class ImageEncA:
""" asymmetric image encryption """
def __init__(self, public_key):
self.public_key = RSA.import_key(public_key)
def encrypt(self, image_path):
with open(image_path, "rb") as f:
img_bytes = f.read()
cipher = PKCS1_OAEP.new(self.public_key)
ciphertext = cipher.encrypt(img_bytes)
return ciphertext
def decrypt(self, ciphertext, private_key, output_path):
private_key = RSA.import_key(private_key)
cipher = PKCS1_OAEP.new(private_key)
img_bytes = cipher.decrypt(ciphertext)
with open(output_path, "wb") as f:
f.write(img_bytes)
Here is the code for generating keys:
# Asymetric key generation
def generate_key_a():
key = RSA.generate(2048)
private_key = key.export_key()
public_key = key.publickey().export_key()
return private_key, public_key
# Symetric key generation
def generate_key_s():
key = os.urandom(32)
return key
# Asymetric key generation with a larger bit size
def generate_key_a_4096():
key = RSA.generate(4096)
private_key_b = key.export_key()
public_key_b = key.publickey().export_key()
return private_key_b, public_key_b
So how would I fix this problem?

ecdsa signing key format

I am trying to create a public/private key pair using python.
I have created a private key using the following method:
private_key = ''.join(['%x' % random.randrange(16) for x in range(0, 64)])
using this private key I have attempted to use a ecdsa graph to generate to corresponding public key
def privateKeyToPublicKey(s):
sk = ecdsa.SigningKey.from_string(s, curve=ecdsa.SECP256k1)
vk = sk.verifying_key
return ('\04' + sk.verifying_key.to_string())
I have not been able to create the signing key (sk) due to a formatting error where my string is in the wrong format. But I am not sure how/what format the string s should be for SigningKey to work.
I get the following error when running the script:
Traceback (most recent call last):
File "address.py", line 23, in <module>
privateKeyToPublicKey(private_key)
File "address.py", line 20, in privateKeyToPublicKey
sk = ecdsa.SigningKey.from_string(s, curve=ecdsa.SECP256k1)
File "/usr/local/lib/python3.6/dist-packages/ecdsa/keys.py", line
149, in from_string
assert len(string) == curve.baselen, (len(string), curve.baselen)
AssertionError: (64, 32)
Here's a more complete code sample for clarity about the answer. Python3.
from ecdsa import SigningKey, SECP256k1
import sha3, random, binascii
private_key = ''.join(['%x' % random.randrange(16) for x in range(0, 64)])
private_key = bytes(private_key, 'utf-8')
private_key = binascii.unhexlify(private_key)
priv = SigningKey.from_string(private_key, curve=SECP256k1)
pub = priv.get_verifying_key().to_string()
keccak = sha3.keccak_256()
keccak.update(pub)
address = keccak.hexdigest()[24:]
print(address, priv.to_string().hex())
I have realised my error, the input must be in bytes, the private key in hex format. In python2 you may use:
private_key.decode('hex')
or you may use
binascii.unhexlify
in python3

pysodium crypto_box_open throwing ValueError

I'm using libsodium with the python bindings in pysodium 0.6.6
When using crypto_box and crypto_box_open, I always get a ValueError. Here is a simple example:
import pysodium
import random
serverPK, serverSK = pysodium.crypto_box_keypair()
clientPK, clientSK = pysodium.crypto_box_keypair()
testText = "test message"
nonce1 = str(random.random())
nonce2 = str(random.random())
cipherText = pysodium.crypto_secretbox(testText,nonce1,clientPK)
message = pysodium.crypto_secretbox_open(cipherText, nonce2, clientSK)
print message
And here is the error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/.../pysodium-0.6.6/pysodium/__init__.py", line 181, in crypto_box_open
__check(sodium.crypto_box_open(msg, padded, ctypes.c_ulonglong(len(padded)), nonce, pk, sk))
File "/Users/.../pysodium-0.6.6/pysodium/__init__.py", line 70, in __check
raise ValueError
ValueError
crypto_box_keypair() creates a key pair, to be used with crypto_box().
crypto_secretbox() is not asymmetric encryption: a single key is used both to encrypt and to decrypt. If this is actually what you want, the key can be generated that way:
key = pysodium.randombytes(pysodium.crypto_secretbox_KEYBYTES)
For a given key, the nonce has to be unique for each message, and has to be pysodium.crypto_secretbox_NONCEBYTES bytes long. It can be a simple counter, or a random nonce:
nonce = pysodium.randombytes(pysodium.crypto_secretbox_NONCEBYTES)
Unlike the secret key, a nonce can be public. But it has to be the same for encrypting and for decrypting:
cipherText = pysodium.crypto_secretbox(testText, nonce, key)
message = pysodium.crypto_secretbox_open(cipherText, nonce, key)
The libsodium documentation provides examples on how to use box and secretbox, which can be easily translated to pysodium equivalents.

PyCrypto export/import of signature

created a client-server application with sockets and I am trying to transfer the signature from client to server. I convert it from tuple to string and then back to tuple. But signing stops working. How to resolve this?
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
public_key_file = open('public.pem','r')
public_key = RSA.importKey(public_key_file.read())
signature = "(90392831408741910958006452852395405116864328891950288888434929210668328849466319419951775157374761930395371626801844365799774616689823184955256615103504859356914334395152128600862146719619859327119380994333493461955529620578485576675021993313219918726432622856542420570716350341841652548574072964446809201965L,)"
signature_tuple = signature.split(",")
message = "Block_Height:1 From:c52030257a864a67ae4ef8a726282ed2b6b273fbccb474885027a857 To:2 Amount:3"
if public_key.verify(message, signature_tuple) == True:
print "Signature valid"
.
Traceback (most recent call last):
File "C:\Users\kucerjan\Desktop\test\sco\public_test.py", line 12, in <module>
if public_key.verify(message, signature_tuple) == True:
File "build\bdist.win32\egg\Crypto\PublicKey\RSA.py", line 221, in verify
return pubkey.pubkey.verify(self, M, signature)
File "build\bdist.win32\egg\Crypto\PublicKey\pubkey.py", line 126, in verify
return self._verify(M, signature)
File "build\bdist.win32\egg\Crypto\PublicKey\RSA.py", line 257, in _verify
return self.key._verify(m, s)
File "build\bdist.win32\egg\Crypto\PublicKey\_slowmath.py", line 73, in _verify
return self._encrypt(sig) == m
File "build\bdist.win32\egg\Crypto\PublicKey\_slowmath.py", line 65, in _encrypt
return pow(m, self.e, self.n)
TypeError: unsupported operand type(s) for pow(): 'str', 'long', 'long'
This signature is already converted to string using str(signature). I basically need to convert it to string and back.
Function reference: https://www.dlitz.net/software/pycrypto/api/current/Crypto.PublicKey.RSA._RSAobj-class.html#verify
Public key:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDFiMH7Lbd4JPFug8TaxX1DT8ad
lzzGm7CG1js0IQn2pCPPWBS+io1i0iUPmj78IOtUuoBqtEYGPgwqguYHozBuvdJy
Lcz4C2bYcjb2l8mQ4PM7iaCN4eHB+4xa+iJduogTjq8gx5m3j5mttEGUbZc2Q/AO
yde592P2iuRIrXcLuwIDAQAB
-----END PUBLIC KEY-----
The problem is in deserializing the signature tuple.
PyCrypto is expecting a tuple with an integer as the first value, you are passing it a string with a beginning paren "(" and then a string version of a number.
Instead of doing this:
signature_tuple = signature.split(",")
do this
signature_tuple = eval(signature)
That will properly parse the signature.
Now, there are security risks with using eval. So, if I were you, I'd come up with a better serialization/deserialization process.
The best way is to use PKCS1_v1_5 for real applications in combination with base64 for encoding and decoding the signature between client and server. No eval is needed.
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
import base64
message = 'To be signed'
key = RSA.importKey(open('privkey.der').read())
h = SHA.new(message)
signer = PKCS1_v1_5.new(key)
signature = signer.sign(h)
signature_enc = str(base64.b64encode(signature))
#print signature_enc
signature_dec = str(base64.b64decode (signature_enc))
#print sugnature_dec
key = RSA.importKey(open('pubkey.der').read())
h = SHA.new(message)
verifier = PKCS1_v1_5.new(key)
if verifier.verify(h, signature_dec):
print "The signature is authentic."
else:
print "The signature is not authentic."

PyCrypto : AssertionError("PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()")

I am creating various processes that do different tasks. One of them and only one of them, has a security module that creates the PyCrypto objects.
So my program starts, creates the various processes, the process that handles messages uses the security module to decrypt and I get the following errors:
firstSymKeybin = self.cipher.decrypt(encFirstSymKeybin, '')
File "/usr/local/lib/python2.7/dist-packages/Crypto/Cipher/PKCS1_v1_5.py", line 206, in decrypt
m = self._key.decrypt(ct)
File "/usr/local/lib/python2.7/dist-packages/Crypto/PublicKey/RSA.py", line 174, in decrypt
return pubkey.pubkey.decrypt(self, ciphertext)
File "/usr/local/lib/python2.7/dist-packages/Crypto/PublicKey/pubkey.py", line 93, in decrypt
plaintext=self._decrypt(ciphertext)
File "/usr/local/lib/python2.7/dist-packages/Crypto/PublicKey/RSA.py", line 235, in _decrypt
r = getRandomRange(1, self.key.n-1, randfunc=self._randfunc)
File "/usr/local/lib/python2.7/dist-packages/Crypto/Util/number.py", line 123, in getRandomRange
value = getRandomInteger(bits, randfunc)
File "/usr/local/lib/python2.7/dist-packages/Crypto/Util/number.py", line 104, in getRandomInteger
S = randfunc(N>>3)
File "/usr/local/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 187, in read
return self._singleton.read(bytes)
File "/usr/local/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 163, in read
return _UserFriendlyRNG.read(self, bytes)
File "/usr/local/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 122, in read
self._check_pid()
File "/usr/local/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 138, in _check_pid
raise AssertionError("PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()")
AssertionError: PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()
Decrypting works well on interactive, when not called from a process.
My security module looks like this:
'''
Created on 25 Apr 2013
#author: max
'''
import base64, ast, binascii
from Crypto.Cipher import AES
from Crypto.Cipher import PKCS1_v1_5
from Crypto.PublicKey import RSA
import br_consts
class SecurityMod(object):
'''
classdocs
'''
def __init__(self):
'''
Constructor
'''
super(SecurityMod,self).__init__()
self.privkey = RSA.importKey(open('./privkeyBR.pem', 'r').read())
self.cipher = PKCS1_v1_5.new(self.privkey)
self.ridToKeySalt = {}
#depending on the type of message, encryption format is different
def encrypt(self, msg, rqId, rid):
##code
return encMsg
#return string of object so it can be parse by JSON
def decrypt(self, encMsg, rqId, rid):
#code
return msgObjStr
def pad_data(self,data):
if len(data) == 0:
return data
if len(data) % 16 == 0:
padding_required = 15
else:
padding_required = 15 - (len(data) % 16)
data = '%s\x80' % data
data = '%s%s' % (data, '\x00' * padding_required)
return data
def unpad_data(self,data):
if not data:
return data
data = data.rstrip('\x00')
if data[-1] == '\x80':
return data[:-1]
else:
return data
You need to call Crypto.Random.atfork() after os.fork()
I just put the __init__() in the security module before the other ones
Installing the pycryptodome package fixed this issue for me on Mac and Linux. It is a fork of the Pycrypto package.
pip install pycryptodome
Here is the link to their docs: https://pycryptodome.readthedocs.io/en/latest/index.html

Categories

Resources