Using RSA public key as private key and vice-versa I am writing an application that involves checking to see if data has been created by me. I do this by encrypting the data with the private key on my server, and then decrypting it using the public key on the client. If the result matches the hashed version of the message then I know it came from me. However, I'm using RSA and so that means I have to flip the keys since I know you can't encrypt with a private key.

Here is how I am generating my keys:

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)


However, here private_key is my public key and public_key is my private key. Is there something I need to change? I've heard public keys are generally smaller but I don't understand enough of the settings to tell if they're symmetric or not.

Any advice would be appreciated.

Thanks.

EDIT: They are definitely not symmetric. As suspected, my public key is much shorter. How do I reverse this?

EDIT2: How about signing/verifying instead?

Server:

from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import padding
import base64
import uuid
import hashlib


message_id = uuid.uuid1()
with open("private_key.pem", "rb") as key_file:
    private_key = serialization.load_pem_private_key(
        key_file.read(),
        password=None,
        backend=default_backend()
    )
message = bytes(hashlib.sha3_256((str(message_id) + str(valid)).encode("utf-8")).hexdigest(), "utf-8")
encrypted_data = private_key.sign(
    message,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA3_256()),
        salt_length=padding.PSS.MAX_LENGTH
        ),
    hashes.SHA3_256()
)
encrypted_data = str(base64.b64encode(encrypted_data), "utf-8")


Client:

from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.exceptions import InvalidSignature
import hashlib
import base64


encrypted = response["encrypted"]
message_id = response["message_id"]
valid = response["valid"]
with open("public_key.pem", "rb") as key_file:
    public_key = serialization.load_pem_public_key(
        key_file.read(),
        backend=default_backend()
    )
message = bytes(hashlib.sha3_256((str(message_id) + str(valid)).encode("utf-8")).hexdigest(), "utf-8")
encrypted = base64.b64decode(encrypted)
try:
    public_key.verify(
        encrypted,
        message,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA3_256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA3_256()
    )
    print("Yes")
except InvalidSignature:
    print("No")
I am writing an application that involves checking to see if data has been created by me.


That's known as verifying data that was signed. It is good practice.


  I do this by encrypting the data with the private key on my server, and then decrypting it using the public key on the client.


That's where it gets wrong. The server must sign with the private key, and then the client must verify the signature with the public key. In RSA there are commonalities between sign and decrypt (both include the private-key operation $x\mapsto y=x^d\bmod N$), and between verify and encrypt (both include the public-key operation $y\mapsto x=y^e\bmod N$), but there also are important differences. For this reason, well-designed APIs, and knowledgeable advisers, won't let you encrypt with the private key. Don't even try. Ever. Forget about it.

Since the API includes PSS, which is a signature padding, it most likely includes primitives to sign with RSASSA-PSS. We are not a code review site, thus I won't try to find where. But my reading of hazmat is that primitives in that area are not meant for direct use, I would look elsewhere.

Edit: I do not immediately see an issue with the code as it now stands, except for naming tag and variable encrypted, which I guess are for a signature. But then I do not know this API, and we are not a code review site.