Intro
Recently I was having a little trouble implementing encryption due to chunk size, and then getting proper padding so I figured I would share a simplified example using Python. Bonus steganography included because why not?
Explanation and Definitions
Encryption: The process of encoding a message so that it can be read only by the sender and the intended recipient. Encryption systems often use two keys, a public key, available to anyone, and a private key that allows only the recipient to decode the message.
In this example we will be using Public Key cryptography based off a generate 2048 bit RSA key, that is then wrapped in a PKCS1_OAEP cipher that is hashed with SHA-2, specifically SHA-256. Simply put RSA isnt considered entirely secure and neither is SHA1, so we are wrapping our RSA in PKCS1_OAEP and modifying the hash that PKCS1_OAEP will be using from SHA-1 to SHA-2 standards. Additionally I couldve taken things a step further and encrypted our key with DES using a passphrase however for demonstrative purposes the way we have things now shall suffice.
Since I only have a limited understanding of cryptography Ill be the first to say my implementation may not be perfect or the strongest out there but for basic purposes it should get the job done. I had immense difficulty finding resources or sample code on encrypting large strings using RSA + PKCS1_OAEP + SHA256 from PyCrypto so it felt good to finally get something working.
- Steganography - the practice of concealing messages or information within other nonsecret text or data.
- RSA- "is the most widespread and used public key algorithm. Its security is based on the difficulty of factoring large integers. The algorithm has withstood attacks for 30 years, and it is therefore considered reasonably secure for new designs." Source by dlitz.net, PyCrypto documentation
- PKCS1_OAEP - Public Key Cryoptography Standard 1 - Optional Asymmetric Encryption Padding
- SHA - Secure Hash Algorithm. A cryptographic hash function that is a mathmatical operation ran on digital data.
Requirements
In order to get our program running we will first need the help of two libraries. The two libraries that I had to install using pip were Stepic and Pycrypto. Stepic is what we will use for our steganography, and pycrypto will handle our encryption implementation
Generating our Keys
We are going to go ahead and generate our public and private RSA keys to be used for this project. Note its probably not best practice to dump them into a txt file lest someone happen upon them but for learning purposes it should be fine.
2048 is considered secure by the documentation, and PEM will be our output format for readability purposes. This simple script dumps the keys into seperate text files so we can call them later on. Really it should be fairly self explanatory.
|
|
Encrypting our data
Heres where we really get to the important part, trying to implement some encryption. Now lets explain how we are going to do this.
First we create a new SHA256 hash; then we read in our pubkey.txt as key, next we import the public key using the RSA library, finally we wrap the RSA with the cipher PKCS1_OAEP and the SHA256 hash. The reason we do all this is because RSA by itself is not considered secure so we want to use PKCS1_OAEP to apply a cipher and pad it. The purpose of the SHA256 hash is that by default the pycrypto implementation of PKCS1_OAEP uses SHA1 as its hash, which is also considered insecure. Also note we could use our private key but lets not since we wont be sharing that one. Simply put, not only do we want encryption but we also want padding and a strong hash. Keep in mind, we can of course always implement strong encryption using bigger keys or a salted hash.
Then to create a dummy string I have declared a large block of As, Bs, Cs, and Ds and concatenated them together for a total length of 3000. Now if we were using just RSA we would be encrypting at a chunk size of 256 as thats our RSA modulus of 2048, however since we are using PKCS1_OAEP as our wrapper our chunk size is different. The documentation lays it out pretty simple, Chunk Size = (RSA modulus - 2) -(2 * (hash digest size))
. A quick check for the hash digest size of SHA256 in the documentation yields 32 and in the end our chunk size will be 190. So we use our splitter function to break the string into chunks of size 190, then we encrypt those chunks.
Following this to reduce length and also obscure the underlying encryption we use the zlib library and base64 libraries to apply compression and encoding. So zlib.compress to compress our string and base64.b64encode to encode it. We dont need to do this but if youre going about this for your own use you may find it useful in making it a bit harder to discern how you went about implementing your encryption. Fantastic so at this point were ready to take our string and send it to whomever has the private key, but why stop there.
During all my research I saw a multitude of articles also discussing steganography, the act of hiding information in plain sight. So as an added bonus lets take our encryption a step further and throw it into a cat picture. Specifically this cat picture, I used a PNG as it is a lossless format and will retain our information. Stepic makes shoving our information trivial and you can find the annotated lines towards the end of the code.
Now that we have got a basic understanding of what we are implementing and why, heres the code.
|
|
This is before we add our encryption, its a large 20.5mb picture so its a bit unwieldy for most regular uses. Sorry if the massive cat picture makes your browser lag a bit.
Heres our final product. As our encryption came out at about 5747, the cat picture is now of size 21.1mb. This is achieved with a hardly noticable difference in our picture.
This picture no longer carries the encoding
Decrypting our payload
Since weve laid out all of our groundwork for generating keys and encrypting, we ought to be able to fly through decrypting. Were going to open the picture using the stepic library again and decode it. Then we undo our base64 encoding and zlib compression. After that we split our string into chunks of 256, its not 190 I know but thats what the documentation says the PKCS1_OAEP.decrypt function takes for size and it works. After that we print our decrypted text and the length of it to verify we got everything back. I will drop the annotated code and output below.
|
|
If we run this program on our cat picture our final output comes out as:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
3000