This is a 4 year old nodejs project I took over, and I was asked to refactor it using golang, but in the refactoring I found that the nodejs encryption was deprecated. And, I don't know which mode of AES is used to encrypt this code
Can any expert help me to see how to decrypt this nodejs encryption with golang? Thank you very much!
Encryption code for nodejs :
exports.createToken = function (src: string, timestamp: string, key: any) {
var msg = src + '|' + timestamp;
var cipher: any = CryptoJS.createCipher('aes256', key);
var enc: any = cipher.update(msg, 'utf8', 'hex');
enc += cipher.final('hex');
return enc;
};
Decryption code for nodejs :
exports.parseToken = function (token: string, key: string): any {
let decipher = CryptoJS.createDecipher('aes256', key);
let dec: string;
try {
dec = decipher.update(token, 'hex', 'utf8');
dec += decipher.final('utf8');
} catch (err) {
console.error('[token] fail to decrypt token. %j', token);
return null;
}
var ts = dec.split('|');
if (ts.length !== 2) {
// illegal token
return null;
}
return { src: ts[0], timestamp: Number(ts[1]) };
};
The deprecated methods crypto.createCipher()
and crypto.createDecipher()
apply the proprietary OpenSSL function EVP_BytesToKey()
to derive a 32 bytes key and a 16 bytes IV from a password. No salt is used, the digest MD5 and an iteration count of 1. This algorithm is very insecure, which is why both methods are deprecated.
The posted code applies aes-256-cbc
(i.e. AES-256 in CBC mode) and just this key derivation to derive a key/IV pair. Since the key derivation does not use a salt, always the same ciphertext results for the same plaintext and password. E.g. for:
var src = 'The quick brown fox jumps over the lazy dog';
var timestamp = '1616409134831';
var passphrase = 'my secret passphrase';
the ciphertext is:
60673700fb64da36b65829ee3c578d1ec675638a95c8dee4e7c026ee72a837c2170c13b7b24125c02871663a64fd646dd9994793943eeb70b3e959cbc4cd423a
So for decryption in Go you need an implementation of EVP_BytesToKey()
, e.g. here:
package main
import (
"crypto/aes"
"crypto/cipher"
"fmt"
"github.com/walkert/go-evp"
"encoding/hex"
)
func main() {
key, iv := evp.BytesToKeyAES256CBCMD5([]byte(""), []byte("my secret passphrase")) // MD5, no salt, passprase: my secret passphrase
ciphertext, _ := hex.DecodeString("60673700fb64da36b65829ee3c578d1ec675638a95c8dee4e7c026ee72a837c2170c13b7b24125c02871663a64fd646dd9994793943eeb70b3e959cbc4cd423a")
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
cbc := cipher.NewCBCDecrypter(block, iv)
plaintextPadded := make([]byte, len(ciphertext))
cbc.CryptBlocks(plaintextPadded , ciphertext)
plaintext := string(PKCS7Unpad(plaintextPadded ))
fmt.Println("Decrypted data: ", string(plaintext))
}
func PKCS7Unpad(src []byte) []byte { // PKCS7 unpadding from https://stackoverflow.com/a/41595640/9014097
length := len(src)
unpadding := int(src[length-1])
return src[:(length - unpadding)]
}
Running this code results in the above plaintext.