简体   繁体   中英

AES GCM Encryption/Decryption C# and Angular

Iam trying to implement the AES GCM Mode encryption. In my application the encryption happens in angular and decryption in C# code. iam using nodejs crypto library in angular for encryption

ENCRYPTION CODE IN ANGULAR

data = "{hello world}";
var randomIV = CryptoJS.lib.WordArray.random(12).toString();
const ALGO = 'aes-256-gcm';
var forge = require('node-forge');
// encrypt some bytes using GCM mode
var cipher = forge.cipher.createCipher('AES-GCM', _dEncP);
cipher.start({
      iv: randomIV, // should be a 12-byte binary-encoded string or byte buffer
      additionalData: 'nvn', // optional
      tagLength: 128 // optional, defaults to 128 bits
});
cipher.update(forge.util.createBuffer('object' == typeof data ? JSON.stringify(data) : data.toString()));
cipher.finish();
var encryptedData = cipher.output;
const encodedB64 = forge.util.encode64(encryptedData.data);
const tag = cipher.mode.tag;
const tagB64 = forge.util.encode64(tag.data);
// outputs encrypted hex

const trasmitmsg = randomIV + "|" + tagB64 + "|" + encodedB64;

In C# iam trying to use system.crypro library

DECRYPTION CODE IN C#

string[] data = cipherText.Split("|");
           
String ivString = data[0];
String additionalString = data[1];
String cipherString = data[2];

byte[] keyBytes = Encoding.UTF8.GetBytes(key);
byte[] ivBytes = Convert.FromBase64String(ivString);

byte[] encdata = Convert.FromBase64String(cipherString);
byte[] tag = Convert.FromBase64String(additionalString);

var aesAlg = new AesGcm(keyBytes);
var plaintextBytes = new byte[encdata.Length];
aesAlg.Decrypt(ivBytes, encdata, tag, plaintextBytes);
var result = Encoding.UTF8.GetString(plaintextBytes)

When i run this code, i get below error System.Security.Cryptography.CryptographicException: 'The computed authentication tag did not match the input authentication tag.'

Can you please help what iam doing wrong here, do you have sample working example for angular(encryption) and c#(decryption code)

The IV/nonce is encoded/decoded inconsistently in both codes, which results in different IVs/nonces during encryption/decryption. Furthermore, the additional data is not taken into account during decryption.

CryptoJS.lib.WordArray.random(12) generates a 12 bytes sequence wrapped in a WordArray and hex encoded with .toString() , resulting in a 24 bytes IV/nonce. This is used during encryption. Although GCM can use arbitrarily long IVs/nonces, the recommended length is 12 bytes, which should also be kept for efficiency and compatibility reasons. To do this, the WordArray must be converted to a bytes string using the Latin1 encoder and so passed to the start() function.
For concatenation, the WordArray should not be converted with the Hex encoder, but with the Base64 encoder, as this is consistent with the encoding for tag and ciphertext (also, in the C# code, the IV/Nonce is decoded with Base64).
As you can see, using CryptoJS is inefficient because of the WordArray type when interacting with node-forge. Therefore, the generation of the IV/nonce should be done directly with node-forge, which eliminates the dependency on the CryptoJS library (since CryptoJS is used exclusively for the generation of the IV/nonce in the posted code):

var randomIV = forge.random.getBytesSync(12);

This returns the IV/nonce directly as a bytes string which can be passed directly to the start() function. For concatenation the IV/nonce has to be encoded with Base64:

var randomIVB64 = forge.util.encode64(randomIV);

Regarding the additional associated data (aad), they are optional, ie they do not have to be used during encryption. But if they are used during encryption, then they must also be used during decryption. If there is additional associated data, it has to be concatenated, because the decrypting side needs it for decryption:

var aad = 'nvn'
cipher.start({
      iv: randomIV, 
      additionalData: aad, 
      tagLength: 128 
});
...
var aadB64 = forge.util.encode64(aad) 
const trasmitmsg = aadB64 + "|" + randomIVB64 + "|" + tagB64 + "|" + encodedB64;

In the C# code, the individual parts are to be separated accordingly:

...
string addString = data[0];
string ivString = data[1];
string additionalString = data[2];
string cipherString = data[3];
byte[] add = Convert.FromBase64String(addString);
...

and the additional associated data are to be taken into account when decrypting:

aesAlg.Decrypt(ivBytes, encdata, tag, plaintextBytes, add);

With these changes, a ciphertext generated with the JavaScript code can be decrypted with the C# code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM