I'm trying to rewrite rails activesupport decryption to golang, but got this error
err aesGCMOpen got cipher: message authentication failed
Here is the rails code from active support decryption
class Crypton
SECRET_KEY_BASE = ENV["SECRET_KEY_BASE"]
class << self
def encrypt text
raise 'Encypt failed, secret_key_base not found' unless SECRET_KEY_BASE.present?
text = text.to_s unless text.is_a? String
len = ActiveSupport::MessageEncryptor.key_len
salt = SecureRandom.hex len
key = ActiveSupport::KeyGenerator.new(SECRET_KEY_BASE).generate_key salt, len
crypt = ActiveSupport::MessageEncryptor.new key
encrypted_data = crypt.encrypt_and_sign text
"#{salt}$$#{encrypted_data}"
end
def decrypt text
raise 'Decrypt failed, secret_key_base not found' unless SECRET_KEY_BASE.present?
salt, data = text.split "$$"
len = ActiveSupport::MessageEncryptor.key_len
key = ActiveSupport::KeyGenerator.new(SECRET_KEY_BASE).generate_key salt, len
crypt = ActiveSupport::MessageEncryptor.new key
crypt.decrypt_and_verify data
end
end
end
and here is my golang code, I tried to rewrite from rails decryption activesupport
// DecryptGCM
// reference on Rails 5.2-stable:
// https://github.com/rails/rails/blob/5-2-stable/activesupport/lib/active_support/message_encryptor.rb#L183
func DecryptGCM(encryptedText string, secretKeyBase string) (string, error) {
encryptText := strings.Split(encryptedText, "$$")
saltHex := encryptText[0]
encodedText := encryptText[1]
splitEncodedText := strings.Split(encodedText, "--")
encodedText = splitEncodedText[0]
ivText := splitEncodedText[1]
authTagText := splitEncodedText[2]
decodeText, err := base64.StdEncoding.DecodeString(encodedText)
if err != nil {
return "", fmt.Errorf(`err b64 decode text got %v`, err)
}
ivDecodeText, err := base64.StdEncoding.DecodeString(ivText)
if err != nil {
return "", fmt.Errorf(`err b64 iv got %v`, err)
}
authTagTextDecoded, err := base64.StdEncoding.DecodeString(authTagText)
if err != nil {
return "", fmt.Errorf(`err b64 auth tag got %v`, err)
}
key := GenerateKey(secretKeyBase, saltHex)
block, err := aes.NewCipher(key)
if err != nil {
return "", fmt.Errorf(`err aesNewCipher got %v`, err)
}
aesGCM, err := cipher.NewGCM(block)
if err != nil {
return "", fmt.Errorf(`err chipperNewGCM got %v`, err)
}
plaintext, err := aesGCM.Open(nil, ivDecodeText, decodeText, authTagTextDecoded)
if err != nil {
return "", fmt.Errorf(`err aesGCMOpen got %v`, err)
}
return string(plaintext), nil
}
func GenerateKey(secretKeyBase string, saltHex string) []byte {
key := pbkdf2.Key([]byte(secretKeyBase), []byte(saltHex), 65536, 32, sha1.New)
return key
}
func TestMain(t *testing.T) {
encryptText := "7c7eb6202943398b0d0619d008d226372f1b3b341eb39500eab71c3b67b7f641$$hDJ5e+6QkoCjk4cqT+hAY9c7Jj7Hxg==--t9hrqWnzQeeJTffr--1bHoguSmIkYQrpI1cd/KRQ=="
decrypted, err := DecryptGCM(encryptText, SECRET_KEY)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Decrypted:", decrypted)
}
}
Here is the key:
SECRET_KEY_BASE="3ae9b0ce19316f877554a0427044180e27267fb9798db9147feeb318865b3a52f79824201608f6e4e10dc8e3f29e5bf4b83e46c4103ff8d98b99903d054d720i"
I generated the encrypted data from this rails command
Crypton.encrypt("hello, world")
If you found any solution please leave a comment,
thank you
Can decrypt encrypted data from rails encryption
In the aesGCM.Open()
call of the Go code, ciphertext and authentication tag are passed in the third and fourth parameters respectively. This is wrong. Both data must be concatenated and passed in the third parameter. The fourth parameter needs to be nil
. This parameter is for the additional data that is not used here.
In addition, the deserialization of the decrypted data is missing in the Go code. For this, an appropriate library must be applied, e.g. ruby-marshal.
Overall, the following changes are required:
import (
"bytes"
rbmarshal "github.com/dozen/ruby-marshal"
...
)
...
func DecryptGCM(encryptedText string, secretKeyBase string) (string, error) {
...
plaintext, err := aesGCM.Open(nil, ivDecodeText, append(decodeText, authTagTextDecoded...), nil) // Fix 1
if err != nil {
return "", fmt.Errorf(`err aesGCMOpen got %v`, err)
}
var v string
rbmarshal.NewDecoder(bytes.NewReader(plaintext)).Decode(&v) // Fix 2
return string(v), nil
}
With these changes, the data encrypted with the Rails code can be successfully decrypted with the Go code.