Hazmat RSA Encryption
[Hazmat Home][Home]
With public-key encryption, we create a key pair: a public key and a private key. If Alice is sending data to Bob, she can add her digital signature, and which will prove that she is the sender and also verify that the data has not been changed. She does this by signing the data with her private key, and then Bob can prove the signature with Alice's public key. In this example, we will use RSA keys to sign a message, and then verify the correct signature, but verify that an incorrect signature will fail
|
Theory
At the core of digital trust is the usage of digital signatures. With this, we can verify the creator of the data, and also that it has not been modified. We do this using public-key encryption, and in this article, we will look at how we can use the hazmat (Hazardous Material) primitives in the Python cryptography library.
With public-key encryption, we create a key pair: a public key and a private key. If Alice is sending data to Bob, she can add her digital signature, and which will prove that she is the sender and also verify that the data has not been changed. She does this by signing the data with her private key, and then Bob can prove the signature with Alice's public key. In this example, we will use RSA keys to sign a message, and then verify the correct signature, but verify that an incorrect signature will fail.
To generate a key we can simply add:
private_key = rsa.generate_private_key(public_exponent=65537,key_size=size)
and where size is the number of bits in the public modulus (N), and the public_exponent is typically the value of 65,537. This will then generate a private key and a public key. To extract the public key we can use:
pub = private_key.public_key()
To create a signature from a message we use our private key to sign it, aloong with a salt value and a given hash type (SHA-256):
signature = private_key.sign(message,padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),hashes.SHA256())
To verify we use the associated public key, and generate an exception if it is incorrect:
try: rtn=pub.verify(signature,message,padding.PSS(mgf=padding.MGF1(hashes.SHA256()),salt_length=padding.PSS.MAX_LENGTH),hashes.SHA256()) except exceptions.InvalidSignature: print("A bad signature failed") else: print("Good signature verified")
Coding
The following is the code to generate a good signature and a bad one:
from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives import hashes import binascii import sys size=1024 message = b"Hello world" if (len(sys.argv)>1): message=str(sys.argv[1]).encode() if (len(sys.argv)>2): size=int(sys.argv[2]) print("Message: ",message) print("Key size: ",size) try: private_key = rsa.generate_private_key(public_exponent=65537,key_size=size) pub = private_key.public_key() ciphertext = pub.encrypt(message,padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),algorithm=hashes.SHA256(),label=None)) print ("\nCiphertext:\n",binascii.b2a_hex(ciphertext).decode()) plaintext = private_key.decrypt(ciphertext,padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),algorithm=hashes.SHA256(),label=None)) print("\nDecrypted Message: ",plaintext.decode()) print("\n=== Private Key PEM format ===") pem = private_key.private_bytes(encoding=serialization.Encoding.PEM,format=serialization.PrivateFormat.PKCS8,encryption_algorithm=serialization.NoEncryption()) print ("\nPrivate key (PEM):\n",pem.decode()) pem = pub.public_bytes(encoding=serialization.Encoding.PEM,format=serialization.PublicFormat.SubjectPublicKeyInfo).decode("utf-8") print("\n=== Public Key format ===") print ("Public key (PEM):\n",pem) except Exception as e: print(e)
A sample run for a key size of 1,204 bit encryption keys is:
Message: b'Hello world' Key size: 1024 Ciphertext: e79ebd3f8ce2ae3f0bfe7d08ec484dda2d7aa1b8206f6d1a3b2aa57bcc0f9bf260a1474f55b794c69aa297874336d6574c4d43867768c9576c20a3b712f9dbe695d10b6eb322a4dc950c6b29fb0f16dc23f51ce4882aeb2899eb9e7f418a02677c17a4f344ba9c56ad095e1a704642a877e7f504db352530ce59c81906fbd773 Decrypted Message: Hello world === Private Key PEM format === Private key (PEM): -----BEGIN PRIVATE KEY----- MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOuIIWDQJ3lsA2oA dzteHvoEo/fJh/kjIYx6iWrcnsXze4tPXzwYnXI7VjUtes8rWRLeGEBdxlFTdYKv xVl1aL/o74FimJEtR5TkqMoWAQSSPVEk5HG5yQ3JReSIiutggrnOA7Q0Pvf62TAr TLcsn2aLpxeL4PkJtAdLBQkec85xAgMBAAECgYA++bgK/b0APzwudI9sxT69FM6O q0j6+US4He9uLi0Zun93ATLyi5ZVe74SG+XxTKLVY6LSWwBysEEEP2EiCT2HRzxi ShtzN/ITkSJ/ibEtPEm6UpB/zVi+t4/VIU5L7bpQgCR/Cys5fLptVOWV4DxVXD4r ICM5gS9xyQeMRn5XrQJBAPgd74CtayW0Iq04KrOM1psFVjRky0EAUyU2r4IFvZWA E6FJzLVTIdSHl4eA7dxfH3v4sg2YrHoM2jhr+FjX0CcCQQDzA9VA9NCww0UOUEhA Y5Hbl8eRkc3rW35uYGVZ6zyMUWenj9OaS5djdc/fhTY6nI5nriwD+3WBVirjkMNP +vOnAkA6WyAXjbIjvghh4VH5xOg8sbaiwAomEaN8ny4MULlUTjlPuAHTq+fLCtES 57oKGL1Y9Rc7p2r5kchJeOuCSdAFAkEAtCb7SJQ0wVWBNANADLvDvU7fm4LDbN7+ 80Ry3h4NXNhcR1PQQmoXaJxF5iYS9ZBp4WK1Lp6TlbYTRXBj2j7nsQJBAL/8qGYn +8AJK2oUlyAD/9BU/czZKTZtzp/9ACvOqC3Bs9IH4kk3w0pdnofLbaY+JQJS2Mcu y1wsmaloS6qkFxc= -----END PRIVATE KEY----- === Public Key format === Public key (PEM): -----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDriCFg0Cd5bANqAHc7Xh76BKP3 yYf5IyGMeolq3J7F83uLT188GJ1yO1Y1LXrPK1kS3hhAXcZRU3WCr8VZdWi/6O+B YpiRLUeU5KjKFgEEkj1RJORxuckNyUXkiIrrYIK5zgO0ND73+tkwK0y3LJ9mi6cX i+D5CbQHSwUJHnPOcQIDAQAB -----END PUBLIC KEY-----
Note that we normally use RSA keys of 2,048 bits and above, as the state-of-the-art in factorizing the modulus is approaching 1,024 bits.