RSA decryption without using the key - python

I am still new in learning RSA, so sorry for stupid question.
I am given an RSA public key and a ciphertext.
I know how RSA works so i already calculate from the N , the p and q.
Now without brute force to find the private key, I should find a way to descrypt it to plaintext and
my problem is following: When i print my cyphertext, it looks like this:
b'\xdcM\xbf\xe6\x91\x1c\xf7\x1a\xbfVM\x10\x9eu\x97\x9f\x86yx~y\xab\xac\xab\xf6\xc0G\xb5\x84\r\x89\x0b\x83\x8a\x84\x88Z28(\xd4\x99S\xd8\xf4\xed\r\xc8\xec\x00k\x0cx(\xde\x01\x0f\'\x08w\xb9_\x96X\xcap\xe0\xc5\x9b\x1c/\x0f\xabW\xb4+d6\x00Y\xcf\x04\xf3\x916$W\xa8&\xc1\x94\x90\xae\xb0\xceF,\x86.\x81Oo\x00\x00\xbc\x1fBE\x95u\xaa\xe1\xf5a\x97\r\xa5\n4\x05\x182\xd8\xb5\xfd,M\xdd\xc9\xdf\xe7\xcce\x9b\xf5\x075\x8d/\xd6\xc4R\xf3{=\xd8\xb7\x8c\xd5\x0fjo\xec\xd6\x15\x03\x1b=\x9fL\x032\xee\\x8b[\xf0\x7f-\xb6X\xaa\xba\x15\xf7AsT\xdck\x14\xf3?\xfc\x82\q\xaf\xea=\xc4\x07\x03\x82\xc0\xfb1b\x89-\x81\\xda\xbf\x94]\xac#\xf2y0\x7f\x83/\xb5\xe6i\x7f\xe4\xbb\n\xad\xdd\xbb\xbf\xa5\xdc\x08\xb6\x82\xb4\xf4g\x16\x82\x0c~\x13\x8b-\\xf0n\x99\x93\x00\xa9\xc0{Y\xb9\x93\xfeV\x1dI\x8f\xa5\xab\x17\xf7\xd4HfP\xdc\xa6\x10^b\x9cy\xc5\x81Z+\xa2H\xbe=\xdf\x9c\xd7\x9d\x809\xaag\xce\xa2\xe8\x96\xc6\xc1\xc7\x90\xdd\xe6%.-z3\\x8e?\xaf\xbbx\xb4\xe9;\xba\x87\x01:R\x1d\x889M}\xf9\xf2\xacx\xe1\xea\xbfG\xb8\x8fr:\x89\xef\x85\x9e\xbd\x10J\x1a\xbd\xe5t\x0f\x8f8\xba6&\nU\x04\xc7\xc3\xef7t)\xd8m:>\xf9\xa4#\xc7\x10l\xeas\r\x83\xfb\x96fXi\xa7=R\xff\x7f\xbf0%\xa7\x88c\x0e\x9fp{\xaa\xcb\xcen|T\x131Z\xb3X\xecB\xbf+\xf9\xa76S8\xf7\x10\xe9)\x04\xd8e\xe6\xb5 \x1f\xe8\xce\xd8\x87"\xb6\xe4\x8c\xfd\xec~//\x8b\xc1\x9b\x8a\xe7\xcd7\xc3\xf24\x01\xf7\xcf\xa9Q\x93X\x06\x8e[\xba96\xca\x17b\xaf\xe2\x8ea\x10[M%\xe0\x06h#\rN\xb6\xe7%`\xae\xfbW\xc3\x0e\x05i\xbd\xed\x96l/U\x01\xe4\xbbPJ\x96\xad\xb6\x0b\x87*\x0ct\xc2$'
How am I supposed to using the rsa with these base64 bytes? I tried to decode it with base64 and it gives me :
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xdc in position 0: invalid continuation byte.
Also convert it doesnt give me a readable string. Can someone help me with this and give me a clue ?
def decrypt(fname):
with open(fname+'.enc', 'rb') as f:
ct = f.read()
with open(pkfile, 'rb') as f:
pk = RSA.importKey(f.read(), 'PEM')
p,q = fermat(pk) //using fermat factorization to get p and q
b = ct.decode("utf-8")

bytes.decode is not for encryption, it just converts from bytes to a string object. See https://docs.python.org/3/library/stdtypes.html#bytes for more info.
You have to calculate the inverse of e mod n, then you can use:
private_key = RSA.construct((n, e, d))
dsmg = private_key.decrypt(msg)

Related

Converting RSA signature to String

I'm creating my RSA Signature like this.
transactionStr = json.dumps(GenesisTransaction())
signature = rsa.sign(transactionStr.encode(), client.privateKey, 'SHA-1')
But I'm unable to get it to a string so I can save it.
I have tried decoding it using utf8
signature.decode("utf8")
but I get the error "'utf-8' codec can't decode byte 0xe3 in position 2"
any way I can do this?
A RSA signature looks like this
b'aL\xe3\xf4\xbeEM\xc4\x9e\n\x9e\xf4M`\xba\x85*\x13\xd52x\xd9\\\xe8F\x1c\x07\x90[/\x9dy\xce\xa9IV\x89\xe0\xcd9\\_3\x1e\xaa\x80\xdea\xd1\xbem/\x8e\x91\xbd\x13\x12o\x8c\xed\xf6\x89\xb5\x0b'
.decode('utf8') is used to decode text encoded in UTF8, not arbitrary bytes. Convert the byte string to a hexadecimal string instead:
>>> sig = b'aL\xe3\xf4\xbeEM\xc4\x9e\n\x9e\xf4M`\xba\x85*\x13\xd52x\xd9\\\xe8F\x1c\x07\x90[/\x9dy\xce\xa9IV\x89\xe0\xcd9\\_3\x1e\xaa\x80\xdea\xd1\xbem/\x8e\x91\xbd\x13\x12o\x8c\xed\xf6\x89\xb5\x0b'
>>> s = sig.hex()
>>> s
'614ce3f4be454dc49e0a9ef44d60ba852a13d53278d95ce8461c07905b2f9d79cea9495689e0cd395c5f331eaa80de61d1be6d2f8e91bd13126f8cedf689b50b'
To convert back, if needed:
>>> b = bytes.fromhex(s)
>>> b
b'aL\xe3\xf4\xbeEM\xc4\x9e\n\x9e\xf4M`\xba\x85*\x13\xd52x\xd9\\\xe8F\x1c\x07\x90[/\x9dy\xce\xa9IV\x89\xe0\xcd9\\_3\x1e\xaa\x80\xdea\xd1\xbem/\x8e\x91\xbd\x13\x12o\x8c\xed\xf6\x89\xb5\x0b'
>>> b==sig
True

Cannot decrypt simple string in Python using AES128

I get k_dh from a server as an int:
k_dh = 119503520092559061448841199568006105540928326398312683239977940696158565641455510032815414544631246909440988946150279621651776468095255682411875734678137913094987602893885584001153718721315823055714857464429284631169985212295788520092555311452302786716587927084384268157895132756379511638032100445133977318010
I have to get the last 16 bytes from this and I do this way, converting it in HEX and getting the last 32 HEX chars:
key = str(hex(int(k_dh)))[-32:]
In addition, I receive from the server also the ciphertext:
chipertext = a6e0397f88668030aa861bb09fee236d
I convert it in bytes:
ciphertext = b'\xa6\xe09\x7f\x88f\x800\xaa\x86\x1b\xb0\x9f\xee#m'
using: ciphertext = bytes.fromhex(ciphertext)
And then I try to decrypt without success:
key = unhexlify(key)
IV = Random.new().read(AES.block_size)
decipher = AES.new(key,AES.MODE_CBC,IV)
plaintext = decipher.decrypt(to_decrypt)
print(plaintext)
Which give me b'\x8d]\xd9)I*\x90\xe52\x88\x04\xc7\xcc+8/'.
It should be a readable sentence, something is wrong but I don't know where.
Hope that someone could find my mistake.
Thanks!

Python 3 XChaCha20 test vectors work for encryption but decryption stage fails

I use the following vectors to test XChaCha20 encryption with AEAD by Poly1305 in python:
Vectors:
https://datatracker.ietf.org/doc/html/draft-arciszewski-xchacha-03#appendix-A.3
pycryptodome:
https://pycryptodome.readthedocs.io/en/latest/src/cipher/chacha20_poly1305.html
The drafts use HEX for the test vectors, if you really need to check me convert using this service:
https://www.asciitohex.com/
import json
from base64 import b64encode
from base64 import b64decode
from Crypto.Cipher import ChaCha20_Poly1305
from Crypto.Random import get_random_bytes
#nonce_xchacha20 = get_random_bytes(24)
nonce_xchacha20 = b64decode("QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZX")
#header = b"header"
header = b64decode("UFFSU8DBwsPExcbH")
plaintext = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."
#key = get_random_bytes(32)
key = b64decode("gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp8=")
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce_xchacha20)
cipher.update(header)
ciphertext, tag = cipher.encrypt_and_digest(plaintext)
jk = [ 'nonce', 'header', 'ciphertext', 'tag' ]
jv = [ b64encode(x).decode('utf-8') for x in (cipher.nonce, header, ciphertext, tag) ]
result = json.dumps(dict(zip(jk, jv)))
print(result)
# We assume that the key was securely shared beforehand
try:
b64 = json.loads(result)
jk = [ 'nonce', 'header', 'ciphertext', 'tag' ]
jv = {k:b64decode(b64[k]) for k in jk}
cipher = ChaCha20_Poly1305.new(key=key, nonce=jv['nonce'])
cipher.update(jv['header'])
plaintext = cipher.decrypt_and_verify(jv['ciphertext'], jv['tag'])
print("The message was: " + plaintext)
except (ValueError, KeyError):
print("Incorrect decryption")
print("sanity check if key values are the same: ")
print(b64encode(jv['nonce']))
print(b64encode(jv['header']))
print(b64encode(jv['ciphertext']))
print(b64encode(jv['tag']))
Why does my decryption stage fail if the test vectors encrypt correctly according to IETF-draft?
{"nonce": "AAAAAFBRUlNUVVZX", "header": "UFFSU8DBwsPExcbH", "ciphertext": "vW0XnT6D1DuVdleUk8DpOVcqFwAlK/rMvtKQLCE5bLtzHH8bC0qmRAvzqC9O2n45rmTGcIxUwhbLlrcuEhO0Ui+Mm6QNtdlFsRtpuYLBu54/P6wrw2lIj3ayODVl0//5IflmTJdjfal2iBL2FcaLE7Uu", "tag": "wIdZJMHHmHlH3q/YeArPSQ=="}
Incorrect decryption
sanity check if key values are the same:
b'AAAAAFBRUlNUVVZX'
b'UFFSU8DBwsPExcbH'
b'vW0XnT6D1DuVdleUk8DpOVcqFwAlK/rMvtKQLCE5bLtzHH8bC0qmRAvzqC9O2n45rmTGcIxUwhbLlrcuEhO0Ui+Mm6QNtdlFsRtpuYLBu54/P6wrw2lIj3ayODVl0//5IflmTJdjfal2iBL2FcaLE7Uu'
b'wIdZJMHHmHlH3q/YeArPSQ=='
When I convert the byte arrays back to base64, they still match the JSON output.
So reading my key values from JSON for decryption was done correctly.
Where is the mistake? I literally use a code example from the site offering pycryptodome and encryption was done correctly. It should decrypt just fine.
The decryption will be done correctly if you replace in the line
jv = [ b64encode(x).decode('utf-8') for x in (cipher.nonce, header, ciphertext, tag) ]
the expression cipher.nonce with nonce_xchacha20. The bug causes an incorrect nonce to be supplied in the JSON.
It seems that cipher.nonce can only be used to determine a randomly generated nonce (a random nonce is generated if no explicit nonce is specified when instantiating the cipher, s. here).
A second (trivial) change is in the line
print("The message was: " + plaintext)
necessary. Here a UTF8 decoding must be performed, i.e. plaintext must be replaced by plaintext.decode('utf8').
In your first post, the AADs were also set incorrectly. But this has been corrected in the meantime.
With these two changes, the code, especially the decryption, works on my machine.

ValueError: Ciphertext length must be equal to key size

I know this question was asked a lot, but I still don't really understand how could I debug my code. I was googling this question for over 3 days now, but I just can't find a suitable answer for my code. I want to add the encrypted message with an input but every time I run my code it just gives the
Traceback (most recent call last):
File "decryption.py", line 27, in <module>
label=None
File "/opt/anaconda3/lib/python3.7/site-packages/cryptography/hazmat/backends/openssl/rsa.py", line 357, in decrypt
raise ValueError("Ciphertext length must be equal to key size.")
ValueError: Ciphertext length must be equal to key size.
error. I know its with the label, but I just can't find the answer
Here is my code for the decryption:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
with open("private_key.pem", "rb") as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None,
backend=default_backend()
)
with open("public_key.pem", "rb") as key_file:
public_key = serialization.load_pem_public_key(
key_file.read(),
backend=default_backend()
)
textInput = str(input(">>> "))
encrypted = textInput.encode()
original_message = private_key.decrypt(
encrypted,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
original_decoded_message = original_message.decode("utf-8")
print(original_decoded_message)
And for the encryption:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
public_key = private_key.public_key()
pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
with open('private_key.pem', 'wb') as f:
f.write(pem)
pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
with open('public_key.pem', 'wb') as f:
f.write(pem)
raw_message = str(input(">>> "))
message = raw_message.encode("utf-8")
encrypted = public_key.encrypt(
message,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
print(encrypted)
Im really new to Asymmetric encryption / decryption.
This minor modification in the decryption part of your code made it work for me:
textInput = str(input(">>> "))
# encrypted = textInput.encode() #this is incorrect!
encrypted = eval(textInput)
This is what I get:
For the encryption part:
>>> test message
b'>xB)\xf1\xc5I\xd6\xce\xfb\xcf\x83\xe2\xc5\x8f\xcfl\xb9\x0f\xa2\x13\xa5\xe1\x03\xf7p\xb3\x9c\xeb\r\xc1"\xf2\x17\x8b\xea\t\xed\xb2xG\xb7\r\xa9\xf8\x03eBD\xdd9>\xbe\xd1O\xe2\x9f\xbb\xf9\xff5\x96l\xea\x17FI\x8d\x02\x05\xea\x1dpM\xbb\x04J\xfc\x0c\\\xfe\x15\x07\xaf \x9e\xc2\xf9M\xa4\x1d$\xc3\x99my\xb6\xc5\xad\x97\xd06\xd2\x08\xd3\xe2\xc8H\xca\xd8\xfd{\xe6\xc6\xa3\x18\xeb\xe6\xcc\xc5\x9a\xc8*\xbb\xc1\x8c\x80,\x1f\r#\x9b\x9d\xc5\x91I\xa8\xc01y\xbc\xa73\xd3\x19;\xef\x8a\xfb\xc2\xc4\x9e\xbe\x8f\xeb\x1d\x12\xbd\xe4<\xa0\xbb\x8d\xef\xee\xa3\x89E\x07"m\x1d\xb0\xf3\xd2:y\xd9\xbd\xef\xdf\xc9\xbb\x1b\xd5\x03\x91\xa4l\x8bS\x9e\x80\x14\x90\x18\xc4\x9e\\?\x8eF\x05\xa1H\x9e:\x0c\x96\x8e\xb3E3\x90\xa2\xa1\xd9\x88\xa0<X\x7f\rIP\x00\xbf\xf6\x15\xfb9tW\x17\x9f\xca\x95\xf6|\xd7\x90\xbcp\xe5\xb5,V\x1b\xe9\x90\xf6\x87 v=6'
Now for the decryption part, I use the output:
>>> b'>xB)\xf1\xc5I\xd6\xce\xfb\xcf\x83\xe2\xc5\x8f\xcfl\xb9\x0f\xa2\x13\xa5\xe1\x03\xf7p\xb3\x9c\xeb\r\xc1"\xf2\x17\x8b\xea\t\xed\xb2xG\xb7\r\xa9\xf8\x03eBD\xdd9>\xbe\xd1O\xe2\x9f\xbb\xf9\xff5\x96l\xea\x17FI\x8d\x02\x05\xea\x1dpM\xbb\x04J\xfc\x0c\\\xfe\x15\x07\xaf \x9e\xc2\xf9M\xa4\x1d$\xc3\x99my\xb6\xc5\xad\x97\xd06\xd2\x08\xd3\xe2\xc8H\xca\xd8\xfd{\xe6\xc6\xa3\x18\xeb\xe6\xcc\xc5\x9a\xc8*\xbb\xc1\x8c\x80,\x1f\r#\x9b\x9d\xc5\x91I\xa8\xc01y\xbc\xa73\xd3\x19;\xef\x8a\xfb\xc2\xc4\x9e\xbe\x8f\xeb\x1d\x12\xbd\xe4<\xa0\xbb\x8d\xef\xee\xa3\x89E\x07"m\x1d\xb0\xf3\xd2:y\xd9\xbd\xef\xdf\xc9\xbb\x1b\xd5\x03\x91\xa4l\x8bS\x9e\x80\x14\x90\x18\xc4\x9e\\?\x8eF\x05\xa1H\x9e:\x0c\x96\x8e\xb3E3\x90\xa2\xa1\xd9\x88\xa0<X\x7f\rIP\x00\xbf\xf6\x15\xfb9tW\x17\x9f\xca\x95\xf6|\xd7\x90\xbcp\xe5\xb5,V\x1b\xe9\x90\xf6\x87 v=6'
test message
The problem with your original code is that you are writing the representation of a bytes string as a unicode string (when using print(encrypted)), then encoding it into a bytes object in the decrypt code, and then passing it to the decrypt function. Encoding this string will not yield the original bytes string encrypted.
This example illustrates the problem:
>>> x = bytes(bytearray.fromhex('f3'))
>>> x #A bytes string, similar to encrypt
b'\xf3'
>>> print(x)
b'\xf3'
>>> len(x)
1
>>> str(x) #this is what print(x) writes to stdout
"b'\\xf3'"
>>> print(str(x))
b'\xf3'
>>> len(str(x)) #Note that the lengths are different!
7
>>> x == str(x)
False
>>> str(x).encode() #what you were trying to do
b"b'\\xf3'"
>>> x == str(x).encode()
False
>>> eval(str(x)) #what gets the desired result
b'\xf3'
>>> x == eval(str(x))
True
The thing is that the print function prints the representation of the object rather than the object itself. Essentially, it does this by getting a printable value of inherently unprintable objects by using __repr__ or __str__ methods of that object.
This is the documentation for __str__:
Called by str(object) and the built-in functions format() and print()
to compute the “informal” or nicely printable string representation of
an object. The return value must be a string object.
.
.
The default
implementation defined by the built-in type object calls
object.__repr__().
and __repr__:
Called by the repr() built-in function to compute the “official”
string representation of an object. If at all possible, this should
look like a valid Python expression that could be used to recreate an
object
This "official" representation returned by __repr__ is what allowed me to use the eval function in the decrypt code to solve the problem.
TL;DR:
copying the output of print(encrypted) copies the informal, human readable value returned by encrypted.__str__() or the "official" representation returned by encrypted.__repr__(), which are both in a human readable encoding such as utf-8. They cannot be encoded back(by using utf-8 encoding) to create the original bytes string that they represent.
It is also worth looking into this question from a stackoverflow user, facing the same issue. The answer gives a way to actually encode this representation back into a bytes object if you want. It's worth looking into this because eval should be avoided, it is very unsafe. For that method to work, you would have to remove the leading b' and trailing ' from the string textInput before encoding it.
My final recommendation would be to pass the entire object encrypted between the two programs by either using files with the pickle module, or some form of socket programming. Best to stay away from using print and input to pass data like this between programs to avoid encoding issues.

Fixed-digit base64 encode and decode in Python

I'm trying to encode and decode a base64 string. It works fine normally, but if I try to restrict the hash to 6 digits, I get an error on decoding:
from base64 import b64encode
from base64 import b64decode
s="something"
base 64 encode/decode:
# Encode:
hash = b64encode(s)
# Decode:
dehash = b64decode(hash)
print dehash
(works)
6-digit base 64 encode/decode:
# Encode:
hash = b64encode(s)[:6]
# Decode:
dehash = b64decode(hash)
print dehash
TypeError: Incorrect padding
What am I doing wrong?
UPDATE:
Based on Mark's answer, I added padding to the 6-digit hash to make it divisible by 4:
hash = hash += "=="
But now the decode result = "some"
UPDATE 2
Wow that was stupid ..
Base64 by definition requires padding on the input if it does not decode into an integral number of bytes on the output. Every 4 base64 characters gets turned into 3 bytes. Your input length does not divide evenly by 4, thus there's an error.
Wikipedia has a good description of the specifics of Base64.

Categories