digitalbazaar / forge

A native implementation of TLS in Javascript and tools to write crypto-based and network-heavy webapps
https://digitalbazaar.com/
Other
5.01k stars 767 forks source link

node-forge AES-GCM fails to decrypt from .NET core 5.0 #1068

Closed RobertBRenfro closed 6 months ago

RobertBRenfro commented 6 months ago

Greetings, I'm currently developing a POC where an encrypted message from .net core 5.0 is sent to angular where node-forge is used to decrypt the message. node-forge always fails to decrypt the message. I've placed breakpoints in the node-forge and angular code to gather some data, which is shown below. I'm also including the relevant angular and .net code. Any help or insight you can provide will be greatly appreciated.

.net sends iv: a8vtZQXrB7ahkpCO tag: 9aWu5mYiVpOK6oNHrV9+LQ== payload: ykUd/Xc=

angular receives iv: a8vtZQXrB7ahkpCO tag: 9aWu5mYiVpOK6oNHrV9+LQ== payload: ykUd/Xc=

node-forge decodes (forge.util.decode64) iv: kËíe\u0005ë\u0007¶¡’ tag: õ¥®æf\"V“ŠêƒG­_~- payload: ÊE\u001dýw

node-forge (start decipher) iv: kËíe\u0005ë\u0007¶¡’ tag: õ¥®æf\"V“ŠêƒG­_~- (this._tag) this._j0: [1808526693,99289014,-1584230258,1,]

node-forge (finish) computed tag does not match. payload is not decrypted. this.tag.bytes(): 5w]!\u00070º·TE°¬G£ð this.tag: õ¥®æf\"V“ŠêƒG­~-

angular code

    decryptUsingAES256(decString) {
        var data = decString.split("|");
        var iv = forge.util.decode64(data[0]);
        var tag = forge.util.decode64(data[1]);
        var decodedData = forge.util.decode64(data[2]);
        decodedData = forge.util.createBuffer(decodedData);

        var decipher = forge.cipher.createDecipher("AES-GCM", forge.util.createBuffer(this.key));
        decipher.start({
          iv: iv,
          tagLength: 128, 
          tag: tag
        });
        decipher.update(decodedData);
        const result = decipher.finish(); 
        var response = decipher.output.data;
        if(result){
            response = decipher.output.data;
        }
        return response;
    }

.net code

       private static string EncryptStream(IConfiguration configuration, string originalBody)
        {
            string outPutText = originalBody;
            try
            {
                // Get bytes of plaintext string
                byte[] plainBytes = Encoding.UTF8.GetBytes(outPutText);

                // Get parameter sizes
                int nonceSize = AesGcm.NonceByteSizes.MaxSize;
                int tagSize = AesGcm.TagByteSizes.MaxSize;
                int cipherSize = plainBytes.Length;

                // We write everything into one big array for easier encoding
                int encryptedDataLength = 4 + nonceSize + 4 + tagSize + cipherSize;
                Span<byte> encryptedData = encryptedDataLength < 1024
                                         ? stackalloc byte[encryptedDataLength]
                                         : new byte[encryptedDataLength].AsSpan();

                // Copy parameters
                BinaryPrimitives.WriteInt32LittleEndian(encryptedData.Slice(0, 4), nonceSize);
                BinaryPrimitives.WriteInt32LittleEndian(encryptedData.Slice(4 + nonceSize, 4), tagSize);
                var iv = encryptedData.Slice(4, nonceSize);
                var tag = encryptedData.Slice(4 + nonceSize + 4, tagSize);
                var cipherBytes = encryptedData.Slice(4 + nonceSize + 4 + tagSize, cipherSize);

                // Generate secure nonce
                RandomNumberGenerator.Fill(iv);

                // Encrypt
                using var aes = new AesGcm(Encoding.UTF8.GetBytes(configuration.GetSection("HPSSettings:AESEncryptKey").Value));
                aes.Encrypt(iv, plainBytes.AsSpan(), cipherBytes, tag);

                // Encode for transmission
                outPutText = $@"{Convert.ToBase64String(iv)}|{Convert.ToBase64String(tag)}|{Convert.ToBase64String(cipherBytes)}";
            }
            catch (CryptographicException e)
            {
                Serilog.Log.Error("A Cryptographic error occurred: {0}", e.Message);
            }

            return outPutText;
        }
    }
RobertBRenfro commented 6 months ago

I found the issue! AES-GCM is now working end to end. The issue was that .net was injecting the key in the wrong format (ut8). the correct format is iso-8859-1.