How can I encrypt with a RSA private key in python?

295
July 09, 2018, at 01:10 AM

Is it possible to encrypt a message with a private key in python using pycryptodome or any other library? I know that you are not supposed to encrypt with the private key and decrypt with the public key, but my purpose is to encrypt with the private one so the receiver could be sure that the message was send by the real author. More than secure encryption I'm looking for some kind of obfuscation. I want to do an app where the message is public but it can only be seen if you have the public key. I've tried to do this:

from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64

def generate_keys():
    modulus_lenght = 256 * 4
    private_key = RSA.generate(modulus_lenght, Random.new().read)
    public_key = private_key.publickey()
    return private_key, public_key
def encrypt_private_key(a_message, private_key):
    encryptor = PKCS1_OAEP.new(private_key)
    encrypted_msg = encryptor.encrypt(a_message)
    encoded_encrypted_msg = base64.b64encode(encrypted_msg)
   return encoded_encrypted_msg
def decrypt_public_key(encoded_encrypted_msg, public_key):
    encryptor = PKCS1_OAEP.new(public_key)
    decoded_encrypted_msg = base64.b64decode(encoded_encrypted_msg)
    decoded_decrypted_msg = encryptor.decrypt(decoded_encrypted_msg)
    return decoded_decrypted_msg
private_key, public_key = generate_keys()
message = "Hello world"
encoded = encrypt_private_key(message, private_key)
decoded = decrypt_public_key(encoded, public_key)
print decoded

But it raises the next error: TypeError: This is not a private key.

Answer 1

Short answer

  • the code that you are using doesn't allow you to do that for security reasons
  • alternative code below

Long answer

I was curious about your problem and then I started to try to code

After a while I realized that if you run this snippet you will see that it correctly works:

#!/usr/bin/env python
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64
def generate_keys():
    modulus_length = 1024
    key = RSA.generate(modulus_length)
    #print (key.exportKey())
    pub_key = key.publickey()
    #print (pub_key.exportKey())
    return key, pub_key
def encrypt_private_key(a_message, private_key):
    encryptor = PKCS1_OAEP.new(private_key)
    encrypted_msg = encryptor.encrypt(a_message)
    print(encrypted_msg)
    encoded_encrypted_msg = base64.b64encode(encrypted_msg)
    print(encoded_encrypted_msg)
    return encoded_encrypted_msg
def decrypt_public_key(encoded_encrypted_msg, public_key):
    encryptor = PKCS1_OAEP.new(public_key)
    decoded_encrypted_msg = base64.b64decode(encoded_encrypted_msg)
    print(decoded_encrypted_msg)
    decoded_decrypted_msg = encryptor.decrypt(decoded_encrypted_msg)
    print(decoded_decrypted_msg)
    #return decoded_decrypted_msg
def main():
  private, public = generate_keys()
  print (private)
  message = b'Hello world'
  encoded = encrypt_private_key(message, public)
  decrypt_public_key(encoded, private)
if __name__== "__main__":
  main()

but if you now change two of the final lines [i.e. the role of the keys] into:

    encoded = encrypt_private_key(message, private)
    decrypt_public_key(encoded, public)

and rerun the program you will get the TypeError: No private key

Let me quote from this great answer:

"As it turns out, PyCrypto is only trying to prevent you from mistaking one for the other here, OpenSSL or Ruby OpenSSL allow you for example to do both: public_encrypt/public_decrypt and private_encrypt/private_decrypt

[...]

Additional things need to be taken care of to make the result usable in practice. And that's why there is a dedicated signature package in PyCrypto - this effectively does what you described, but also additionally takes care of the things I mentioned"

Adapting this link I came to the following code that should solve your question:

# RSA helper class for pycrypto
# Copyright (c) Dennis Lee
# Date 21 Mar 2017
# Description:
# Python helper class to perform RSA encryption, decryption, 
# signing, verifying signatures & keys generation
# Dependencies Packages:
# pycrypto 
# Documentation:
# https://www.dlitz.net/software/pycrypto/api/2.6/
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA512, SHA384, SHA256, SHA, MD5
from Crypto import Random
from base64 import b64encode, b64decode
import rsa
hash = "SHA-256"
def newkeys(keysize):
    random_generator = Random.new().read
    key = RSA.generate(keysize, random_generator)
    private, public = key, key.publickey()
    return public, private
def importKey(externKey):
    return RSA.importKey(externKey)
def getpublickey(priv_key):
    return priv_key.publickey()
def encrypt(message, pub_key):
    #RSA encryption protocol according to PKCS#1 OAEP
    cipher = PKCS1_OAEP.new(pub_key)
    return cipher.encrypt(message)
def decrypt(ciphertext, priv_key):
    #RSA encryption protocol according to PKCS#1 OAEP
    cipher = PKCS1_OAEP.new(priv_key)
    return cipher.decrypt(ciphertext)
def sign(message, priv_key, hashAlg="SHA-256"):
    global hash
    hash = hashAlg
    signer = PKCS1_v1_5.new(priv_key)
    if (hash == "SHA-512"):
        digest = SHA512.new()
    elif (hash == "SHA-384"):
        digest = SHA384.new()
    elif (hash == "SHA-256"):
        digest = SHA256.new()
    elif (hash == "SHA-1"):
        digest = SHA.new()
    else:
        digest = MD5.new()
    digest.update(message)
    return signer.sign(digest)
def verify(message, signature, pub_key):
    signer = PKCS1_v1_5.new(pub_key)
    if (hash == "SHA-512"):
        digest = SHA512.new()
    elif (hash == "SHA-384"):
        digest = SHA384.new()
    elif (hash == "SHA-256"):
        digest = SHA256.new()
    elif (hash == "SHA-1"):
        digest = SHA.new()
    else:
        digest = MD5.new()
    digest.update(message)
    return signer.verify(digest, signature)
def main():
    msg1 = b"Hello Tony, I am Jarvis!"
    msg2 = b"Hello Toni, I am Jarvis!"
    keysize = 2048
    (public, private) = rsa.newkeys(keysize)
    # https://docs.python.org/3/library/base64.html
    # encodes the bytes-like object s
    # returns bytes
    encrypted = b64encode(rsa.encrypt(msg1, private))
    # decodes the Base64 encoded bytes-like object or ASCII string s
    # returns the decoded bytes
    decrypted = rsa.decrypt(b64decode(encrypted), private)
    signature = b64encode(rsa.sign(msg1, private, "SHA-512"))
    verify = rsa.verify(msg1, b64decode(signature), public)
    #print(private.exportKey('PEM'))
    #print(public.exportKey('PEM'))
    print("Encrypted: " + encrypted.decode('ascii'))
    print("Decrypted: '%s'" % (decrypted))
    print("Signature: " + signature.decode('ascii'))
    print("Verify: %s" % verify)
    rsa.verify(msg2, b64decode(signature), public)
if __name__== "__main__":
    main()

Final note: the last prints have ascii because as stated here "In case of base64 however, all characters are valid ASCII characters"

Rent Charter Buses Company
READ ALSO
I do not get how to get displayGPA() cleanly handle a ZeroDivisionError

I do not get how to get displayGPA() cleanly handle a ZeroDivisionError

Problem is in displayGPA() may divide by zero (ZeroDivisionError) if no courses are added to the dictionary or the grades added to the dictionary are 00 floats

194
On click, doubleclick and mouse moving

On click, doubleclick and mouse moving

I have an application using tkinter where different actions are required on mouse click, double click and when the mouse is being movedThe following class works however I wonder if there is no easier- or tkinter builtin way of doing this?

225
Removing empty strings from a re.findall command [duplicate]

Removing empty strings from a re.findall command [duplicate]

This question already has an answer here:

193
python pandas dataframe sort in descending order

python pandas dataframe sort in descending order

I'd like to sort kst column in descending order

264