I'm using some encryption functions in C# and Java whose output doesn't seem to match. I've been fighting with this for a few days and thought I'd finally turn here for help.
Input string: "a"
Here is a snippet of the java implementation:
CryptoAesGcmService.getInstance().encryptData("a", aes_key)
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class CryptoAesGcmService {
private static CryptoAesGcmService instance;
private static final int GCM_TAG_LENGTH = 16;
private static final int GCM_IV_LENGTH = 12;
public static CryptoAesGcmService getInstance() {
if (instance == null) {
instance = new CryptoAesGcmService();
}
return instance;
}
public String encryptData(String plaintext, String key) throws Exception {
return encrypt2(plaintext, key, createGCMParameter());
}
private static String encrypt(String plaintext, String key, GCMParameterSpec gcmParameterSpec) throws Exception {
// return plaintext;
try {
byte[] plainTextByte = plaintext.getBytes(StandardCharsets.UTF_8);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, createSecretKeySpec(key), gcmParameterSpec);
byte[] cipherText = cipher.doFinal(plainTextByte);
return Base64.getEncoder().encodeToString(cipherText);
} catch (Exception e) {
throw new Exception("Unexpected exception occur.", e);
}
}
private static SecretKeySpec createSecretKeySpec(String secretKey) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(secretKey.getBytes(StandardCharsets.UTF_8));
return new SecretKeySpec(md.digest(), "AES");
}
private static GCMParameterSpec createGCMParameter() {
return new GCMParameterSpec(GCM_TAG_LENGTH * 8, new byte[GCM_IV_LENGTH]);
}
}
Output: fix 3UhjsGLxeoCyP/cd7c7p++I=
Here is a snippet of the .Net implementation:
aes.Encrypt("a", aes_key);
using System;
using System.Buffers.Binary;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
namespace api.Class
{
public class AESGCM
{
private const int KEY_BIT_SIZE = 256;
private const int MAC_BIT_SIZE = 128;
private const int NONCE_BIT_SIZE = 128;
private readonly SecureRandom random;
private static AESGCM instance;
public static AESGCM Instance //property of this class. Create an instance if it is not created yet
{
get
{
if (instance == null)
instance = new AESGCM();
return instance;
}
}
public AESGCM()
{
random = new SecureRandom();
}
//encrypt with strings
public string Encrypt(string text, string key, byte[] nonSecretPayload = null)
{
if (string.IsNullOrEmpty(text))
throw new ArgumentException("Text required!", "text");
//var decodedKey = Convert.FromBase64String(key);
var decodedKey = Encoding.UTF8.GetBytes(key);
var plainText = Encoding.UTF8.GetBytes(text);
var cipherText = EncryptWithKey(plainText, decodedKey, nonSecretPayload);
return Convert.ToBase64String(cipherText);
}
//encrypt with byte array
private byte[] EncryptWithKey(byte[] text, byte[] key, byte[] nonSecretPayload = null)
{
if (key == null || key.Length != KEY_BIT_SIZE / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KEY_BIT_SIZE), "key");
nonSecretPayload = nonSecretPayload ?? new byte[] { };
var nonce = new byte[NONCE_BIT_SIZE / 8];
random.NextBytes(nonce, 0, nonce.Length);
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(key), MAC_BIT_SIZE, nonce, nonSecretPayload);
cipher.Init(true, parameters);
var cipherText = new byte[cipher.GetOutputSize(text.Length)];
var len = cipher.ProcessBytes(text, 0, text.Length, cipherText, 0);
cipher.DoFinal(cipherText, len);
using (var combinedStream = new MemoryStream())
{
using (var binaryWriter = new BinaryWriter(combinedStream))
{
binaryWriter.Write(nonSecretPayload);
binaryWriter.Write(nonce);
binaryWriter.Write(cipherText);
}
return combinedStream.ToArray();
}
}
}
}
Output: always changing
example 1
4BNEStJ12YZIQsaFhOcufy1rgXW2/H5kBmPyBSVdf2qP
example 2
z4xNJgr6YLg+lsCA2jUn0HKorN8UXrVm0QtKl10w/Yba
example 3
0IxfAp2vIOmj3fvsJ25VVINHpnaxtZ5KNl89Qk7MNFcn
I want output .net same java. I don't know enough about encryption nor Java to know where to turn next. Any guidance would be greatly appreciated
UPDATE
I try to follow the advice but not working.
using System;
using System.Text;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
namespace api.Class
{
public class AESGCM
{
private const int KEY_BIT_SIZE = 256;
private const int MAC_BIT_SIZE = 128;
//encrypt with strings
public string Encrypt(string text, string key)
{
if (string.IsNullOrEmpty(text))
throw new ArgumentException("Text required!", "text");
var decodedKey = Encoding.UTF8.GetBytes(key);
var plainText = Encoding.UTF8.GetBytes(text);
var cipherText = EncryptWithKey(plainText, decodedKey);
return Convert.ToBase64String(cipherText);
}
//encrypt with byte array
private byte[] EncryptWithKey(byte[] text, byte[] key)
{
if (key == null || key.Length != KEY_BIT_SIZE / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KEY_BIT_SIZE), "key");
var nonce = new byte[12];
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(key), MAC_BIT_SIZE, nonce);
cipher.Init(true, parameters);
var cipherText = new byte[cipher.GetOutputSize(text.Length)];
var len = cipher.ProcessBytes(text, 0, text.Length, cipherText, 0);
cipher.DoFinal(cipherText, len);
return cipherText;
}
}
}
Output: fix 7szCRlNpTMM+93eTi332Gdw=
FINAL CODE
using System;
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
namespace api.Class
{
public class AESGCM
{
private const int KEY_BIT_SIZE = 256;
private const int MAC_BIT_SIZE = 128;
//encrypt with strings
public string Encrypt(string text, string key)
{
if (string.IsNullOrEmpty(text))
throw new ArgumentException("Text required!", "text");
var decodedKey = sha256_hash(key);
var plainText = Encoding.UTF8.GetBytes(text);
var cipherText = EncryptWithKey(plainText, decodedKey);
return Convert.ToBase64String(cipherText);
}
//encrypt with byte array
private byte[] EncryptWithKey(byte[] text, byte[] key)
{
if (key == null || key.Length != KEY_BIT_SIZE / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KEY_BIT_SIZE), "key");
var nonce = new byte[12];
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(key), MAC_BIT_SIZE, nonce);
cipher.Init(true, parameters);
var cipherText = new byte[cipher.GetOutputSize(text.Length)];
var len = cipher.ProcessBytes(text, 0, text.Length, cipherText, 0);
cipher.DoFinal(cipherText, len);
return cipherText;
}
private byte[] sha256_hash(string value)
{
Byte[] result;
using (var hash = SHA256.Create())
{
Encoding enc = Encoding.UTF8;
result = hash.ComputeHash(enc.GetBytes(value));
}
return result;
}
}
}
Output: same the java
Thank you very much, @user9014097