package aes import ( "bytes" "crypto/aes" "crypto/cipher" "crypto/rand" "encoding/base64" "encoding/json" "fmt" "io" ) // PKCS7Padding pads an input slice to be a multiple of the block size. func PKCS7Padding(ciphertext []byte, blockSize int) []byte { padding := blockSize - len(ciphertext)%blockSize padtext := bytes.Repeat([]byte{byte(padding)}, padding) return append(ciphertext, padtext...) } // PKCS7UnPadding removes the padding from a padded ciphertext. func PKCS7UnPadding(origData []byte) ([]byte, error) { length := len(origData) unpadding := int(origData[length-1]) if unpadding > length { return nil, fmt.Errorf("unpadding size is invalid") } return origData[:(length - unpadding)], nil } // Encrypt encrypts a string to an encrypted string. func Encrypt(key, text string) (string, error) { block, err := aes.NewCipher([]byte(key)) if err != nil { return "", err } b := base64.StdEncoding.EncodeToString([]byte(text)) ciphertext := make([]byte, aes.BlockSize+len(b)) iv := ciphertext[:aes.BlockSize] if _, err := io.ReadFull(rand.Reader, iv); err != nil { return "", err } stream := cipher.NewCFBEncrypter(block, iv) stream.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b)) ciphertext = PKCS7Padding(ciphertext, aes.BlockSize) return base64.StdEncoding.EncodeToString(ciphertext), nil } // Decrypt decrypts an encrypted string to an original string. func Decrypt(key, cryptoText string) (string, error) { ciphertext, _ := base64.StdEncoding.DecodeString(cryptoText) ciphertext, err := PKCS7UnPadding(ciphertext) if err != nil { return "", err } block, err := aes.NewCipher([]byte(key)) if err != nil { return "", err } if len(ciphertext) < aes.BlockSize { return "", fmt.Errorf("ciphertext too short") } iv := ciphertext[:aes.BlockSize] ciphertext = ciphertext[aes.BlockSize:] stream := cipher.NewCFBDecrypter(block, iv) stream.XORKeyStream(ciphertext, ciphertext) result, err := base64.StdEncoding.DecodeString(string(ciphertext)) if err != nil { return "", err } return string(result), nil } // EncryptType encrypts a type to an encrypted string. // Note: This assumes the type can be marshaled to JSON. func EncryptType(key string, data interface{}) (string, error) { jsonData, err := json.Marshal(data) if err != nil { return "", err } return Encrypt(key, string(jsonData)) }