288 lines
11 KiB
ReStructuredText
288 lines
11 KiB
ReStructuredText
.. _usage:
|
||
|
||
Usage
|
||
=====
|
||
|
||
This section describes the usage of the Python-RSA module.
|
||
|
||
Before you can use RSA you need keys. You will receive a private key
|
||
and a public key.
|
||
|
||
.. important::
|
||
|
||
The private key is called *private* for a reason. Never share this
|
||
key with anyone.
|
||
|
||
The public key is used for encrypting a message such that it can only
|
||
be read by the owner of the private key. As such it's also referred to
|
||
as the *encryption key*. Decrypting a message can only be done using
|
||
the private key, hence it's also called the *decryption key*.
|
||
|
||
The private key is used for signing a message. With this signature and
|
||
the public key, the receiver can verify that a message was signed
|
||
by the owner of the private key, and that the message was not modified
|
||
after signing.
|
||
|
||
|
||
Generating keys
|
||
---------------
|
||
|
||
You can use the :py:func:`rsa.newkeys` function to create a keypair:
|
||
|
||
>>> import rsa
|
||
>>> (pubkey, privkey) = rsa.newkeys(512)
|
||
|
||
Alternatively you can use :py:meth:`rsa.PrivateKey.load_pkcs1` and
|
||
:py:meth:`rsa.PublicKey.load_pkcs1` to load keys from a file:
|
||
|
||
>>> import rsa
|
||
>>> with open('private.pem', mode='rb') as privatefile:
|
||
... keydata = privatefile.read()
|
||
>>> privkey = rsa.PrivateKey.load_pkcs1(keydata)
|
||
|
||
|
||
Time to generate a key
|
||
++++++++++++++++++++++
|
||
|
||
Generating a keypair may take a long time, depending on the number of
|
||
bits required. The number of bits determines the cryptographic
|
||
strength of the key, as well as the size of the message you can
|
||
encrypt. If you don't mind having a slightly smaller key than you
|
||
requested, you can pass ``accurate=False`` to speed up the key
|
||
generation process.
|
||
|
||
Another way to speed up the key generation process is to use multiple
|
||
processes in parallel to speed up the key generation. Use no more than
|
||
the number of processes that your machine can run in parallel; a
|
||
dual-core machine should use ``poolsize=2``; a quad-core
|
||
hyperthreading machine can run two threads on each core, and thus can
|
||
use ``poolsize=8``.
|
||
|
||
>>> (pubkey, privkey) = rsa.newkeys(512, poolsize=8)
|
||
|
||
These are some average timings from my desktop machine (Linux 2.6,
|
||
2.93 GHz quad-core Intel Core i7, 16 GB RAM) using 64-bit CPython 2.7.
|
||
Since key generation is a random process, times may differ even on
|
||
similar hardware. On all tests, we used the default ``accurate=True``.
|
||
|
||
+----------------+------------------+------------------+
|
||
| Keysize (bits) | single process | eight processes |
|
||
+================+==================+==================+
|
||
| 128 | 0.01 sec. | 0.01 sec. |
|
||
+----------------+------------------+------------------+
|
||
| 256 | 0.03 sec. | 0.02 sec. |
|
||
+----------------+------------------+------------------+
|
||
| 384 | 0.09 sec. | 0.04 sec. |
|
||
+----------------+------------------+------------------+
|
||
| 512 | 0.11 sec. | 0.07 sec. |
|
||
+----------------+------------------+------------------+
|
||
| 1024 | 0.79 sec. | 0.30 sec. |
|
||
+----------------+------------------+------------------+
|
||
| 2048 | 6.55 sec. | 1.60 sec. |
|
||
+----------------+------------------+------------------+
|
||
| 3072 | 23.4 sec. | 7.14 sec. |
|
||
+----------------+------------------+------------------+
|
||
| 4096 | 72.0 sec. | 24.4 sec. |
|
||
+----------------+------------------+------------------+
|
||
|
||
If key generation is too slow for you, you could use OpenSSL to
|
||
generate them for you, then load them in your Python code. OpenSSL
|
||
generates a 4096-bit key in 3.5 seconds on the same machine as used
|
||
above. See :ref:`openssl` for more information.
|
||
|
||
|
||
Encryption and decryption
|
||
-------------------------
|
||
|
||
To encrypt or decrypt a message, use :py:func:`rsa.encrypt` resp.
|
||
:py:func:`rsa.decrypt`. Let's say that Alice wants to send a message
|
||
that only Bob can read.
|
||
|
||
#. Bob generates a keypair, and gives the public key to Alice. This is
|
||
done such that Alice knows for sure that the key is really Bob's
|
||
(for example by handing over a USB stick that contains the key).
|
||
|
||
>>> import rsa
|
||
>>> (bob_pub, bob_priv) = rsa.newkeys(512)
|
||
|
||
#. Alice writes a message, and encodes it in UTF-8. The RSA module
|
||
only operates on bytes, and not on strings, so this step is
|
||
necessary.
|
||
|
||
>>> message = 'hello Bob!'.encode('utf8')
|
||
|
||
#. Alice encrypts the message using Bob's public key, and sends the
|
||
encrypted message.
|
||
|
||
>>> import rsa
|
||
>>> crypto = rsa.encrypt(message, bob_pub)
|
||
|
||
#. Bob receives the message, and decrypts it with his private key.
|
||
|
||
>>> message = rsa.decrypt(crypto, bob_priv)
|
||
>>> print(message.decode('utf8'))
|
||
hello Bob!
|
||
|
||
Since Bob kept his private key *private*, Alice can be sure that he is
|
||
the only one who can read the message. Bob does *not* know for sure
|
||
that it was Alice that sent the message, since she didn't sign it.
|
||
|
||
|
||
RSA can only encrypt messages that are smaller than the key. A couple
|
||
of bytes are lost on random padding, and the rest is available for the
|
||
message itself. For example, a 512-bit key can encode a 53-byte
|
||
message (512 bit = 64 bytes, 11 bytes are used for random padding and
|
||
other stuff). See :ref:`bigfiles` for information on how to work with
|
||
larger files.
|
||
|
||
Altering the encrypted information will *likely* cause a
|
||
:py:class:`rsa.pkcs1.DecryptionError`. If you want to be *sure*, use
|
||
:py:func:`rsa.sign`.
|
||
|
||
>>> crypto = rsa.encrypt(b'hello', bob_pub)
|
||
>>> crypto = crypto[:-1] + b'X' # change the last byte
|
||
>>> rsa.decrypt(crypto, bob_priv)
|
||
Traceback (most recent call last):
|
||
...
|
||
rsa.pkcs1.DecryptionError: Decryption failed
|
||
|
||
|
||
.. warning::
|
||
|
||
Never display the stack trace of a
|
||
:py:class:`rsa.pkcs1.DecryptionError` exception. It shows where
|
||
in the code the exception occurred, and thus leaks information
|
||
about the key. It’s only a tiny bit of information, but every bit
|
||
makes cracking the keys easier.
|
||
|
||
Low-level operations
|
||
++++++++++++++++++++
|
||
|
||
The core RSA algorithm operates on large integers. These operations
|
||
are considered low-level and are supported by the
|
||
:py:func:`rsa.core.encrypt_int` and :py:func:`rsa.core.decrypt_int`
|
||
functions.
|
||
|
||
Signing and verification
|
||
------------------------
|
||
|
||
You can create a detached signature for a message using the
|
||
:py:func:`rsa.sign` function:
|
||
|
||
>>> (pubkey, privkey) = rsa.newkeys(512)
|
||
>>> message = 'Go left at the blue tree'.encode()
|
||
>>> signature = rsa.sign(message, privkey, 'SHA-1')
|
||
|
||
This hashes the message using SHA-1. Other hash methods are also
|
||
possible, check the :py:func:`rsa.sign` function documentation for
|
||
details. The hash is then signed with the private key.
|
||
|
||
It is possible to calculate the hash and signature in separate operations
|
||
(i.e for generating the hash on a client machine and then sign with a
|
||
private key on remote server). To hash a message use the :py:func:`rsa.compute_hash`
|
||
function and then use the :py:func:`rsa.sign_hash` function to sign the hash:
|
||
|
||
>>> message = 'Go left at the blue tree'.encode()
|
||
>>> hash = rsa.compute_hash(message, 'SHA-1')
|
||
>>> signature = rsa.sign_hash(hash, privkey, 'SHA-1')
|
||
|
||
In order to verify the signature, use the :py:func:`rsa.verify`
|
||
function. This function returns True if the verification is successful:
|
||
|
||
>>> message = 'Go left at the blue tree'.encode()
|
||
>>> rsa.verify(message, signature, pubkey)
|
||
True
|
||
|
||
Modify the message, and the signature is no longer valid and a
|
||
:py:class:`rsa.pkcs1.VerificationError` is thrown:
|
||
|
||
>>> message = 'Go right at the blue tree'.encode()
|
||
>>> rsa.verify(message, signature, pubkey)
|
||
Traceback (most recent call last):
|
||
File "<stdin>", line 1, in <module>
|
||
File "/home/sybren/workspace/python-rsa/rsa/pkcs1.py", line 289, in verify
|
||
raise VerificationError('Verification failed')
|
||
rsa.pkcs1.VerificationError: Verification failed
|
||
|
||
.. warning::
|
||
|
||
Never display the stack trace of a
|
||
:py:class:`rsa.pkcs1.VerificationError` exception. It shows where
|
||
in the code the exception occurred, and thus leaks information
|
||
about the key. It's only a tiny bit of information, but every bit
|
||
makes cracking the keys easier.
|
||
|
||
Instead of a message you can also call :py:func:`rsa.sign` and
|
||
:py:func:`rsa.verify` with a :py:class:`file`-like object. If the
|
||
message object has a ``read(int)`` method it is assumed to be a file.
|
||
In that case the file is hashed in 1024-byte blocks at the time.
|
||
|
||
>>> with open('somefile', 'rb') as msgfile:
|
||
... signature = rsa.sign(msgfile, privkey, 'SHA-1')
|
||
|
||
>>> with open('somefile', 'rb') as msgfile:
|
||
... rsa.verify(msgfile, signature, pubkey)
|
||
|
||
|
||
.. _bigfiles:
|
||
|
||
Working with big files
|
||
----------------------
|
||
|
||
RSA can only encrypt messages that are smaller than the key. A couple
|
||
of bytes are lost on random padding, and the rest is available for the
|
||
message itself. For example, a 512-bit key can encode a 53-byte
|
||
message (512 bit = 64 bytes, 11 bytes are used for random padding and
|
||
other stuff).
|
||
|
||
How it usually works
|
||
++++++++++++++++++++
|
||
|
||
The most common way to use RSA with larger files uses a block cypher
|
||
like AES or DES3 to encrypt the file with a random key, then encrypt
|
||
the random key with RSA. You would send the encrypted file along with
|
||
the encrypted key to the recipient. The complete flow is:
|
||
|
||
#. Generate a random key
|
||
|
||
>>> import rsa.randnum
|
||
>>> aes_key = rsa.randnum.read_random_bits(128)
|
||
|
||
#. Use that key to encrypt the file with AES.
|
||
#. :py:func:`Encrypt <rsa.encrypt>` the AES key with RSA
|
||
|
||
>>> encrypted_aes_key = rsa.encrypt(aes_key, public_rsa_key)
|
||
|
||
#. Send the encrypted file together with ``encrypted_aes_key``
|
||
#. The recipient now reverses this process to obtain the encrypted
|
||
file.
|
||
|
||
.. note::
|
||
|
||
The Python-RSA module does not contain functionality to do the AES
|
||
encryption for you.
|
||
|
||
Only using Python-RSA: the VARBLOCK format
|
||
++++++++++++++++++++++++++++++++++++++++++
|
||
|
||
.. warning::
|
||
|
||
The VARBLOCK format is NOT recommended for general use, has been deprecated since
|
||
Python-RSA 3.4, and has been removed in version 4.0. It's vulnerable to a
|
||
number of attacks:
|
||
|
||
1. decrypt/encrypt_bigfile() does not implement `Authenticated encryption`_ nor
|
||
uses MACs to verify messages before decrypting public key encrypted messages.
|
||
|
||
2. decrypt/encrypt_bigfile() does not use hybrid encryption (it uses plain RSA)
|
||
and has no method for chaining, so block reordering is possible.
|
||
|
||
See `issue #19 on GitHub`_ for more information.
|
||
|
||
.. _Authenticated encryption: https://en.wikipedia.org/wiki/Authenticated_encryption
|
||
.. _issue #19 on GitHub: https://github.com/sybrenstuvel/python-rsa/issues/13
|
||
|
||
As of Python-RSA version 4.0, the VARBLOCK format has been removed from the
|
||
library. For now, this section is kept here to document the issues with that
|
||
format, and ensure we don't do something like that again.
|