0
$\begingroup$

I have a big problem about encryption with AES-GCM.

Between using dotnet and Node.js, the encryption results are slightly different.

Here is the code in Node.js from JOSE using Crypto Library :

    var cipher = crypto.createCipheriv("id-aes256-GCM", key, iv);
    cipher.setAutoPadding(false);
    cipher.setAAD(aad);
    var ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
    return {
        cipher: ciphertext,
        tag: cipher.getAuthTag()
    }

The same with C# and BouncyCastle library :

        var iv = Convert.FromBase64String("3Wey/ctxwAnK7ZRv");          

        var cipher = new GcmBlockCipher(new AesFastEngine());
        var parameters = new AeadParameters(new KeyParameter(key), MacBitSize, iv, aad);
        cipher.Init(true, parameters);

        //Generate Cipher Text With Auth Tag
        var cipherText = new byte[cipher.GetOutputSize(secretMessage.Length)];
        var len = cipher.ProcessBytes(secretMessage, 0, secretMessage.Length, cipherText, 0);
        cipher.DoFinal(cipherText, len);

        var tag = cipher.GetMac();

The authentication tag is completely different, but the ciphertext is nearly the same except 4 bytes that are different.

That is very strange, and I don't understand why this is different for the same cipher because every parameter is the same (iv, key, aad).

Does anyone know what is wrong?

Thanks in advance!

EDIT:

for example I get this with NodeJS : WCgMFNfaGsoHynha+Nl+DYT7MiPiU3mbnnEMMMgbHjepkFObJEM8QT7UoTRt74iwmSsDI6IPqgpCpJwYtg/pre5nwbbZqwLh43QlmOm2xZhbeIXq1xOSapNIG2oAjiSJsij9scMjwwrQrYhBujiMbJqy4ppwyD8w5EiNaUGw3ZzUSTxe3ZSl6Mr8kHnsc3L1NVc+qpRyry1V3N+A2pNuE7VzI3Q3QclD2tAAVXtmF2GfXyY70y9+sdib/cXiqJh8Z8q7aZVTAWCLjWjvBn98N0p8KNY35NvKRmzJSu8tZ20ZBA==

And with dotnet BouncyCastle or other same library : WCgMFNfaGsoHynha+Nl+DYT7MiPiU3mbnnEMMMgbHjepkFObJEM8QT7UoTRt74iwmSsDI6IPqgpCpJwYtg/pre5nwbbZqwLh43QlmOm2xZhbeIXq1xOSapNIG2oAjiSJsij9scMjwwrQrYhBujiMbJqy4ppwyD8w5EiNaUGw3ZzUSTxe3ZSl6Mr8kHnsc3L1NVc+qpRyry1V3N+A2pNuE7VzI3Q3QclD2tAAVXtmF2GfXyY70y94sdib/cXiqJh8Z8q7aeVTAWCLjWjvBn98N0x8KNY35N3KRmzJSu8tZ20Z

The source text is a encoded JSON string from object. Maybe it's serialization of the JSON.

Other things, the ciphertext generated in dotnet is not decryptable in nodeJS, so it's probably a cross platform problem ?

EDIT2:

Here is the code to decrypt in NodeJS :

var jose = require('jose') ;

secret = 'rioa62N/Kvx78wUu6icgvDthco+Ro086WFqT4h4QMj4=';

// retrieve the header
var header = new Buffer('eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwiY3R5IjoiSldUIn0=', 'base64') ; // same as in dotnet
// Retreive the intialisation vector
var iv = new Buffer('3Wey/ctxwAnK7ZRv', 'base64'); // same as in dotnet
// Retrieve the cipher text (contains the JWS)
var ciphertext = new Buffer('R2HdWvfZuyNyf9VxZ/FNAkxZQpS9eSTHCnucA32+fB/+Yn1Qnm/HTjHXUsYiO84xv+v7BfiR8Myubb7lWjlCl43TFIB4TIiMPeEI7xLkRzRORnqOfFCktlirwv5Tjzj/vknpaTB1ZLKgIiNxOhZhuPr8S0dLVRm7Sy6tbNPKbUJgBAueg8Q28x3XGonDaznf8SwMVbnqRWD2FSYhoA4aH04iKXyvD9RMECl+/eAzSMpvJAvBCFaASe6A11VF/Jt3nMuOIqMsa93hnS8Y84Idpa9k2glMytS7tD/q2s2olaCs','base64') ;
// Retrieve the authentication tag
var authTag = new Buffer('TMrUu7Q/6trNqJWgrEKwYg==','base64') ;

// get the decryptor
var aes = new jose.jwa('A256GCM') ;
// Compute the aad (base 64 representation of the header)
var aad = new Buffer('eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwiY3R5IjoiSldUIn0=', 'base64') ; // same as in dotnet

// The key must be a buffer
var key = new Buffer(secret, 'base64') ;

// Decryption
var plain = aes.decrypt ( ciphertext, authTag, aad, iv, key )

// Display the jwt
console.log ( 'Part 1: ' + header ) ;
console.log ( 'Part 2: ') ;
console.log ( 'Part 3: ' + iv ) ;
console.log ( 'Part 4 :' + plain ) ;
console.log ( 'Part 5: ' + authTag) ;

Here is the code to encrypt in dotnet :

        var sharedKey = Convert.FromBase64String("rioa62N/Kvx78wUu6icgvDthco+Ro086WFqT4h4QMj4=");

        byte[] key = Convert.FromBase64String("+bGRaL6EPzUCHu0GiNxthgvD3hJN/glKJRQ7B66LJLo=");

        var payload = new
        {
            iss = "XXX",
            exp = 1519828214,
            //exp = DateTimeOffset.Now.ToUnixTimeSeconds() + 90,  // Expiration time is up to 90s, lets play on safe side
            sub = "XXX",
            secret = Convert.ToBase64String(key)
        };


        string jwt = JwtUtils.Encode(payload, sharedKey, JwtHashAlgorithm.HS256); // HMACSHA256 enconding

        var jweHeader = new { alg = "dir", enc = "A256GCM", cty = "JWT" };
        var serializedJweHeader = JsonConvert.SerializeObject(jweHeader, Formatting.None);
        var serializedJweHeaderBytes = Encoding.UTF8.GetBytes(serializedJweHeader);

        var nonce = Convert.FromBase64String("3Wey/ctxwAnK7ZRv");
        var secretMessage = Encoding.UTF8.GetBytes(jwt);

        var cipher = new GcmBlockCipher(new AesFastEngine());
        var parameters = new AeadParameters(new KeyParameter(sharedKey), 128, nonce, serializedJweHeaderBytes);
        cipher.Init(true, parameters);

        //Generate Cipher Text With Auth Tag
        var cipherText = new byte[cipher.GetOutputSize(secretMessage.Length)];
        var len = cipher.ProcessBytes(secretMessage, 0, secretMessage.Length, cipherText, 0);
        cipher.DoFinal(cipherText, len); // Convert.ToBase64String(cipherText.Take(225).ToArray()) to have the base64 représentation to set in NodeJS for the decryption, it appears that cipherText has tag so that's why I take only 225 first

        var tag = cipher.GetMac(); // Convert.ToBase64String(tag) to have the base64 representation

Here is the result crash of NodeJS whn I try to run the above code :

> crypto.js:183
  var ret = this._handle.final();
                         ^

Error: Unsupported state or unable to authenticate data
    at Decipheriv.final (crypto.js:183:26)
    at Object.decrypt (C:\Program Files\nodejs\node_modules\jose\lib\jwa\content
.js:140:72)
    at Object.<anonymous> (C:\Program Files\nodejs\decrypt_jwe_hard.js:35:17)
    at Module._compile (module.js:643:30)
    at Object.Module._extensions..js (module.js:654:10)
    at Module.load (module.js:556:32)
    at tryModuleLoad (module.js:499:12)
    at Function.Module._load (module.js:491:3)
    at Function.Module.runMain (module.js:684:10)
    at startup (bootstrap_node.js:187:16)
$\endgroup$

closed as off-topic by Gilles, Maarten Bodewes, e-sushi Mar 4 '18 at 1:54

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Programming questions are off-topic even if you are writing or debugging cryptographic code. Unless your question is specifically about how the cryptographic algorithm, protocol or side-channel (mitigation) works, you should look into asking on Stack Overflow instead." – Gilles, Maarten Bodewes, e-sushi
If this question can be reworded to fit the rules in the help center, please edit the question.

  • 1
    $\begingroup$ Which 4 bytes are different? $\endgroup$ – mikeazo Mar 2 '18 at 21:18
  • 2
    $\begingroup$ This would be a mere programming question and as such off-topic, except it's not clear that someone who knows nodejs but not bouncycastle or vice versa could help either, and it's abundantly clear that these APIs were both badly designed and documented, and incoherently confuse different types of objects in cryptography for hapless practitioners like yourself. Can you please provide complete working examples for both APIs including all the inputs and outputs—including the values of key, iv, plaintext, etc.? $\endgroup$ – Squeamish Ossifrage Mar 3 '18 at 20:25
  • $\begingroup$ There is probably an encoding issue for the input message, that's the only reasonable explanation for a partial difference in ciphertext. $\endgroup$ – Maarten Bodewes Mar 4 '18 at 0:34
  • $\begingroup$ for example I get this with NodeJS :See next message $\endgroup$ – user56610 Mar 5 '18 at 8:18
  • $\begingroup$ Get rid of the intermediate JSON serialization step for the plaintext. Write out the exact text of the plaintext, byte for byte, and compare that. $\endgroup$ – Squeamish Ossifrage Mar 6 '18 at 15:12
1
$\begingroup$

There is an encoding issue for the input message. That is the only reasonable explanation for a partial difference in ciphertext. GCM uses CTR underneath, so if the IV or key is wrong then all the ciphertext changes. The wrong bytes are exactly at the same location in the plaintext as they are in the ciphertext.

Of course the authentication tag will be completely$^{*1}$ different if even a single bit of plaintext message is different.


$\space^{*1}$ Well, each bit of the authentication tag changes with a chance of $1\over2$ of course.

$\endgroup$
  • $\begingroup$ Ok I'll try this way, but any ciphertext cannot be decrypted with nodeJS, it make NodeJS crash, maybe cause of IV ok key issues. $\endgroup$ – user56610 Mar 5 '18 at 8:33
  • $\begingroup$ You didn't note any crashes in above post. If you have any programming related issues, post them on SO with minimal code, a complete error description and what you've been trying to do. And of course the encryption or cryptography tag or I'll miss them. $\endgroup$ – Maarten Bodewes Mar 5 '18 at 8:42
  • $\begingroup$ I updated the question with the code to encrypt in C# and the code to decrypt in NodeJS (EDIT2), I changed all the key in code but it's the same as the real key. $\endgroup$ – user56610 Mar 5 '18 at 10:37

Not the answer you're looking for? Browse other questions tagged or ask your own question.