Welcome to Part 3! A quick recap of where we’ve been. In Part 1: Buzzwords and Hash Function we talked about some foundational cryptography vocab and were introduced to hash functions, how they’re used, and some drawbacks. In Part 2: Symmetric Ciphers we upped the complexity a bit and discussed symmetric ciphers, including important properties of keys and different modes that help us avoid leakage. Making great progress! In this section, we are going to ease more into crypto with asymmetric ciphers. Ready?
As always, we are going to break this down into bite-sized chunks. But this one is a little more complex, so our sections will be a little different.
Let’s define a few key terms that may come in handy:
- Symmetric Ciphers (as discussed in Part 2) are a family of ciphers that uses the same key to encrypt as it does to decrypt. These are sometimes referred to as secret key algorithms, because, if the key is the same on both sides, it needs to be kept secret so that not just anyone can decrypt it.
- Asymmetric Ciphers are a family of ciphers that uses a different key to encrypt than it does to decrypt. These are sometimes referred to as public key algorithms, because, when the encrypting and decrypting keys are different, that allows for one to be public without compromising the correctness or privacy of the decrypted message.
- A Key Pair refers to the mathematically related encryption and decryption keys, the public and private keys, in asymmetric cryptography.
We will revisit these terms as we go, so don’t worry about memorizing! They will begin to feel familiar.
What are asymmetric ciphers?
Thinking back to Part 2, we can recall that a symmetric cipher is an algorithm that allows us to encrypt and decrypt information using the same key for both operations. Conversely, an asymmetric cipher is a cryptographic algorithm that uses a different key to encrypt than it uses to decrypt. Thus, we have a key pair, because the encryption and decryption keys come in pairs, as they have to be mathematically related to work. The pair consists of a public and private portion. The private key needs to be kept secret by the owner, but it’s counterpart, the public key, can be shared in the clear.
Wait… shared in the clear? Yes, you read that right. This is just fine. Although the keys are required to be mathematically related, you cannot derive the private key if you have the public key.
Similar to hashes and symmetric ciphers, there are multiple asymmetric cryptographic algorithms. A few examples include RSA (Rivest-Shamir-Adleman), DH (Diffie-Hellman), and ECC (Elliptic Curve Cryptography). In our examples we are going to stick with RSA.
First let’s create a key pair and take a peek at what the keys look like!
In your *nix based terminal, we will use openssl to generate an RSA key. Here is an article that includes how to generate an RSA key on a Windows system. Here we go!
$ openssl genrsa -out key.pem 2048
In the command above, we are using openssl to generate a 2048-bit RSA key that it will output to a file called ‘key.pem’. ‘PEM’ is just a data format. No need to worry too much about that right now.
After typing the above command and hitting enter, you’ll see some output while it does the key generation. What is it doing exactly? Well, asymmetric key pairs are basically just very large numbers. Exactly how these numbers are determined is up to the specific algorithm. For example, RSA is based on how hard it is to factor the product of two large prime numbers. So, the output that you see is being spit out while the algorithm crunches some numbers.
Once it is finished, let’s look at the file. You can do this in the terminal however you are comfortable, I’m just going to use ‘cat’.
$ cat key.pem
You’ll see that the data in that file begins with ‘—–BEGIN RSA PRIVATE KEY——’ and then a bunch of key data followed by ‘—–END RSA PRIVATE KEY——‘. This is the file that is very important to keep protected. The compromise of a private key means that anyone who has access to it can now decrypt data that was encrypted with the corresponding public key.
Okay, but where is our corresponding public key? When we generate our RSA key using this method, we have to extract the public key from our ‘key.pem’ private key file. Remember how we said the two keys were mathematically related? Well we can actually derive the public key from the private key data once we have it. So let’s do that:
$ openssl rsa -in key.pem -outform PEM -pubout -out public.pem
In the command above, we are again using openssl’s RSA utilities, but this time we are reading in the key.pem private key data and outputting the public key to a PEM file called ‘public.pem’. Let’s take a look at that one, too!
$ cat public.pem
When we output the public key, we should see that the file begins with ‘—–BEGIN PUBLIC KEY——‘, has some key data in the middle, and ends with ‘—–END PUBLIC KEY——‘. Now our public and private keys are in their own separate files, and we can begin to use them for encryption and decryption!
What do we do with this now?
Once we have the keys in this format, the natural usage would be to distribute the public key and to store the private key somewhere safe. This allows anyone who has access to the public key to use it to encrypt something. They then can send the encrypted content to us, and we can decrypt it with the corresponding private key. This is one typical use case of asymmetric cryptography: the public key is used to publicly encrypt some data, and then the owner of the corresponding private key is the only one who is able to decrypt the data.
To see an example of encrypting and decrypting with our keys in some code, we are going to continue using our trusty friend, pycrypto! Let’s write another script. This one is going to read in our public key and use it to encrypt some data. As always, this will sink in a little more effectively if you type these things out yourself as opposed to copy+pasting. Here we go!
A quick note… I am calling this script ‘rsa.py’ and saving it into the same directory where my ‘key.pem’ and ‘public.pem’ are saved. This allows everything to import as expected without having to include code for directory traversal.
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
# an arbitrary message to encrypt... you can make this
# whatever you want!
data_to_encrypt = b'Here is some data to encrypt with the public key.'
# import the public key we just made
public_key_file = open("public.pem", "r")
public_key = RSA.importKey(public_key_file.read())
# don't worry too much about this, it simply helps us
# with proper padding of the RSA encryption
rsa_enc_obj = PKCS1_OAEP.new(public_key)
# encrypt and print!
encrypted = rsa_enc_obj.encrypt(data_to_encrypt)
print("Encrypted data: ")
From the top: we need to import the RSA module and the PKCS1_OAEP module from our installed packages in pycrypto. Next, we pick some arbitrary data to encrypt and save it to a variable. Now, we need to read in the public key data that we generated and saved to a file called ‘public.pem’. Then, we create an RSA object with that public key (and a padding standard… don’t worry too much about this). Finally, we can encrypt the message and print out the jumbled data! Remember, we can run this by saving the file as ‘rsa.py’ and typing ‘python rsa.py’ into the terminal. If all went well, this should print out some unreadable text… yay! This means it is encrypted and regular onlookers won’t be able to understand a darned thing.
Okay, so this is pretty cool. We just used a public key to encrypt some data. Now, only the private key holder can decrypt it. In this case, that also happens to be us. So let’s pretend that someone wanted to send us a private message meant for our eyes only, and we received a file that we now want to decrypt. Let’s give that a shot, too. At the end of our existing script, lets add on:
# import the private key we just made
private_key_file = open("key.pem", "r")
private_key = RSA.importKey(private_key_file.read())
# create an rsa object with our key and pkcs padding...
rsa_dec_obj = PKCS1_OAEP.new(private_key)
# decrypt and print!
decrypted = rsa_dec_obj.decrypt(encrypted)
print("Decrypted data: " + str(decrypted))
From the top… I just added an extra print statement to give us a little space between our encrypted data and the output that will come from our decrypted data. Then we read in the private key that we generated earlier and had saved into a file called ‘key.pem’. Once we have imported that key, we create an RSA object with it. Finally, we use that RSA object to decrypt the data we had previously encrypted (yet pretending it came from some other secret agent). Again, let’s run this by typing ‘python rsa.py’ into the terminal. If everything went well, we will see our encrypted data which still exists in our script, then we will see our message properly decrypted at the end!
Just for fun, let’s do a quick experiment to show that the decryption only works with the exact corresponding private key. Let’s copy our private key file and save the copy as ‘corrupt.pem’. Next, let’s go into the file and corrupt it… leave the header and footer, but corrupt some of the data in the middle. To keep it simple, let’s just replace one letter with another. I found a single ‘K’ in the third line of my key and replaced it with ‘L’ instead. I used vim because I’m comfortable with it but use whatever text editor you like!
$ cp key.pem corrupt.pem
$ vim corrupt.pem
Now, let’s change our script to use ‘corrupt.pem’ instead of ‘key.pem’ to perform our decryption. So change:
private_key_file = open("key.pem", “r")
private_key_file = open("corrupt.pem", "r")
Now run the script again by typing ‘python rsa.py’ (or up-arrowing). You should see that python throws an error because it can’t properly decrypt. Even this small change makes it so that the data is unreadable. Again, this points to the importance of keeping your private key protected, so that it can’t get corrupted. However, and most importantly, it is also so that no one else can get their hands on it!
The Good and the Bad
Let’s circle back and remember why this is really cool. With symmetric cryptography, we somehow have to solve the problem of securely sharing a key with someone who we may have never met. With asymmetric cryptography, we don’t have to solve that problem, because the public key can be broadcasted.
Great! But it does come with a major drawback. Asymmetric cryptography algorithms are much, much slower than symmetric algorithms. However, we often are able to combine symmetric and asymmetric cryptography for use in protocols, so we can reap the benefits of both!
What is an example of this hybrid use case?
One of the most prominent examples of combining asymmetric and symmetric cryptography for use in the real world is Transport Layer Security (TLS). TLS is the improvement upon its predecessor protocol, Secure Sockets Layers (SSL). It is designed to provide a secure communication channel between two parties. Initially, it leverages asymmetric cryptography in order to verify identity of the other party as well as to agree on a secret key and share it. Then, it is able to switch to symmetric cryptography, encrypting things with the key that was negotiated during the asymmetric portion. This is really clever, because the asymmetric algorithm allows us to encrypt and decrypt without having to have met before, then we can share a secret key for use with a symmetric cipher and switch to that since it is so much faster! If you’re interested in learning more about applications of asymmetric cryptography, consider looking into certificates, digital signatures, or SSH.
In the end…
Let’s wrap this up! We have talked about how asymmetric ciphers are different from symmetric ciphers, what a key pair is, and a couple of real-world applications. We also got our hands dirty with actual key generation and some python code. In Part 2, I assigned some extra credit homework. Now it’s your turn. Without guidance, try playing with what you learned to get a better understanding and to sink things deeper into your long term memory.
Asymmetric cryptography has a lot of depth to it, but, remember, one bite-sized chunk at a time! Thanks for sticking with me. And moving into the future, you can look forward to a deep-dive into TLS!
Credit – “Asymmetric Cyphers” inspired by The Matrix (Warner Bros.)
Ellie Daw is passionate about diversity in tech and giving back, innovation and experimentation, and bringing security and good user experience together. She loves cryptography, hence her handle @cryptoreo, and has been heavily involved in teaching cryptography to professionals as well as developing cryptography workshops for youth cybersecurity education initiatives. On the technical side, Ellie has spent the last couple of years working as a software engineer on crypto and network protocol libraries. She believes there is always more for her to learn, but understands the importance of letting loose, too! In her free time you can find her exploring new places and trying new foods, or dabbling in any active or creative pastime!Tags: ciphercryptodawhighlightpythontutorial