0

I'm trying to convert this java code:

package ffsd;

import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class GatewayEncryptUtil {
  public static String encrypt(String plainText, String key) throws Exception {
    return new String(Base64.getEncoder().encode(doCrypt(plainText.getBytes(), 1, key)));
  }
  
  public static byte[] doCrypt(byte[] inputText, int operation, String key) throws Exception {
    MessageDigest mDigest = MessageDigest.getInstance("SHA-512");
    byte[] secretKey = key.getBytes();
    byte[] digestSeed = mDigest.digest(secretKey);
    byte[] hashKey = Arrays.copyOf(digestSeed, 16);
    
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    SecretKeySpec skspec = new SecretKeySpec(hashKey, "AES");
    String cipherKey = new String(secretKey);
    GCMParameterSpec gcmParams = new GCMParameterSpec(128, cipherKey.substring(0, 16).getBytes());
    cipher.init(operation, skspec, gcmParams);
    return cipher.doFinal(inputText);
  }
  
  public static void main(String[] args) {
    try {
      System.out.println(encrypt("Password", "1234567890123456"));
    } catch (Exception e) {
      e.printStackTrace();
    } 
  }
}

to C# .NET 5.0 using the new AesGcm class:

using System;
using System.Security.Cryptography;
using System.Text;

namespace ConsoleApp3
{
    class Program
    {
        static void Main(string[] args)
        {
            string inputText = "Password";
            string msgId = "1234567890123456";

            byte[] hashKey;
            byte[] secretKey = Encoding.ASCII.GetBytes(msgId);

            using (var hasher = SHA512.Create())
            {
                byte[] digestSeed = hasher.ComputeHash(secretKey);
                hashKey = new byte[16];
                Array.Copy(digestSeed, hashKey, hashKey.Length);
            }

            using (var aesGcm = new AesGcm(hashKey))
            {
                byte[] nonce = new byte[AesGcm.NonceByteSizes.MaxSize];
                byte[] plainText = Encoding.ASCII.GetBytes(inputText);
                byte[] authTag = new byte[AesGcm.TagByteSizes.MaxSize];
                byte[] cipherText = new byte[plainText.Length];

                aesGcm.Encrypt(nonce, plainText, cipherText, authTag);
                string cipherTextBase64 = Convert.ToBase64String(cipherText);
                string authTagBase64 = Convert.ToBase64String(authTag);

                Console.WriteLine(cipherTextBase64);
                Console.WriteLine(authTagBase64);
            }
        }
    }
}

But I don't know what the nonce is supposed to be. Not seeing that in the java code anywhere. Can anyone give me any pointers to this? The result of the java code is: "gc1zTHlIPQusN5e+Rjq+veDoIYdU1nCQ" mine is obviously incomplete and not coming close.

Corey
  • 226
  • 1
  • 13

2 Answers2

0

IV and Nonce mean the same thing.

The IV is the second argument to GCMParameterSpec here:

GCMParameterSpec gcmParams = new GCMParameterSpec(128, cipherKey.substring(0, 16).getBytes());

.NET calls this Nonce.

omajid
  • 14,165
  • 4
  • 47
  • 64
  • Thanks, I gave that a go earlier and received this error: System.ArgumentException: 'The specified nonce is not a valid size for this algorithm. (Parameter 'nonce')' A size of 16 doesn't seem to work. Doesn't look like there is much customization available on AesGcm object either – Corey Oct 18 '21 at 16:19
  • 1
    Oh, wow. The original .NET change that added AesGCM limited the size to 12 and only 12: https://github.com/dotnet/corefx/pull/31389. That seemed arbitrary but then I stumbled onto https://stackoverflow.com/questions/44009204/correct-nonce-iv-size-for-aes-gcm-mode and https://crypto.stackexchange.com/questions/41601/aes-gcm-recommended-iv-size-why-12-bytes. It seems like 12 is the recommended value and others values are less secure/interoperable. – omajid Oct 18 '21 at 16:31
  • I guess that implies that I can't port this code over using this implementation. I guess it's off to bouncy castle next and see what is available there. – Corey Oct 18 '21 at 17:14
0

I was able to use BouncyCastle to convert this to C#:

using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using System;
using System.Security.Cryptography;
using System.Text;

namespace ConsoleApp3
{
    class Program
    {
        static void Main(string[] args)
        {
            string inputText = "Password";
            string msgId = "1234567890123456";

            string value = Encrypt(inputText, msgId);
            Console.WriteLine(value);
        }

        public static string Encrypt(string plainText, string msgId)
        {
            const byte GcmTagSize = 16;

            byte[] hashKey;
            byte[] secretKey = Encoding.ASCII.GetBytes(msgId);

            using (var hasher = SHA512.Create())
            {
                byte[] digestSeed = hasher.ComputeHash(secretKey);
                hashKey = new byte[16];
                Array.Copy(digestSeed, hashKey, hashKey.Length);
            }

            var keyParameter = new KeyParameter(hashKey);
            var keyParameters = new AeadParameters(keyParameter, GcmTagSize * 8, secretKey);

            var cipher = CipherUtilities.GetCipher("AES/GCM/NoPadding");
            cipher.Init(true, keyParameters);

            var plainTextData = Encoding.ASCII.GetBytes(plainText);
            var cipherText = cipher.DoFinal(plainTextData);

            return Convert.ToBase64String(cipherText);
        }
    }
}
Corey
  • 226
  • 1
  • 13