What is Cryptography?
Cryptography is the science of creating secrets. The word Cryptography comes from the word crypto and graphy roughly translating to secret writing. In order to make information secret, you use a cipher, an algorithm that converts plain text into ciphertext, which is gibberish unless you have a key that lets you undo the cipher. The process of making the text secret is called encryption and the reverse process is called decryption.
Cryptography can take some useful bytes of data then scramble them with an algorithm making it nearly impossible for a computer to understand its true meaning without the proper key credentials.
Brief History of Cryptography
Ciphers have been used long before computers showed up. Julius Caesar used what's now called a Caesar Cipher to encrypt private correspondence. He would shift the letters in the message forward by 3 letters. So A
became D
and the word Crypto
became FUBSWR
. To decipher the message recipients had to know both the algorithm and the number to shift by which acted as the key.
Now fast forward to the 21st century and all the algorithms you are using today are made obsolete by Super Quantum Computers that can crack them with brute force. Cryptography is always evolving, what is considered safe and secure today may not be the case tomorrow. It is absolutely essential to understand how certain key concepts work that we'll learn through this blog.
Hashing Function
The word Hash essentially means Mix. Let's understand the term through an analogy:
Imagine a new deck of cards. You write a step-by-step procedure for shuffling them. The end result is a mixed up deck of cards. If you followed the same procedure for every new same deck of cards you would get the same result.
A hash function is like shuffling a deck of cards except you start with input and then pass it off to a hashing function, this function returns a fixed length value of what looks like alphanumeric gibberish.
The important thing here is that the same input will produce the same output just like the deck of cards. However, it is very difficult for the computer to reverse engineer the hash and find out what the original message was. Hence, it is used by developers to store passwords and secret data.
Implementing this in Go:
package main
import (
"fmt"
"crypto/sha256"
)
func main() {
password := "Password123"
algorithm := sha256.Sum256([]byte(password))
fmt.Printf("Hashed Password: %x\n", algorithm)
}
Here the password is Password123
and the algorithm we used is SHA-256
.
You can run this code here
Salting
The fact that hashing function always returns the same value is also a problem when it comes to secret data. If a hacker obtains the database and the password is hashed, they can often just go through a rainbow table that has a bunch of precomputed hashes and find a bunch of commonly used passwords. Salt is just a random value added to the hash making it much more difficult to decrypt.
package main
import (
"encoding/base64"
"fmt"
"golang.org/x/crypto/scrypt"
)
func main() {
salt := []byte{0xc8}
password, _ := scrypt.Key([]byte("Password123"), salt, 1<<15, 8, 1, 32)
fmt.Println(base64.StdEncoding.EncodeToString(password))
}
Run code here
Symmetric Encryption
Before learning about Symmetric Encryption, let's understand what is Encryption. With encryption, we take in a message, scramble the bytes to make it unreadable then provide a key or password allowing somebody else to decrypt it.
Symmetric Encryption means there's a shared password that both the sender and receiver of the message will need. Symmetric Encryption uses the same key for both the encryption and the decryption of the data.
Let's see how you implement this:
package main
import (
"crypto/aes"
"encoding/hex"
"fmt"
)
func main() {
// cipher key
key := "thisis32bitlongpassphraseimusing"
// plaintext
pt := "Password12345678"
c := EncryptAES([]byte(key), pt)
// plaintext
fmt.Printf("PASSWORD: %v\n", pt)
// ciphertext
fmt.Printf("ENCRYPTED (CIPHER-TEXT): %v\n", c)
// decrypt
DecryptAES([]byte(key), c)
}
// this function encrypts the password
func EncryptAES(key []byte, plaintext string) string {
c, _ := aes.NewCipher(key)
out := make([]byte, len(plaintext))
c.Encrypt(out, []byte(plaintext))
return hex.EncodeToString(out)
}
// this function decrypts the password
func DecryptAES(key []byte, ct string) {
ciphertext, _ := hex.DecodeString(ct)
c, _ := aes.NewCipher(key)
pt := make([]byte, len(ciphertext))
c.Decrypt(pt, ciphertext)
s := string(pt[:])
fmt.Println("DECRYPTED:", s)
}
Run code live here
Keypairs
There's one big limitation with Symmetric Encryption and that's the fact that both sender and receiver of the data need to share a password, it's just not very practical for two different parties to agree upon a shared password.
Once again maths comes to the rescue in the form of a public-key cryptosystem.
Instead of one key, it uses two keys that are mathematically linked. A public key and a private key. As the name suggests, the private key should be kept secret while the public key can be shared with others.
Think of it like a Bank that doesn't ask for any identification when you make a deposit, all you need is the account number. Your account number at that bank is the public key, and anyone can walk in and make a deposit to that account number, the number is no secret.
However, to withdraw the money, you need to provide a secret password and which is your private key.
You will learn the code implementation and how to generate Private and Public Key in the next section.
Asymmetric Encryption
You use Asymmetric Encryption anytime you go to a website using HTTPS, the browser will automatically find the public key of an SSL certificate installed on the website. That public key is used to encrypt any data that you send to the website. This prevents hackers from gaining anything useful from it in transit your data is then decrypted with a private key by the trusted website.
The implementation goes as follows:
package main
import (
"crypto/rand"
"crypto/rsa"
"fmt"
)
// Returns a pair of Private and Public key
func generateKeyPair(bits int) (*rsa.PrivateKey, *rsa.PublicKey) {
privateKey, _ := rsa.GenerateKey(rand.Reader, bits)
return privateKey, &privateKey.PublicKey
}
func main() {
// generates a key of 128-bit
privateKey, publicKey := generateKeyPair(128)
fmt.Printf("Private key: %v\n", privateKey)
fmt.Println("")
fmt.Printf("Public Key: %v\n", publicKey)
}
Run code here
Summary
We learned the standard concepts in cryptography such as; Hashing, Salting, Keypairs, Asymmetric and Symmetric Encryption and Decryption. We also saw the different use cases and their importance.
You now have an idea about key parts of modern cryptography, what they are, where you use them and how you can use them. This should be more than enough for now to emphasize their importance. Now you are ready to dig deeper and learn some other techniques on your own!