user15287616
user15287616

Reputation: 23

About nodejs AES-128-GCM encryption after the other party can not parse the ciphertext problem

According to the project requirements, a set of request data needs to be encrypted using AES-128-GCM, but the encrypted data obtained is always not parsed by the other party.When I use Golang to write the encryption algorithm, the encrypted data can be parsed properly by the other party.

function Encrypt(text, key) {
    
    if (!text) {
        return '';
    }
    if (typeof text != 'string') {
        text = JSON.stringify(text);
    }

    const md5 = crypto.createHash('md5');
    
    const result = md5.update(key).digest();

    const iv = crypto.randomBytes(12);


    const cipher = crypto.createCipheriv('aes-128-gcm', result, iv);
    cipher.setAutoPadding(0);
    const encrypted = cipher.update(text, 'utf8');

    const finalstr = cipher.final();

    const tag = cipher.getAuthTag();

    const res = Buffer.concat([encrypted, finalstr, tag]);

    return  res.toString('base64');
    
}

The above is the encryption code for nodejs, The key passed in '12f41deed45188c8061c840c643baede'

Now I can prove that there is no problem for the other party to parse the cipher text, then the problem can only be a problem in the encryption code of nodejs, please help me to look at the big guys, to solve the problem for me!

The following is the encryption code for golang 👇

// golang code

type User struct {
    Ai    string `json:"ai"`
    Name  string `json:"name"`
    IdNum string `json:"idNum"`
}

func main() {

    u := User{
        Ai:    "200000000000000001",
        Name:  "某二一",
        IdNum: "110000190201010009",
    }

    str, err := json.Marshal(u)

    if err == nil {
        fmt.Println(string(str))
    }


    //NewGCM_encrypt("12f41deed45188c8061c840c643baede", "{\"ai\":\"200000000000000001\",\"name\":\"某二一\",\"idNum\":\"110000190201010009\"}")
}

func NewGCM_encrypt(keyv, s string) string {
    key, _ := hex.DecodeString(keyv)
    //key := hexStringToByte(keyv)
    fmt.Println("key:", key)
    plaintext := []byte(s)

    //fmt.Println("key", string(key))
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err.Error())
    }

    // Never use more than 2^32 random nonces with a given key because of the risk of a repeat.
    nonce := make([]byte, 12)
    if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
        panic(err.Error())
    }
    //nonce, _ = hex.DecodeString("FA9F93725406FD0E4F621B01")
    fmt.Printf("iv:%x\n", nonce)

    //iv := []byte("123456789012")
    aesgcm, err := cipher.NewGCM(block)
    if err != nil {
        panic(err.Error())
    }

    ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil)

    r := append(nonce, ciphertext...)

    //fmt.Println("ciphertext:", ciphertext)
    //fmt.Printf("ciphertext16str:%x\n", ciphertext)
    //r, _ := hex.DecodeString(fmt.Sprintf("%x", ciphertext))
    //fmt.Println("r:", r)
    return base64.StdEncoding.EncodeToString(r)
}

Upvotes: 1

Views: 1838

Answers (1)

Terry Lennox
Terry Lennox

Reputation: 30675

We need to include the IV with the encrypted data, in addition to the auth tag, otherwise the other party will not be able to decrypt it.

I've updated to match the Golang code, so the Node.js Encrypt should be decrypted by the same code.

I've also added an example of decrypting the data in Node.js, this may be helpful in diagnosing any issues.

NB: Since the IV is random, we won't get the same exact output as the Golang code (or between calls), but it should decrypt correctly.

const crypto = require("crypto");

function Encrypt(text, key) {
    
    if (!text) {
        return '';
    }
    if (typeof text != 'string') {
        text = JSON.stringify(text);
    }

    const cipherKey = Buffer.from(key, "hex");
    const iv = crypto.randomBytes(12);
    const cipher = crypto.createCipheriv('aes-128-gcm', cipherKey, iv);

    const res = Buffer.concat([iv, cipher.update(text), cipher.final(), cipher.getAuthTag()]);
    return res.toString('base64');    
}

function Decrypt(input, key) {
    
    input = input instanceof Buffer ? input: Buffer.from(input, "base64");
    const tag = input.slice(input.length - 16, input.length);
    const iv = input.slice(0, 12);
    const toDecrypt = input.slice(12, input.length - tag.length);

    const cipherKey = Buffer.from(key, "hex");
    const decipher = crypto.createDecipheriv('aes-128-gcm', cipherKey, iv);
    decipher.setAuthTag(tag);

    const res = Buffer.concat([decipher.update(toDecrypt), decipher.final()]);
    return res.toString('utf8');
}

const key = "12f41deed45188c8061c840c643baede"
const encrypted = Encrypt("Why then 'tis none to you; for there is nothing either good or bad, but thinking makes it so.", key);

console.log("Encrypted data:", encrypted);
console.log("Decrypted:", Decrypt(encrypted, key));

Upvotes: 6

Related Questions