Cryptography is the science of hiding information from prying eyes. A lot of people think that cryptography is a fairly new science, when in truth it has been around for a long time. As long as people have needed to send secret messages, people have needed cryptography. For example, Julius Caesar used a very simple cipher of shifting letters 19 characters to the right that eventually became known as the Caesar cipher. Encrypted information is no good to anyone if the intended recipient of the data doesn't know how to decrypt it. The balancing act of many cryptographic problems is that the sender of encrypted information needs to be able to allow the recipient to decrypt the data without allowing anyone else who might intercept the message to decrypt it as well.
This section discusses the various types of cryptographic tasks that are commonly performed. There is a mythos surrounding much of the science of cryptography that often intimidates developers into developing less secure applications. Hopefully this section will illustrate that you don't need a degree in applied mathematics to utilize the cryptographic tools available to you in the .NET Framework. The next few sections discuss different types of encryption. In a real-world scenario, the different types of encryption are often used in conjunction with each other to maximize data integrity.
Secret-key encryption is a symmetric form of encryption. When using secret-key encryption, the same key that is used to encrypt the data is used to decrypt the data. This type of encryption is the fastest form of encryption, and is extremely efficient at encrypting large streams of data such as files, documents, long conversations, and so on.
Secret-key algorithms are also referred to as block ciphers. This is because the encryption is done one block at a time. The algorithm determines the size of the block (8, 16, 24, or 32 bytes). Block ciphers can potentially be reversed, however, because a block cipher will encrypt the same block of data the same way every time. So, for example, the same unencrypted block of 8 bytes will produce the same encrypted output every time. This consistency in output can allow a potential hacker to determine the encryption key. This is resolved through the use of an initialization vector (IV). The IV is used to help encrypt the first block of the stream, and then a portion of the first block is used to encrypt the second. This blending of blocks will prevent the same 8-byte block from creating predictable encryption results because each time that block is encrypted, it is being encrypted with a portion of the preceding block in the stream.
The downside to secret-key encryption is that both parties need to have access to the secret key.
Public-key encryption is an asymmetric encryption algorithm because the key used to encrypt the data is not the key used to decrypt the data. To exchange data between parties using public-key encryption, the sender will encrypt the data with the receiver's public key. Then the receiver can decrypt the data with her private key. The reverse also applies for sending a message in the other direction. In this way, the public key can be transmitted over any channel, regardless of how secure the channel might be.
Take a look at Figure 15.1. It shows how Joe's public key is used to encrypt messages sent to Joe, and Jane's public key is used to encrypt messages sent to Jane.
Figure 15.1. Public-key encryption.
The problem here is that public-key encryption is fairly slow, and is optimized for encrypting and decrypting small amounts of data. It doesn't work on large streams of data the way block ciphers (secret-key encryption) work, so using public-key encryption to send message after message would be impractical. A real-world scenario would involve Joe using Public Key Cryptography Standards (PKCS) to send Jane a shared secret that was generated for just that one conversation. Jane could then use her private key to decrypt the shared secret. When both parties have the shared secret, they can converse securely and efficiently without fear of anyone intercepting and decrypting their messages. This scenario is illustrated in the sequence diagram shown in Figure 15.2.
Figure 15.2. Real-world scenario combining PKCS with secret-key encryption.
A hash is a reduced representation of a large amount of binary data. When a set of data is hashed, it is reduced to a fixed-length set of bytes. The benefit of a hash is that, when using an appropriate hashing algorithm, it is statistically impossible to have two sources of data produce the same hash value. This means that for any arbitrarily long set of data, there is only one unique hash value that represents that set of data.
You can detect if data has been tampered with using hashes. If you send someone a message as well as the hash of that message, she can then hash that message on her end using the same hashing algorithm, and compare it against the transmitted hash. If the two hash values are identical, you can be assured that the message has not been tampered with. However, if the hashes don't match, the message has been modified since it was transmitted.
This isn't all that secure. If someone intercepts the original message, they could just as easily rehash the altered message and send it along to the recipient who will validate the message as unmodified. In reality, the hash is usually encrypted using PKCS such that only the recipient can decrypt the hash using his private key. This prevents the message (and the hash) from being tampered with.
The encryption of hashes leads me to another extremely common task in cryptography: digital signatures.
Forgery aside, a human signature is evidence that a document was signed by the person who claims to be the originator. So, when the document ends with "Sincerely, Kevin Hoffman," and is followed by my signature, that is reasonable proof that I am indeed the one who wrote the document.
The same can be said of an electronic document or message. You can "sign" that message with a digital signature that serves as proof that you are the one who created that document.
When you digitally sign a message, the first thing you do is create a hash of the message. This hash is then encrypted using your private key. This allows anyone with your public key to decrypt the hash, rehash the message on their machine, and compare the two to validate the authenticity of the signature. You encrypt the hash using your private key because you do not want to restrict the list of people who can verify the authenticity of your signature. Therefore, anyone with your public key should be able to verify that a message came from you. Likewise, you should be able to verify the authenticity of any messages sent by people for whom you have public keys.