Python & RSA algorithm

Cryptography python library was born with the goal of being our "cryptographic standard library".

There are Python libraries that provide cryptography services: M2Crypto, PyCrypto, pyOpenSSL, python-nss, and Botan's Python bindings. Five criteria can be evaluated when you try to select one of them: which C backend, how well maintained, Python support, reviewed and completeness. All failed the "reviewed" category. e.g. PyCrypto (probably the most used cryptographic library for Python) doesn’t work on PyPy.

The hope behind this new library is that it will become the cryptographic standard for Python. It will incorporate modern algorithms (e.g. AES-GCM and forward security) that have been "important for a long time” but have been ignored in alternative libraries. Cryptography will be thoroughly tested and come with "sane defaults" and a "sane API". It will support Python 2.x, Python 3.x, and PyPy. It will be well-maintained and, unlike most of the alternatives, will not have known broken options.

Cryptography core developers set out to design a better library, with the following principles:

It should never be easier to do the wrong thing than it is to do the right thing.

You shouldn't need to be a cryptography expert to use it, the documentation should equip you to make the right decisions.

Things which are dangerous should be obviously dangerous, not subtly dangerous.

Put our users' safety and security above all else.

Now, we are going to play a little bit with this library and specially with the RSA asymmetric algorithm. In Brainattica, we are building a project related to security in which we are using this asymmetric algorithm.

Firstly, you can install Cryptography with pip:

$ pip install cryptography

For Debian and Ubuntu, the following command will ensure that the required dependencies are installed:

$ sudo apt-get install build-essential libssl-dev libffi-dev python-dev

You should ensure OpenSSl and libssl are the latest versions as well.

When implementing the flow of sending messages, you’ll need to follow careful cryptographic procedures to ensure everything will work properly.

If Myriam wants to send a secret message to Alistair:

  1. Myriam and Alistair must have their RSA key pair with private and public keys:

    from cryptography.hazmat.backends import default_backend
    from cryptography.hazmat.primitives.asymmetric import rsa
    
    
    myriam_private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
        backend=default_backend()
    )
    myriam_public_key = myriam_private_key.public_key()
    
    
    alistair_private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
        backend=default_backend()
    )
    alistair_public_key = alistair_private_key.public_key()
    
  2. Firstly she should encrypt her message with Alistair’s public key to send it encrypted:

    from cryptography.hazmat.primitives.asymmetric import padding
    from cryptography.hazmat.primitives import hashes
    import base64
    
    
    message = "the code must be like a piece of music"
    message_bytes = bytes(message, encoding='utf8') if not isinstance(message, bytes) else message
    
    
    ciphertext = alistair_public_key.encrypt(
        message_bytes,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA1()),
            algorithm=hashes.SHA1(),
            label=None
        )
    )
    ciphertext  = str(base64.b64encode(ciphertext), encoding='utf-8')
    

    OAEP is the recommended padding choice for encryption for new protocols or applications. PKCS1v15 should only be used to support legacy protocols.

  3. If you have a RSA public key in the PEM format (which are recognizable by the distinctive -----BEGIN {format}----- and -----END {format}----- markers) within "publickeypem_export" var, you can load it:

    from cryptography.hazmat.primitives.serialization import load_pem_public_key
    from cryptography.hazmat.backends import default_backend
    
    
    public_key_pem_export = (bytes(public_key_pem_export, encoding='utf8') if not isinstance(public_key_pem_export, bytes) else public_key_pem_export)
    
    
    public_key_pem_loaded = load_pem_public_key(data=public_key_pem_export, backend=default_backend())
    
  4. Next Myriam should sign your message with her private key to ensure the recipient can check the message sender with Myriam’s public key to confirm the sender is Myriam:

    from cryptography.hazmat.primitives import hashes
    from cryptography.hazmat.primitives.asymmetric import padding
    import base64
    
    
    message = "the code must be like a piece of music"
    data_to_sign = bytes(message, encoding='utf8') if not isinstance(message, bytes) else message
    
    
    signer = Myriam_private_key.signer(
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    signer.update(data_to_sign)
    signature = str(base64.b64encode(signer.finalize()), encoding='utf8')
    

    PSS algorithm is the recommended padding choice for any new protocols or applications, PKCS1v15 should only be used to support legacy protocols.

  5. Myriam has all necessary parts to send her secret message to Alistair now:

    [ciphertext, signature]

  6. When Alistair receives Myriam’s message the first thing he'll do is to decrypt the message:

    ciphertext_decoded = base64.b64decode(ciphertext) if not isinstance(ciphertext, bytes) else ciphertext
    
    
    plain_text = alistair_private_key.decrypt(
        ciphertext_decoded,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA1()),
            algorithm=hashes.SHA1(),
            label=None
        )
    )
    plain_text = str(plain_text, encoding='utf8')
    
  7. Last resort, Alistair should check the message origin to determine it is Myriam:

    try:
        plain_text_bytes = bytes(plain_text, encoding='utf8') if not isinstance(plain_text, bytes) else plain_text
        signature = base64.b64decode(signature) if not isinstance(signature, bytes) else signature
        verifier = myriam_public_key.verifier(
            signature,
            padding.PSS(
                mgf=padding.MGF1(hashes.SHA256()),
                salt_length=padding.PSS.MAX_LENGTH
            ),
            hashes.SHA256()
        )
        verifier.update(plain_text_bytes)
        verifier.verify()
        return true
    except InvalidSignature:
        return False
    

Finally, Alistair has got his message sent by Myriam and he could check the message is authentic. Alistair isn’t able to hide his joy!

All of the major cryptographic libraries are often written in C (or in C++). That's because C allows low-level control that managed languages can't provide. In the upcoming posts we will publish how we can manage cryptography operations with GO.

We've created a repository where you can find all the code about this complete sample in https://github.com/brainattica/python-cryptography-sample.

If you want to take a close look at working with RSA in Android, you can do it here http://blog.brainattica.com/working-with-rsa-in-android/

Have fun!

comments powered by Disqus