11

Given a public key exponent and modulus like the following, how can I encrypt a string and send it to a server as text?

publicKey: 10001,
modulus: 'd0eeaf178015d0418170055351711be1e4ed1dbab956603ac04a6e7a0dca1179cf33f90294782e9db4dc24a2b1d1f2717c357f32373fb3d9fd7dce91c40b6602'

I am trying to replicate the functionality provided by the javascript rsa library http://www.ohdave.com/rsa/ in python. In javascript, it looks something like this:

setMaxDigits(67); //sets a max digits for bigInt
var key = new RSAKeyPair('10001', '10001', 'd0eeaf178015d0418170055351711be1e4ed1dbab956603ac04a6e7a0dca1179cf33f90294782e9db4dc24a2b1d1f2717c357f32373fb3d9fd7dce91c40b6602');
var encrypted = encryptedString(key, 'message');
console.log(encrypted); //prints '88d58fec172269e5186592dd20446c594dbeb82c01edad41f841666500c9a530e24a282c6527ec66f4c826719f12478c6535bdc2baef86e4ff26906a26398413'

I imagine there is a way to do this with the PyCrypto library but I couldn't find any examples that use the exponent and modulus.

Edit 1:

Using the solution below, it appears to be working. Since I'm using python 2.7 I modified it to look like this:

from Crypto.PublicKey.RSA import construct
from binascii import unhexlify
from codecs import encode

e = long(10001)
n = int(encode('d0eeaf17801.....5d041817005535171', 'hex'), 16)

key = construct((n, e))
a = key.encrypt('hello', None)
print(a)

('.X?\xdc\x81\xfb\x9b(\x0b\xa1\xc6\xf7\xc0\xa3\xd7}U{Q?\xa6VR\xbdJ\xe9\xc5\x1f\x
f9i+\xb2\xf7\xcc\x8c&_\x9bD\x00\x86}V[z&3\\]_\xde\xed\xdc~\xf2\xe1\xa9^\x96\xc3\
xd5R\xc2*\xcb\xd9\x1d\x88$\x98\xb0\x07\xfaG+>G#\xf7cG\xd8\xa6\xf3y_ 4\x17\x0b\x0
3z\x0cvk7\xf7\xebPyo-\xa1\x81\xf5\x81\xec\x17\x9e\xfe3j\x98\xf2\xd5\x80\x1d\xdd\
xaf\xa4\xc8I\xeeB\xdaP\x85\xa7',)

Now I want to convert this encrypted text to a string to send via a post request. But this doesn't seem to work:

a.decode('utf-8')
twoLeftFeet
  • 693
  • 1
  • 5
  • 25

2 Answers2

22

With PyCrypto, you can use the Crypto.PublicKey.RSA.construct() function. You'll need to convert the modulus to an int. Here's an example (assuming big-endian):

from Crypto.PublicKey.RSA import construct

e = int('10001', 16)
n = int('d0eeaf...0b6602', 16)  #snipped for brevity
pubkey = construct((n, e))

Then you can do the usual things (like encrypt) with the key:

from Crypto.Cipher import PKCS1_OAEP

cipher = PKCS1_OAEP.new(pubkey)
ciphertext = cipher.encrypt(b'abcde')

Edit: Note that your public exponent, 10001, is mostly likely hexadecimal. This would correspond to the common public exponent 65537. I've updated the above to reflect that.

glibdud
  • 7,550
  • 4
  • 27
  • 37
  • Thanks for the help. It got me started. My end goal is to convert this to json and post it to a server. Can you explain how to convert the string above to a valid json string? – twoLeftFeet Oct 18 '16 at 15:50
  • @roemhildtg That's more suitable for a new question, but it appears that you've just got a 1-tuple with a string, so all you need to do is index it (`a[0]`). – glibdud Oct 18 '16 at 17:00
  • @roemhildtg Also see my edit. I was making the modulus conversion more complicated than it needed to be, and the public exponent less so than it needed to be. – glibdud Oct 18 '16 at 17:09
  • Okay Thanks! I'll might just post a new question about the json aspect of it. – twoLeftFeet Oct 18 '16 at 17:13
  • @roemhildtg Hi, may I ask if you solve this problem? – Jim Apr 03 '17 at 08:30
  • @Jim I didn't figure out how to transfer the encrypted string via json. I ended up going with a different route (using a client side js library) so I didn't really follow up. But the initial part of this question appears to be correct. – twoLeftFeet Apr 03 '17 at 14:01
  • 1
    @roemhildtg maybe you need binascii.hexlify(a[0]) – Jim Apr 04 '17 at 09:50
  • @karthikeayan you need convert the e to long type. such as:e = long(exponent, 16). So I got the encrypted data. but the result is different with the js code. I can not figure out why. – Howardyan Sep 30 '18 at 23:19
  • ```pubkey.encrypt()``` doesn't work: NotImplementedError: Use module Crypto.Cipher.PKCS1_OAEP instead – Sergey Nudnov Apr 14 '22 at 05:05
  • @SergeyNudnov Updated. Directly using the encypt() method on the key was never the best way to encrypt, it was just the simplest to demonstrate. The sticky part of this process was turning the modulus and exponent into a usable key. A user should carefully consider what cipher they use based on the application. – glibdud Apr 14 '22 at 12:24
  • Thank you! When one is not in topic, just in need of a quick solution, as myself in this case, that's important to have best possible code in an answer – Sergey Nudnov Apr 14 '22 at 17:42
  • @SergeyNudnov There isn't really a "one code to rule them all" when taking about cryptography. Be careful about using code without understanding the limitations of the cipher being used. – glibdud Apr 14 '22 at 18:10
  • Yep, agreed. In my case I have just realized that I should use PKCS1_v1_5 instead of PKCS1_OAEP to be understood on the other end :) – Sergey Nudnov Apr 14 '22 at 18:25
5

I tried an alternative way using Crypto.Cipher.PKCS1_OAEP motivated by: https://cryptobook.nakov.com/asymmetric-key-ciphers/rsa-encrypt-decrypt-examples and it just worked.

PS: There seems to be something wrong with modulus given, as modulus n must be the product of two large primes, thus should not be an even number. A tiny modification of n has been applied to make the example code runnable.

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import binascii

e = int('10001', 16)
n = int('d0eeaf178015d0418170055351711be1e4ed1dbab956603ac04a6e7a0dca1179cf33f90294782e9db4dc24a2b1d1f2717c357f32373fb3d9fd7dce91c40b6601', 16)

# Construct a `RSAobj` with only ( n, e ), thus with only PublicKey
rsaKey = RSA.construct( ( n, e ) )
pubKey = rsaKey.publickey()
print(f"Public key:  (n={hex(pubKey.n)}, e={hex(pubKey.e)})")

# Export if needed
pubKeyPEM = rsaKey.exportKey()
print(pubKeyPEM.decode('ascii'))

# Encrypt message using RSA-OAEP scheme
msg = b'Hello, world.'
encryptor = PKCS1_OAEP.new(pubKey)
encrypted = encryptor.encrypt(msg)
print("Encrypted:", binascii.hexlify(encrypted))
ZHANG Wuji
  • 111
  • 2
  • 5