Protect Image File Using Encryption (Written in Go)

The digital platform must protect consumer data including the image data. Cybersecurity attacks often targeting image files.

Purnaresa Yuliartanto
The Startup
4 min readJun 21, 2020

--

Protect credit card photo. Photo by Ryan Born on Unsplash

Background

There are many sensitive data in the form of an image file. Photo, National ID, Certificates, and any confidential image. Imagine if our credit card photo leaked to the internet and shown in Google Search.

Protecting consumer and company data is mandatory by regulation (and common sense).

Objective

Protect the image stored in the system from unauthorized access.

Pseudocode

Storing

  1. Prepare the image file
  2. Prepare the encryption key (secret)
  3. Encrypt the image using AES into the cipher data
  4. Save the cipher data into storage
Store image as an encrypted file.

There are two options on the step no 2. The first option is to have a predefined (Shared Key) to encrypt every image. Another option is to create the newly generated key before every encryption — the newly generated key called Data Encryption Key.

Shared Key is making sense if the system use case is only for internal organization. Since the Shared Key is already predefined, only the cipher data need to be stored.

Generating Data Encryption Key is a must if the image is also moving outside the organization. Principally, we cannot share the Shared Key to outside organizations. Data Encryption Key must be stored alongside the cipher data so the data can be decypher in the future.

Retrieving

  1. Retrieve the cipher data
  2. Retrieve the encryption key (secret)
  3. Decrypt the cipher file using AES into the plain image file
Retrieve image from encrypted file

Code

Library

We have provided a wrapper function for Golang in this link: https://github.com/purnaresa/bulwark/tree/master/encryption

The function EncryptAES receive the plain data of image and encryption key. The output of EncryptAES is cipher data in byte coded.

func (c *Client) EncryptAES(plainData, secret []byte) (cipherData []byte) {
block, _ := aes.NewCipher(secret)
gcm, err := cipher.NewGCM(block)
if err != nil {
return
}
nonce := make([]byte, gcm.NonceSize())
_, err = io.ReadFull(rand.Reader, nonce)
if err != nil {
return
}
cipherData = gcm.Seal(
nonce,
nonce,
plainData,
nil)
return
}

The function DecryptAES need the input of ciphertext and the encryption key. The output is plain data in byte coded — the byte data used for writing the original image.

func (c *Client) DecryptAES(cipherData, secret []byte) (plainData []byte) {
block, err := aes.NewCipher(secret)
if err != nil {
return
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return
}
nonceSize := gcm.NonceSize()
nonce, ciphertext := cipherData[:nonceSize], cipherData[nonceSize:]
plainData, err = gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return
}
return
}

An extra function to generate Data Encryption Key is in function GenerateRandomString. The function needs input for Encryption Key length.

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"func (c *Client) GenerateRandomString(length int) (result string) {
b := make([]byte, length)
for i := range b {
b[i] = letterBytes[mathRand.Intn(len(letterBytes))]
}
result = string(b)
return
}

To use the library, please fork from the Github in case of any future update or patch.

Example Use Case: Store and Retrieve encrypted image

We need to encrypt images from local files. Must store a new file containing the encrypted data, together with the Data Encryption Key. Then finally, we need to retrieve the encrypted image into the plain image.

The original file is “original-image.jpg.” Here is the code for the use case.

package mainimport (
"log"
"github.com/purnaresa/bulwark/encryption"
"github.com/purnaresa/bulwark/utils"
)
func main() {
// encryption start
// step 1
image := utils.ReadFile("original-image.jpg")
// step 2
encryptionClient := encryption.NewClient()
secret := encryptionClient.GenerateRandomString(32)
// step 3
cipherImage := encryptionClient.EncryptAES(image, []byte(secret))
// step 4
err := utils.WriteFile(cipherImage, "encrypted-image")
if err != nil {
log.Fatalln(err)
}
err = utils.WriteFile([]byte(secret), "image-key.txt")
if err != nil {
log.Fatalln(err)
}
// encryption end
// decryption start
// 1
encryptedImage := utils.ReadFile("encrypted-image")
// 2
key := utils.ReadFile("image-key.txt")
// 3
plainImage := encryptionClient.DecryptAES(encryptedImage, key)
err = utils.WriteFile(plainImage, "test-original.jpg")
if err != nil {
log.Fatalln(err)
}
// decryption end
}

The output of Encryption step 4 is file “encrypted-image,” which contains encrypted data of image and file “image-key.txt,” which contain the key to decrypt the image.

Left is the original image, the middle is the encrypted image, right is the data encryption key.

Summary

Protecting images is a simple step. Since almost all popular languages already have a crypto library, doing encryption is just a few additional codes.

Let us continue protecting our data.

Github Link: https://github.com/purnaresa/bulwark

--

--

Purnaresa Yuliartanto
The Startup

IT architect at best cloud provider in the planet. Experience in cybersecurity and tech-fire-fighting.