Migrate RSA decryption from PyCrypto to PyCryptodome - python

Using the PyCrypto library, the following code prints 127:
from Crypto.PublicKey import RSA
import base64
# Private key in tuple form (obscured for privacy)
key = [1, 1, 1]
bk = "zJuG60z9Iv..." # (obscured for privacy)
privatekey = RSA.construct(key)
result = privatekey.decrypt(base64.b64decode(bk))
print(len(result))
To the best of my knowledge, this would be the equivalent using PyCryptodome. However, the resulting value only has a length of 16, indicating a possible decryption error.
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
# Private key in tuple form (obscured for privacy)
key = [1, 1, 1]
bk = "zJuG60z9Iv..." # (obscured for privacy)
privatekey = RSA.construct(key)
cipher = PKCS1_v1_5.new(privatekey)
result = cipher.decrypt(base64.b64decode(bk), None)
print(len(result))
I believe this is because my ciphertext uses textbook unpadded RSA. Does anyone know how I can decrypt this value with PyCryptodome or another maintained library?

PyCryptodome doesn't provide a built-in method to decrypt textbook RSA, but can be decrypted using modular exponentiation from the standard library.
from Crypto.PublicKey import RSA
import base64
# Private key in tuple form (obscured for privacy)
key = [1, 1, 1]
bk = "zJuG60z9Iv..." # (obscured for privacy)
privatekey = RSA.construct(key)
ciphertext = base64.b64decode(bk)
ct_int = int.from_bytes(ciphertext, 'big')
pt_int = pow(ct_int, privatekey.d, privatekey.n)
plaintext = pt_int.to_bytes(privatekey.size_in_bytes(), 'big').lstrip(b'\x00')
print(len(plaintext))

Related

Object type <class 'tuple'> cannot be passed to C code when encrypting file in django

so i want to encrypt file .txt with keyword i was input. but i got error message
Object type <class 'tuple'> cannot be passed to C code
my problem is with this code:
aes = AES.new(key.encode('utf8'), AES.MODE_CTR, counter=ctr)
if i add encode to ctr, then i got error message 'dict' object has no attribute 'encode'
if i remove don't add any encode to key and ctr, then i got error message Object type <class 'str'> cannot be passed to C code
can someone please help me to fix it? i was using django to encrypt with AES 128 with method CTR. or maybe someone can give me example aes encryption with another method but can be run in django. here's my full function code:
# AES supports multiple key sizes: 16 (AES128), 24 (AES192), or 32 (AES256).
key_bytes = 16
# Takes as input a 32-byte key and an arbitrary-length plaintext and returns a
# pair (iv, ciphtertext). "iv" stands for initialization vector.
def encrypt(key, testpass):
assert len(key) == key_bytes
print(testpass)
print(key)
# Choose a random, 16-byte IV.
iv = Random.new().read(AES.block_size)
# Convert the IV to a Python integer.
iv_int = int(binascii.hexlify(iv), 16)
# Create a new Counter object with IV = iv_int.
ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)
print(ctr)
# Create AES-CTR cipher.
aes = AES.new(key.encode('utf8'), AES.MODE_CTR, counter=ctr)
# Encrypt and return IV and ciphertext.
ciphertext = aes.encrypt(testpass)
print(iv)
print(ciphertext)
return (iv, ciphertext)
here's how i called that function:
testpass = Audio_store.objects.all().values_list('password').last()
enkripsi = encrypt("testingtesting11", testpass)
when i print testpass, it contains ('testpass_3kEMV2T.txt',)
but when i print testpass.encode("utf-8"), it shows nothing
Your test data is ('testpass_3kEMV2T.txt',), you need to pull out the element from the tuple and pass it in as bytes to your encrypt method, because the AES.encrypt method requires bytes | bytearray | memoryview as its argument. Here's a version of your encrypt that determines if the argument you pass it is a str and encodes it to bytes if so (but it doesn't check if it's otherwise bytes, I leave that as an exercise for you if you want more stringent checking).
import binascii
from Crypto.Cipher import AES
from Crypto.Util import Counter
from Crypto import Random
key_bytes = 16
# here's a rudimentary change that accepts bytes or
# str
def encrypt(key, plaintext):
if isinstance(plaintext, str):
plaintext = plaintext.encode("utf-8")
assert len(key) == key_bytes
# Choose a random, 16-byte IV.
iv = Random.new().read(AES.block_size)
# Convert the IV to a Python integer.
iv_int = int(binascii.hexlify(iv), 16)
# Create a new Counter object with IV = iv_int.
ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)
print(ctr)
# Create AES-CTR cipher.
aes = AES.new(key.encode('utf8'), AES.MODE_CTR, counter=ctr)
# Encrypt and return IV and ciphertext.
ciphertext = aes.encrypt(plaintext)
return (iv, ciphertext)
# for now, since you're getting your data from the db which is a 1-tuple, you have to pull out the first elem
testpass = ('testpass_3kEMV2T.txt',)
print(encrypt("a"*16, testpass[0]))
Output:
{'counter_len': 16, 'prefix': b'', 'suffix': b'', 'initial_value': 302861312392273214314458272471454024973, 'little_endian': False}
(b'\xe3\xd8\xf7\x90\xcd\x96m\xcb\xa5g)\xd1\xda\xc3\x85\r', b'-F)\x83\x9a\xf9\xe1\xc3\xfb\xfa_<^\x1c:q\x07\xa1#\xbb')

Python AES.MODE_CTR does not decrypt as expected

I am trying to rewrite a node.js decoding function in python
this is the function I am trying to rewrite
module.exports.decrypt = function (data, key, iv, hmacKey) {
iv = iv.slice(0, 16)
var decipherer = crypto.createDecipheriv('aes-128-ctr', key, iv)
var hmac
if (hmacKey) {
hmac = crypto.createHmac('sha1', hmacKey).update(data).digest()
}
return {
data: decipherer.update(data),
hmac: hmac
}
}
the key parameter is generated by crypto.pbkdf2Sync function of node's standard library.
What I implemented in python is
from Crypto.Cipher import AES
from Crypto.Util import Counter
def decrypt(data, key, iv, hmac=None):
iv = iv[:16]
ctr = Counter.new(128)
cipher = AES.new(key, mode=AES.MODE_CTR, IV=iv, counter=ctr)
return cipher.decrypt(data)
I provided the python decrypt function the same same data that the js function receives.
since the data and key, iv params that the js function expects are binary buffers, I copied them to the python code as hex strings and then used binascii.unhexlify in the python code before passing them to the ecrypt python function.
so the arguments that I am providing to both functions are (hex)
data 972acf88c5d7
key 129b6e542600889a75ec7659d9dc23df
iv 31323331323331323331323331323331323331323331313233313233313233313233
The python function returns gibberish.
can anyone help?
thanks!

Why Python and Node.js's HMAC result is different in this code?

Recently, I have a task to make HMAC to communicate API server.
I got a sample code of node.js version which makes HMAC of message. Using concept and sample, I've got to make a python code which is equivalent with node.js version but result is different, but I have no idea why.
Please review both code and help finding the difference.
Python 3.0
import hmac
import string
import hashlib
import base64
secret = 'PYPd1Hv4J6'
message = '1515928475.417'
key = base64.b64encode(secret.encode('utf-8'))
hmac_result = hmac.new(key, message.encode('utf-8'), hashlib.sha512)
print(base64.b64encode(hmac_result.digest()))
Result (Python 3.6)
b'7ohDRJGMGYjfHojnrvNpM3YM9jb+GLJjbQvblzrE17h2yoKfIRGEBSjfOqQFO4iKD7owk+gSciFxFkNB+yPP4g=='
Node.JS
var crypto = require('crypto');
var secret = 'PYPd1Hv4J6';
var message = '1515928475.417'
var key = Buffer(secret, 'base64');
var hmac = crypto.createHmac('sha512', key);
var hmac_result = hmac.update(message).digest('base64');
console.log(hmac_result)
Result (Node.JS 6.11)
m6Z/FxI492VXKDc16tO5XDNvty0Tmv0b1uksSbiwh87+4rmg43hEXM0WmWzkTP3aXB1s5rhm05Hu3g70GTrdEQ==
Your input keys are different, so the outputs will be different.
Node:
var secret = 'PYPd1Hv4J6';
var message = '1515928475.417'
var key = Buffer(secret, 'base64'); // buffer of bytes from the base64-encoded string 'PYPd1Hv4J6'
// <Buffer 3d 83 dd d4 7b f8 27>
Python:
secret = 'PYPd1Hv4J6'
message = '1515928475.417'
key = base64.b64encode(secret.encode('utf-8')) # did you mean b64decode here?
I was able to get them to match by stripping out the base64ing of everything:
Python:
import hmac
import string
import hashlib
import base64
secret = 'PYPd1Hv4J6'
message = '1515928475.417'
key = secret.encode('utf-8')
hmac_result = hmac.new(key, message.encode('utf-8'), hashlib.sha512)
print(base64.b64encode(hmac_result.digest()))
Output:
b'jezLNuBz37FoACm4LdLSqOQ5C93cuGID9a8MQmOZntXklDV3SvWdNfqndzK0a54awKeHY+behFiv4FYyILRoGQ=='
Javascript:
var crypto = require('crypto');
var secret = 'PYPd1Hv4J6';
var message = '1515928475.417'
var hmac = crypto.createHmac('sha512', secret);
var hmac_result = hmac.update(message).digest('base64');
console.log(hmac_result)
Output:
jezLNuBz37FoACm4LdLSqOQ5C93cuGID9a8MQmOZntXklDV3SvWdNfqndzK0a54awKeHY+behFiv4FYyILRoGQ==
Equivalent/Expected python code's is below.
import hmac
import string
import hashlib
import base64
secret = 'PYPd1Hv4J6=='
message = '1515928475.417'
key = base64.b64decode (secret.encode('utf-8'))
hmac_result = hmac.new(key, message.encode('utf-8'), hashlib.sha512)
print(base64.b64encode(hmac_result.digest()))
Padding '=' to targeted and decoding part was important.
Thank you.

RSA generate private key from data with python

I have a cipher message in base64 and a pubkey.pem with the public key information. So because the key is small(576) I have recovered all the needed information to reconstruct the private key : p,q and d.
Now I want to decipher the message but I don't know how to do it. Indeed if I want to use the decrypt function I need an priv_key object but I don't know how to generate it from (n,e,d)
from Crypto.PublicKey import RSA
from base64 import b64decode
#message I want to decipher
msg="e8oQDihsmkvjT3sZe+EE8lwNvBEsFegYF6+OOFOiR6gMtMZxxba/bIgLUD8pV3yEf0gOOfHuB5bC3vQmo7bE4PcIKfpFGZBA"
pub_key64 = 'MGQwDQYJKoZIhvcNAQEBBQADUwAwUAJJAMLLsk/b+SO2Emjj8Ro4lt5FdLO6WHMMvWUpOIZOIiPu63BKF8/QjRa0aJGmFHR1mTnG5Jqv5/JZVUjHTB1/uNJM0VyyO0zQowIDAQAB'
pub_keyDER = b64decode(pub_key64)
pub_key_obj = RSA.importKey(pub_keyDER) #my weak public key
... # how to generate priv_key_obj ???
dsmg=priv_key_obj.decrypt(msg)
You can construct a private from components in the following way (documentation):
from Crypto.PublicKey import RSA
# assume d was correctly calculated
n = 1234....L
e = 65537L
d = 43434...L
private_key = RSA.construct((n, e, d))
dsmg = private_key.decrypt(msg)

PyCrypto: Attribute Error: 'NoneType' object has no attribute 'oid' while using PKCS1_v1_5

SHA = hashlib.sha1()
Eh = SHA.update(chunk)
HRSA.signSHA(Eh,RSAprivatekey)
RSAprivatekey is read in HRSA module and passed as argument to this function:
RSAprivatekey = RSA.importKey(infile.read())
infile points to the 'privatekey.txt' which contained only the RSAprivatekey.
HRSA is a module I've created which basically does this:
def signSHA(hash, key):
signer = PKCS1_v1_5.new(key)
D = signer.sign(hash)
return D
I'm being shown the following error:
File "D:\Study\Sem V\Hybrid Encryption\Phase 2\HRSA.py", line 57, in signSHA
D = signer.sign(hash)
File "C:\Python33\lib\site-packages\Crypto\Signature\PKCS1_v1_5.py", line 110, in sign
em = EMSA_PKCS1_V1_5_ENCODE(mhash, k)
File "C:\Python33\lib\site-packages\Crypto\Signature\PKCS1_v1_5.py", line 211, in EMSA_PKCS1_V1_5_ENCODE
digestAlgo = DerSequence([hash.oid, DerNull().encode()])
AttributeError: 'NoneType' object has no attribute 'oid'
How could I fix this since it's a bug with the PyCrypto code?
hashfunc.update(arg) doesn't return anything. It's there to update the internal state of the hashing function with new input data. If you want to convert the internal state into a hash, then you need to call either hashfunc.digest() or hashfunc.hexdigest().
It would look like this:
Eh = hashlib.sha1(chunk).digest()
HRSA.signSHA(Eh, RSAprivatekey)
You must use the PyCrypto module for SHA1, so:
from Crypto.Hash import SHA1
sha_obj = SHA1.new()
sha_obj.update(chunk)
HRSA.signSHA(sha_obj,RSAprivatekey)
The reason is that the PKCS#1v1.5 signature embeds the ASN.1 Object ID of the hash, but the SHA-1 object obtained from the standard library does not include/know it.
As mentioned, hashlib does not work well with PyCryptodome.
To provide a little more detailed example:
from Crypto.Hash import SHA256
from Crypto.Signature import pkcs1_15
from Crypto.PublicKey import RSA
# Generate a new RSA key pair with the specified key length
key_length = 1024
key = RSA.generate(key_length)
# Get the RSA keys
private_key = key.export_key()
public_key = key.publickey().export_key()
msg = b"The aliens are coming!"
hash = SHA256.new(msg)
# Generate the signature
signer = pkcs1_15.new(RSA.import_key(private_key))
signature = signer.sign(hash)
verifier = pkcs1_15.new(RSA.import_key(public_key))
#Exception will be thrown if verification fails
verifier.verify(hash, signature)

Categories