Go Post #2: AES Encryption

So as I went over how to do a simple hashing program I figured next I would utilize some more of they crypto libraries provided. In this case I used AES to write a simple encryption and decryption program. How this works is rather simple. We pass in two arguments. The first is the plaintext message you want to encrypt and the second is the key used to encrypt the file. A warning here is do not have a default key that is horribly insecure and users will many times use the default. We will now take the encryption key and initialization vector (iv) to encrypt the message. Another warning is here though the iv is not as important as the key make sure you use one and that it’s not something really simple like in the example. The iv you use for encryption must match also between the encryption and the decryption. For this example I use the CFB block mode. This allows for us to have plaintext of any length which makes this example more simple. After we encrypt the message I then decrypt it and print it out just to show  you can. After that we encode the ciphertext (the encrypted text) in base64 so that you can copy and paste it into a decrypter. This way you don’t need to use a file to store the data in this example. Plus it shows you base64 which is fun tool for storing binary data in a human readable format.

package main

import (
 "fmt"
 "os"
 "crypto/aes"
 "crypto/cipher"
 "encoding/base64"
 )
 var commonIV = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}

func main() {
 // Load the plaintext message you want to encrypt.
 plaintext := []byte("hello, world")
 if len(os.Args) > 1 {
 plaintext = []byte(os.Args[1])
 }

// Setup a key that will encrypt the other text.
 key_text := "32o4908go293hohg98fh40gh"
 if len(os.Args) > 2 {
 key_text = os.Args[2]
 }

// We chose our cipher type here in this case
 // we are using AES.
 c, err := aes.NewCipher([]byte(key_text))
 if err != nil {
 fmt.Printf("Error: NewCipher(%d bytes) = %s", len(key_text), err)
 os.Exit(-1)
 }

// We use the CFBEncrypter in order to encrypt
 // the whole stream of plaintext using the
 // cipher setup with c and a iv.
 cfb := cipher.NewCFBEncrypter(c, commonIV)
 ciphertext := make([]byte, len(plaintext))
 cfb.XORKeyStream(ciphertext, plaintext)
 fmt.Printf("%s=>%x\n", plaintext, ciphertext)

// We decrypt it here just for the purpose of
 // showing the fact that it is decryptable.
 cfbdec := cipher.NewCFBDecrypter(c, commonIV)
 plaintextCopy := make([]byte, len(plaintext))
 cfbdec.XORKeyStream(plaintextCopy, ciphertext)
 fmt.Printf("%x=>%s\n",ciphertext, plaintextCopy)

// We must now convert the ciphertext to base64
 // this will allow for the encrypted data to be
 // visible to copy and paste into the decrypter.
 base64Text := make ([]byte, base64.StdEncoding.EncodedLen(len(ciphertext)))
 base64.StdEncoding.Encode(base64Text, []byte(ciphertext))
 fmt.Printf("base64: %s\n", base64Text)
 }

Next we have the decryption program. This program is setup pretty much the same as the encryption one except it takes in the base64 ciphertext and the key. The first thing we then have to do is decode the ciphertext form base64 into the normal binary format that we can decrypt. You will notice here that the iv matches the same as in the encryption program.

package main

import (
    "fmt"
    "os"
    "crypto/aes"
    "crypto/cipher"
    "encoding/base64"
)
var commonIV = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}

var ciphertext []byte

func main() {
    // Load the base64 ciphertext from the arguement.
    if len(os.Args) > 1 {
        // Decode the ciphertext to put it in a usable binary format.
        dbuf := make([]byte, base64.StdEncoding.DecodedLen(len(os.Args[1])))
        base64.StdEncoding.Decode(dbuf, []byte(os.Args[1]))
        ciphertext = []byte(dbuf)
    } else {
        fmt.Printf("Error: At least one argument required!")
        os.Exit(-1)
    }

    // Load the key from the second argument.
    key_text := "32o4908go293hohg98fh40gh"
    if len(os.Args) > 2 {
        key_text = os.Args[2]
    }

    // We chose our cipher type here in this case
    // we are using AES.
    c, err := aes.NewCipher([]byte(key_text));
    if err != nil {
        fmt.Printf("Error: NewCipher(%d bytes) = %s", len(key_text), err)
        os.Exit(-1)
    }

    // We use the CFBDecrypter in order to decrypt
    // the whole stream of ciphertext using the
    // cipher setup with c and a iv.
    cfb := cipher.NewCFBDecrypter(c, commonIV)
    plaintext := make([]byte, len(ciphertext))
    cfb.XORKeyStream(plaintext, ciphertext)

    // We then print out the resulting text.
    fmt.Printf("%x=>%s\n",ciphertext, plaintext)
}

Well I hope this gives people a good example of how to do encryption with go. I felt the need to write this because I could not find a article anywhere on this topic. Most of the examples I used to come up with this were from the cfb_test.go testing code that comes in the source code. These test files have some good examples for using the code if your not afraid of poking around the code base. I also hope this might help people understand some more of the basics of encryption which can come in handy with any application you develop. One last word of warning though this code is in no way ready to be used with actual data and keep it safe. If you want to do that you should probably read up some more on encryption as using encryption without understanding the proper uses of it is not much better then not using it at all.

4 thoughts on “Go Post #2: AES Encryption

  1. Jake

    I’m not an encryption expert but I thought people liked using salts with their encryption keys ;D also, isn’t it a better idea to use part of the actual data to be encrypted to be the IV and have your program recognize at the end or the beginning what the IV/Salt are so that then you don’t use the same one over and over and over making it much easier to encrypt? (at least without the original code)

    Reply
  2. Alpha_Cluster Post author

    If you had payed attention your 2nd question was answered by the link on CFB. CFB uses the data as the IV for subsuqent blocks but still needs a IV for the first block.

    As for salt yes you should use one. Though normally you would just do that by appending something to the password or even to the password before you hash it.

    Reply
  3. ishwor

    i just wrote this aes enc/dec that doesn’t do any modes – if it helps (don’t use this code for anything other than understanding how to use AES module in Go).

    package main

    import (
    “crypto/aes”
    “crypto/rand”
    “fmt”
    “io”
    “os”
    )

    func main() {
    rn := make([]byte, 32)
    _, err := io.ReadFull(rand.Reader, rn)
    if err != nil {
    fmt.Printf(“Error while reading random numbers”, err)
    os.Exit(-1)
    }
    fmt.Println(“=random seed=”)
    fmt.Println(rn)
    fmt.Println(“=”)
    key := make([]byte, 32) // 256-bit AES key
    for i := range key {
    key[i] = byte(i^255) % (rn[i] + 1)
    }
    fmt.Println(“=key=”)
    fmt.Println(key)
    fmt.Println(“=”)

    c, err := aes.NewCipher(key)

    if err != nil {
    fmt.Printf(“NewCipher(%d bytes) = %s”, len(key), err)
    os.Exit(-1)
    }
    fmt.Printf(“Block size: %d\n”, c.BlockSize())
    ciphertext := make([]byte, len(key))
    plaintext := []byte(“hello world this is a plaintext”)
    pad_size := 16 – (len(plaintext) % 16)
    padded_plaintext := make([]byte, pad_size+len(plaintext))
    // fmt.Println(pad_size)
    // fmt.Println(len(padded_plaintext))

    copy(padded_plaintext, plaintext)
    fmt.Println(“=plaintext=”)
    fmt.Println(string(plaintext))
    fmt.Println(plaintext)
    fmt.Println(“=”)

    for i := 0; i < len(padded_plaintext); i += c.BlockSize() {
    c.Encrypt(ciphertext[i:i+c.BlockSize()], padded_plaintext[i:i+c.BlockSize()])
    }

    fmt.Println("=encryption=")
    fmt.Println(string(ciphertext))
    fmt.Println(ciphertext)
    fmt.Println("=")

    c2, err := aes.NewCipher(key)
    if err != nil {
    fmt.Printf("NewCipher(%d bytes) = %s", len(key), err)
    os.Exit(-1)
    }
    pt := make([]byte, len(ciphertext))

    for i := 0; i < len(ciphertext); i += c2.BlockSize() {
    c2.Decrypt(pt[i:i+c2.BlockSize()], ciphertext[i:i+c2.BlockSize()])
    }
    fmt.Println("=decryption=")
    fmt.Println(len(pt))
    fmt.Println(len(ciphertext))
    fmt.Println(string(pt))
    fmt.Println(pt)
    fmt.Println("=")
    }

    Reply
  4. ishwor

    Here’s AES Enc/Dec in CBC mode (not tested):

    package main

    import (
    “crypto/aes”
    “crypto/cipher”
    “encoding/base64”
    “fmt”
    “os”
    )

    var commonIV = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}

    func main() {
    // Load the plaintext message you want to encrypt.
    plaintext := []byte(“hello, world, AES encryption in CBC mode”)
    if len(os.Args) > 1 {
    plaintext = []byte(os.Args[1])
    }

    // Setup a key that will encrypt the other text.
    key_text := “32o4908go293hohg98fh40gh”
    if len(os.Args) > 2 {
    key_text = os.Args[2]
    }

    // We chose our cipher type here in this case
    // we are using AES.
    c, err := aes.NewCipher([]byte(key_text))
    if err != nil {
    fmt.Printf(“Error: NewCipher(%d bytes) = %s”, len(key_text), err)
    os.Exit(-1)
    }

    // We use the CFBEncrypter in order to encrypt
    // the whole stream of plaintext using the
    // cipher setup with c and a iv.
    cbc := cipher.NewCBCEncrypter(c, commonIV)
    pad_size := c.BlockSize() – (len(plaintext) % c.BlockSize())
    padded_plaintext := make([]byte, pad_size+len(plaintext))
    copy(padded_plaintext, plaintext)
    ciphertext := make([]byte, len(padded_plaintext))
    cbc.CryptBlocks(ciphertext, padded_plaintext) //[i:i+c.BlockSize()], plaintext[i:i+c.BlockSize()])
    fmt.Printf(“%s=>%x\n”, padded_plaintext, ciphertext)

    // // We decrypt it here just for the purpose of
    // // showing the fact that it is decryptable.
    cbcdec := cipher.NewCBCDecrypter(c, commonIV)
    plaintextCopy := make([]byte, len(padded_plaintext))
    cbcdec.CryptBlocks(plaintextCopy, ciphertext)

    fmt.Printf(“%x=>%s\n”, ciphertext, plaintextCopy)

    // We must now convert the ciphertext to base64
    // this will allow for the encrypted data to be
    // visible to copy and paste into the decrypter.
    base64Text := make([]byte, base64.StdEncoding.EncodedLen(len(ciphertext)))
    base64.StdEncoding.Encode(base64Text, []byte(ciphertext))
    fmt.Printf(“base64: %s\n”, base64Text)
    }

    Reply

Leave a comment