Encrypt JSON with SHA256 hash, got 512 characters back instead of 64 - python

Based on Google document and cryptography.io.
I attempt to encrypt a queried result which is in a JSON format which look like this {"data":"abc"}. As I know, when encrypting with SHA256, the encrypted data will be in a form of 64 characters but after I looked on and tried it with the code in the link and some modification, I got a result of 512 characters instead of 64 characters.
This is my code:
def encrypt_rsa(_rsa_pub, plaintext):
key_txt = _rsa_pub.encode()
public_key = serialization.load_pem_public_key(key_txt, default_backend())
# encrypt plaintext
pad = padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None)
return public_key.encrypt(plaintext, pad)
pk = ....#public key
message = .... #query result (JSON)
x = json.dumps(message).encode('utf-8')
enc = encrypt_rsa(pk , x)
enc = base64.b64encode(enc).decode()
dec = decrypt_rsa(enc)
There is no error but I got a long encrypted data with 512 characters which is really weird. I have tried decrypting it with the code from cryptography.io and got an error:
AttributeError: 'str' object has no attribute 'decrypt'
which I think this error might occured because of the wrong encrypted data that should be 64 characters but the input is 512 characters. So, based on my case, what did I forgot or what should be add to this code to make it become an encrypted data with 64 characters that can be decrypted?
Edit: decrypt function
def decrypt_rsa(ciphertext):
private_key = .....
pad = padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None)
return private_key.decrypt(ciphertext, pad)
The error occured at decrypt at the line that return the result. I actually passed the encrypted data into this function. And I think it not because of the key since it should not provide error result like this

You are encrypting data with RSA. (Not SHA256, that's just a parameter of the RSA encryption.) Note that RSA is typically only used to encrypt/decrypt very small amounts of data. Typically it is used to encrypt/decrypt another symmetric key, e.g. for AES, and then the bulk encryption/decryption is done with AES.
The size of the RSA cipher text is determined by the RSA key size. In this case you are probably using a 4096 bit RSA keys, which results in 4096 / 8 = 512 byte messages. Messages that are smaller are OK, those are padded. Messages that are larger will not work.
Finally, you are trying to call decrypt on a string, which is not a thing. You need to pass the data to a decrypt_rsa function instead, which does the reverse of the encrypt_rsa function that you've shown.
edit, after decrypt_rsa function added to question: the error message shows that private_key is not actually a private key as it should be, but a string. Why that is we can't say because the code to initialize private_key is not shown.

Related

jose encryption returns binary string but decrypting with decoded string gives error in python

I am using jose for python jwe encryption.
Here is my code from the example
import jose
from time import time
from Crypto.PublicKey import RSA
key = RSA.generate(2048)
claims = {'name': 'Jack'}
pub_jwk = {'k': key.publickey().exportKey('PEM')}
jwe = jose.encrypt(claims, pub_jwk)
jwt = jose.serialize_compact(jwe)
Here the value of jwt is binary string
b'eyJlbmMiOiAiQTEyOENCQy1IUzI1NiIsICJhbGciOiAiUlNBLU9BRVAiLCAiX192IjogMn0.N1RFIEaRIGxCgSkT8HhQI4bO66XOL2RVfn4tMu8BBfGBO79AFKzHUYIRuVqpBX9YcUrsn66n3ccH5O2HO-CuCEPZ6EBM47IBUW1NAdFnm4uc3_X3EAngGTe2hnkLp0RzByYUcaLp2bMn7TWptRmvDGrADaI3uliZCV_ahLeWWFySFjIm_LaLBUzH1okZ-uPqvKQRXDEsdmBSTH5KlsQZHOdRa6uZz_iILmZY6Pp-9XtOSldTLiGasIA_9DNfljP5UtImOhAax_piA7hHeacGAtBNZJVZCWZZajLI6HKz5hVs4aZy7I2EIK6ogL0ubBNMeCQ0dZ70SWjvBTcTbtV2jw.65XaQ1rCSIn25Gc73CJe0g.oo81kAasMwPTISH5XEnnY5Mym3PPXMVs-FtYwgboHUE.5TR5Au7A7JYU7x0iYoPhGQ'
When I decrypt with the same jwt value it gives me exact result.
enc = jose.decrypt(jose.deserialize_compact(jwt), priv_jwk)
Here when I try to make json of encrypted value
data = {'jwt': jwt}
json.dumps(data)
It gives me error Object of type 'bytes' is not JSON serializable
I can decode the encryption like:
jwt = jose.serialize_compact(jwe)
but decrypting with this encryption value will raise error. I don't want to encode jwt in decrypting process.
Is there anyway I can get string instead of byte while encryption so that I can dump it to JSON.
First of all, the Demonware/JOSE project was written for Python 2 originally, you are presumably using the Python 3 branch. There are unresolved issues with the implementation, which to me speaks of the package author not actually understanding the issue very well. JWT tokens, in their compact serialization are just a series of URL-safe Base64 strings concatenated with . characters.
Whenever you encrypt or sign a new token, you have to decode bytes to strings (just decode the bytes value as ASCII). When verifying or decrypting, you'll need to encode the strings to bytes again.
E.g. to encode the jwt value in a JSON object, you'll need to decode:
data = {'jwt': jwt.decode('ascii')}
The whole point of a Javascript Web Token (JWT) is to be exchanged with another party as text. The library compounds matters by claiming the method returns a string.
You could shorten this use the default utf-8 codec, as ASCII is a subset of UTF-8:
data = {'jwt': jwt.decode()}
In the opposite direction, you'll have to encode the compact string to bytes again:
data = json.loads(json_document)
jose.decrypt(jose.deserialize_compact(data['jwt'].encode()), priv_jwk)
But you are basically using outdated software; the Demonware/jose project has not been updated in 3 years time. It also relies on the outdated, unmaintained pycrypto package. You do not want to use.
Instead, take a look at Authlib or JWCrypto, two modules that are actively maintained, and use the cryptography project to handle the tricky cryptography primitives (there is also pyjwt and python-jose, but those projects do not (yet) support JWE encryption, only JWS signed tokens).
Of these, Authlib offers by far the cleanest and clearest API. E.g. to both generate a public / private key pair and create your encrypted token with Authlib, using the default encryption and signing algorithm chosen by Demonware/JOSE, you'd do:
from authlib.jose import jwt
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
# generate a private key with corresponding public key, then
# export the keys as a PEM serializations.
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
private_key_pem = private_key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.PKCS8,
serialization.NoEncryption()
)
public_key = private_key.public_key()
public_key_pem = public_key.public_bytes(
serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo
)
# create an encrypted token (JWT using JWE)
claims = {'name': 'Jack'}
header = {'enc': 'A128CBC-HS256', 'alg': 'RSA-OAEP'}
token = jwt.encode(header, claims, public_key_pem).decode()
# decrypt the token again
claims_from_token = jwt.decode(token.encode(), private_key_pem)
Note that Authlib too returns a bytes value here, so you'd have to decode and encode this too if you wanted to further embed this in JSON and pass the token data from a JSON payload back to the jwt.decode() method.
Using the same public and private key serializations with JWCrypto:
import json
from jwcrypto import jwt, jwk
# Create a JWK key object from the private key
jwk_key = jwk.JWK.from_pem(private_key_pem)
# create a JWT() instance to handle ecryption and serialization
header = {'enc': 'A128CBC-HS256', 'alg': 'RSA-OAEP'}
jwt_token = jwt.JWT(claims, header)
jwt_token.make_encrypted_token(jwk_key)
token = jwt_token.serialize()
# deserialize again, using a new JWT instance but with different arguments;
# Yes, this is a confusing API.
# it's always a good idea to limit what algorithms you'll accept
# note that this is a list of both *signing* and *encryption* algorithms, not
# the possible values of the "alg" key in a JOSE header.
jwt_token = jwt.JWT(key=jwk_key, jwt=token, algs=['A128CBC-HS256', 'RSA-OAEP'])
claims_from_token = json.loads(jwt_token.claims)
Note that this library does return a string when serializing, but you have to do the JSON decoding of the claims manually. The API is also mixing serializing and de-serializing into one class, creating a very confusing mix of arguments, methods, and properties.

How to use decrypt with RSA private key and SHA256 on python

I am learning for school to encrypt and decrypt a file using public and private keys en encoding.
I used this code to encode the message. (which generates public key ≠ not private key error)
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
def signing():
#open file = message als binary
message = open('C:/Users/Gebruiker/Desktop/message.txt', "rb").read()
#open public key -> key
key = RSA.import_key(open('C:/Users/Gebruiker/Desktop/public.pem').read())
#message becomes a hash
h = SHA256.new(message)
#f = open file as write binary
f = open('C:/Users/Gebruiker/Desktop/message.signature', 'wb')
# sign hash message with private key
signature = pkcs1_15.new(key).sign(h)
#write signed hash to file
f.write(signature)
f.close()
But now i am trying to decode this message and I found all these people that do it in different ways and work with different type of encoding and encrypting. And I cant find a clear answer.
what i have right now is this
First i have to read the message so
def decrypt():
f = open('C:/Users/Gebruiker/Desktop/message.signature', 'rb').read()
then i open up my private key
key = RSA.import_key(open('C:/Users/Gebruiker/Desktop/private.pem').read())
Because I write and read in binary i have to turn it back into a hash
h = SHA256.new(f)
And then i have to decrypt the hash using my private key.???
Instead i see a lot of people using something like this.
h = pkcs1_15.new(key).sign(h) # sign hash message with private key
which i don't get. You are supposed to decode it right? not sign it again. this part makes no sense to me.
Now I have 2 problems.
I get an encoding error saying my public key is not a private key. Which is kinda the point of public keying right. so only private key can decrypt? Why am i getting an error?
I don't know how to proceed with the decrypting of my message
Can anybody help me with this?
Thanks a lot!
There is confusion in your question. Signature generation for RSA requires modular exponentiation using the values of the private key, not the public key. Modular exponentiation is also used for encryption with the public key. But although the same mathematical formula is used - at least on the surface - doesn't mean that signature generation is encryption with the private key because such a thing does not exist. Current PKCS#1 standards go out of their way to explain this fact, even though earlier PKCS#1 standards used to identify signature generation with RSA encryption.
What you are trying to do is to verify the message. That's the function you would expect rather than sign. Verification is performed by a trusted public key, not by a private key. You are not trying to decode the message, you are trying to verify that the bytes of the message are indeed signed by the private key that belongs to the same key pair as the trusted public key. Generally, the message is not recovered, not even partially. PKCS#1 is called signature generation with appendix, which contrasts with other schemes called signature generation giving message recovery. The appendix is the signature value, it needs to be appended (included with) the message to be of any use.
Actually, the fact that you can at least recover the hash over the message is specific to some schemes like PKCS#1 signature generation (officially called RSASSA-PKCS1-v1_5 in the standard). Other schemes such as PSS in the same standard may not even recover the hash. This is OK as long the verification (which can take place given the data and therefore hash) can succeed or fail. In other words, the verification should at least result in a boolean true / false, but it doesn't need to generate any other information.
Or, in simplified pseudo-code:
ciphertext = encrypt(publicKey, plaintext)
(recovered) plaintext = decrypt(privateKey, ciphertext)
and
signature = sign(privateKey, data)
verificationResult = verify(publicKey, data, signature)
where the data hashing algorithm is a configuration parameter for the signature generation & verification algorithm. If you want to include it you could e.g. include it as initial parameter:
signature = sign(SHA256alg, privateKey, data)
verificationResult = verify(SHA256alg, publicKey, data, signature)
Finally, you are talking about "decoding". You decode messages that have been encoded using an encoding scheme. Encoding/decoding does not presume the presence of a key. We're talking about encryption / decryption and signature generation / verification instead. Examples of encoding are hexadecimals / base 64 that convert binary to text. Character encoding such as UTF-8 is about converting text to binary.
The convention is to encrypt with a receives public RSA key so that only the holder of the corresponding private key can decrypt the message.
Also by convention, you would use your private RSA key to create a signatur that everybody else with the corresponding public key can verify.
In principle you could use a public key for creating a signature, but this would be an erroneous use case of the key, and is often prevented in the libraries implementing RSA. In your case you get a "public key ≠ not private key error" as you try to use a public key in a sign(..) call.
When signing you don't use the full message as input to RSA, but instead calculates a hash (you use SHA256). This hash would not need to be "decryped" in the signature verification, but instead recalculated on the original message, that you want to verify.

Can I use the AES I.V. or nonce as a Password Salt?

I'm trying to make a Python program which will take the file and key and then it will encrypt the file.
I already know that the AES-GCM and AES-CFB mode uses a nonce and IV, respectively. And I currently store the IV/nonce in the encrypted file itself. I'm pondering over the idea if I can use the IV/nonce of the AES-CFB/AES-GCM as my password hashing salt?
Earlier I hashed the key provided, but when I came to know about Rainbow-tables, I thought of using a more sophisticated way.
The approach I came to know about was PBKDF2.
if filepath.endswith(EXT):
method = 'decrypt'
flag = False
with open(filepath, 'rb+') as f:
f.seek(-NONCE_SIZE,2)
iv = f.read()
os.truncate(filepath, os.path.getsize(filepath) - NONCE_SIZE)
# If the file doesn't end with the required extension,
# then identify the method as `encrypt` and do the same
# with the key provided.
else:
method = 'encrypt'
flag = True
iv = Random.new().read(NONCE_SIZE)
# Make a cipher object with the nonce and key and write
# to the file with the arguments.
# Previous approach as commented-out code line below
# key = hashlib.sha3_256(key.encode()).digest()
key = PBKDF2(key, iv, dkLen=32)
crp = getattr(AES.new(key, AES.MODE_GCM, nonce=iv), method)
I expect that the IV/nonce used as a password hashing salt provides the security required.
That is what the IV and the nonce are there for already. Using them twice might have catastrophic effects on the encryption. A nonce is by definition a number that is used only once.
I realized that there is no saner way other than to just create two
different random bytes, one for the password derivation salt, and the
other one for nonce for the block cipher.

RSA with AES 128

This is my protocol:
Encryption and signing - user A
cipher using the public key from user B
sign the encrypted message with the private key A
Verifying and decrypting - user B
verify the signature with the public key A
decrypt the message with the private key B
The private key A and B are the same (128 bit)
I want to send the text using this protocol with AES in mode CBC so i create this code but doesnt work ,apperar in signature:
bytes object has no attribute n
the code is the following:
def firmar(self, datos):
try:
h = SHA256.new(datos)
signature = pss.new(self.keyprivada).sign(h)
return signature
except (ValueError, TypeError):
return None
def comprobar(self, text, signature):
h = SHA256.new(text)
print(h.hexdigest())
verifier = pss.new(self.keypublica)
try:
verifier.verify(h, signature)
return True
except (ValueError, TypeError):
return False
This section is no longer relevant as the code has changed
Firstly, you are usine ECB this is insecure due to the relationship between text and its output being constant.
Secondly, CBC requires an IV hence a different implementation would be required.
Lastly and most crucially:
AES is NOT an asymmetric encryption algorithm
meaning that it must be encrypted and decrypted with the same key. You use the public and the private keys as you would with asymmetrical encryption methods.
An alternative:
If you were to implement RSA properly you could then generate a random byte array and use that as your key, then send it encrypted to the recipient to decrypt it and use it as the key to decrypt the aes as it would be the same.
Now:
You use the private key to sign the data... RSA requires you to use the public key (now private - not distributed) to encrypt it however you cannot decrypt something encrypted with the private key with the public key. Instead you distribute the ‘private key’ for decryption and keep the ‘public key’ for encryption so no one else can encrypt or sign the data.
WHAT are you doing!
There is a relationship between public and private keys! You cannot just use random byte arrays.
Read the Wikipedia article.

Asymmetric cryptography - Plaintext size error

I’m trying to encrypt small data using asymmetric cryptography with python. I'm currently using M2Crypto to generate 1024 bit private/public key pair.
After using different python libraries, such has M2Crypto and Pycrypto (with several variations on it), I'm having plaintext size problems: ValueError: Plaintext is too long.
This happens because I'm trying to encrypt the data and after that encrypting that last encryption (encryption over encryption), e.g.:
Encryption:
EKpuuser(EKprown(Data)) -> EData
puser: Public key user,
prown: Private key (data) owner
Decryption:
DKpruser(DKpuown(EData)) -> Data
pruser: Private key user,
puown: Public key (data) owner
I have tried many solutions that I've found around the web, but the only one that helped me to pass this problem was using signatures before doing encryption:
ciphertext = 'xpto'
m_EOi = hashlib.sha1()
m_EOi.update(ciphertext_EOi)
sig_EOi = (m_EOi.hexdigest())
But this solution isn't what I need, because after I used it and encrypt the signature (and encrypt the encryption), then do the decryption, can't decrypt the signature, so I can't get to the initial message.
Edited:
I already have done something like e.g.:
BLOCK_SIZE = 32
PADDING = '{'
message = 'today'
key = 'aaaaaaaaaa123456'
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s))) cipher = AES.new(key)
encoded = EncodeAES(cipher, message)
key = 123
h1 = SHA256.new()
h1.update(key)
key1 = h1.digest()[0:16]
iv1 = Random.new().read(16)
cipher1 = AES.new(key1, AES.MODE_CFB, iv1)
criptogram1 = iv1 + cipher1.encrypt(data1)
But I allways have the plaintext size problem.
Asymmetric cryptography isn't made for what you are trying to do. Asymmetric cryptography is usually used in hybrid solutions to encrypt the keys of symmetric cryptographic systems which are used to encrypt the actual data.
Usually something like this:
data + a symmetric (random) key (K) -> symmetric cipher (e.g. AES) -> cipher text
K + public asymmetric key of the recipient -> asymmetric cipher -> Ke
Then you transmit the cipher text and Ke to the recipient
K is usually way smaller than the maximum data size of asymmetric ciphers while your ordinary plain text data is not.
When you encrypt data with an RSA key, for example, you want to pad the data with OAEP padding. No matter how small your plaintext is, like "Today", it's going to get padded out to the full modulus of the key, e.g. 1024 bits. If you next try to encrypt that with the same size key, it won't fit. There is no room anymore to pad again. You need a bigger key, or, you don't pad. Not padding would be a big mistake -- you need the padding to be secure.
Why would you encrypt twice anyway? It's not making it any safer. Are you devising your own scheme? That would be risky.
Why would you sign ciphertext? A digital signature over ciphertext is signing a document of unintelligible gibberish -- try taking that signature in front of a court. Why not just add a MAC?
After more research I've managed to find something that helped me. It isn't 100% what I was looking for (related to the plaintext size error) but helps me in a way that I use signatures the go around the problem. Here is the link were I did find out the information:
http://e1ven.com/2011/04/06/how-to-use-m2crypto-tutorial

Categories

Resources