Skip to content

Instantly share code, notes, and snippets.

@logeable
Last active July 26, 2022 06:00
Show Gist options
  • Select an option

  • Save logeable/d0da172fbdb46577537d03757b029627 to your computer and use it in GitHub Desktop.

Select an option

Save logeable/d0da172fbdb46577537d03757b029627 to your computer and use it in GitHub Desktop.
golang implement CryptoJS aes256/cbc/pkcs7 encryption and decryption
// reference https://stackoverflow.com/a/27250883
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"crypto/rand"
"encoding/base64"
"fmt"
"hash"
"io"
)
func pad(src []byte) []byte {
l := aes.BlockSize - len(src)%aes.BlockSize
padding := bytes.Repeat([]byte{byte(l)}, l)
return append(src, padding...)
}
func unpad(src []byte) ([]byte, error) {
if len(src) == 0 {
return nil, fmt.Errorf("src is empty")
}
l := int(src[len(src)-1])
if l >= len(src) {
return nil, fmt.Errorf("pad length is too long")
}
return src[:len(src)-l], nil
}
// Encrypt plain text by password to base64 encoded ciphertext
func Encrypt(password []byte, plaintext []byte) (string, error) {
padded := pad(plaintext)
ciphertext := make([]byte, aes.BlockSize+len(padded))
tmp := ciphertext[:aes.BlockSize]
copy(tmp, []byte("Salted__"))
_, err := io.ReadFull(rand.Reader, tmp[8:])
if err != nil {
return "", err
}
evpBytes := EvpKDF(md5.New, password, tmp[8:], 32+16, 1)
key := evpBytes[:32]
iv := evpBytes[32:]
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], padded)
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
// Decrypt cipher text
func Decrypt(password []byte, ciphertext string) ([]byte, error) {
decoded, err := base64.StdEncoding.DecodeString(ciphertext)
if err != nil {
return nil, err
}
evpBytes := EvpKDF(md5.New, password, decoded[8:16], 32+16, 1)
key := evpBytes[:32]
iv := evpBytes[32:]
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
mode := cipher.NewCBCDecrypter(block, iv)
decrypted := make([]byte, len(decoded[aes.BlockSize:]))
mode.CryptBlocks(decrypted, []byte(decoded[aes.BlockSize:]))
return unpad(decrypted)
}
// copy from https://github.com/tcolgate/gostikkit/blob/master/evpkdf/evpkdf.go
func EvpKDF(hash func() hash.Hash, password []byte, salt []byte, keysize int, iterations int) []byte {
hasher := hash()
derivedKey := []byte{}
block := []byte{}
// Generate key
for len(derivedKey) < keysize {
if len(block) != 0 {
io.Copy(hasher, bytes.NewBuffer(block))
}
io.Copy(hasher, bytes.NewBuffer(password))
io.Copy(hasher, bytes.NewBuffer(salt))
block = hasher.Sum(nil)
hasher.Reset()
// Iterations
for i := 1; i < iterations; i++ {
io.Copy(hasher, bytes.NewBuffer(block))
block = hasher.Sum(nil)
hasher.Reset()
}
derivedKey = append(derivedKey, block...)
}
return derivedKey[0:keysize]
}
func main() {
password := []byte("test")
plaintext := []byte("secret")
ciphertext, err := Encrypt(password, plaintext)
if err != nil {
panic(err)
}
fmt.Println(ciphertext)
plaintext, err = Decrypt(password, ciphertext)
if err != nil {
panic(err)
}
fmt.Println(string(plaintext))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment