We have a target Java codebase responsible for decrypting an incoming payload. The payload is being encrypted within a .NET Core isolated Azure Function before being sent to the endpoint where the Java code attempts decryption. The encryption logic in .NET has been implemented to mirror the approach used in the Java code. However, during decryption on the Java side, we are encountering an "Invalid MAC" error.
The Java code for encryption:
package encrypt;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.time.Instant;
import java.util.Base64;
import javax.crypto.Cipher;
import org.bouncycastle.crypto.digests.Blake2bDigest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.IESParameterSpec;
public class Encrypt {
public static void main(String[] args) throws Exception {
// plain-text request
String input = "Encryption with ECIES";
String encryptedpayload = encryptECIES(input);
System.out.println(encryptedpayload);
}
public static String encryptECIES(String payLoad) throws Exception {
Security.addProvider(new BouncyCastleProvider());
String key = {{PUBLIC KEY}};
KeyFactory keyFactory = KeyFactory.getInstance("EC");
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode((base64PublicKey)));
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance("ECIESwithSHA512/NONE/NoPadding");
IESParameterSpec iesParamSpec = new IESParameterSpec(null, null, 256);
cipher.init(1, publicKey, iesParamSpec);
return Base64.getEncoder().encodeToString(cipher.doFinal(payLoad.getBytes()));
}
}
The Java code snippet for decryption:
package encrypt;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.time.Instant;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import org.bouncycastle.crypto.digests.Blake2bDigest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.IESParameterSpec;
import org.json.JSONObject;
public class Decrypt {
public static void main(String[] args) throws Exception{
// encrypted payload
String input = "BEXNm796h82FKz79//6spJ/u9Aq7W+972N+vdx6dBnjiFibP+2XXHuHOzsLKqgKslj6NW8gIKk3//LPOckKnsWyETUtTKLF7LXRWzOp1jQbQfBHXqOu1J81N85r2PvHiL9vQG/oKxJMXXPLlSfJ0m1GTfG7FKzrELqhxAUoG3jUpIujjgycOHIw+iw9oQDcCGNZtpm7D";
String payload = createDecryptionCipher(input);
}
private static String createDecryptionCipher(String input) throws NoSuchPaddingException, NoSuchAlgorithmException, InterruptedException,
InvalidKeySpecException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("ECIESwithSHA512/NONE/NoPadding");
String privateKey = "{{PRIVATE KEY}}";
IESParameterSpec iesParamSpec = new IESParameterSpec(null, null,256);
cipher.init(2, getDecryptedPrivateKey(privateKey), iesParamSpec);
return new String(cipher.doFinal(Base64.getDecoder().decode(input.getBytes())));
}
}
I've used similar encryption logic in C# that is used in the Java code snippet.
The C# encryption logic that is used:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using NSec.Cryptography;
using System.Text;
using System.Text.Json;
using System.Security.Cryptography;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.OpenSsl;
using System.Text.RegularExpressions;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Agreement;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto.EC;
using Org.BouncyCastle.Asn1.Anssi;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.TeleTrust;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Asn1.X509;
using System.Security.Cryptography.X509Certificates;
using Org.BouncyCastle.Bcpg.OpenPgp;
namespace BharatConnectEncryption;
public class Encryption_Demo
{
private readonly ILogger<Encryption_Demo> _logger;
public Encryption_Demo(ILogger<Encryption_Demo> logger)
{
_logger = logger;
}
[Function("Encryption_Demo")]
public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req)
{
try
{
string input = "Encryption with ECIES";
Console.WriteLine("Original: " + input);
string pemPublicKey = "{{PUBLIC KEY}}"
ECPublicKeyParameters pubKey = LoadPublicKeyFromPem(pemPublicKey);
string encrypted = ECIESEncryptionUtil.Encrypt(input, pubKey);
Console.WriteLine("Encrypted: " + encrypted);
var response = req.CreateResponse(System.Net.HttpStatusCode.OK);
await response.WriteStringAsync(encrypted);
return response;
}
catch (Exception ex)
{
var response = req.CreateResponse(System.Net.HttpStatusCode.InternalServerError);
await response.WriteStringAsync($"There is some error. The reason is : {ex.Message}");
return response;
}
}
public static ECPublicKeyParameters LoadPublicKeyFromPem(string pem)
{
using var reader = new StringReader(pem);
var pemReader = new PemReader(reader);
var keyObject = pemReader.ReadObject();
if (keyObject is ECPublicKeyParameters pubKey)
return pubKey;
if (keyObject is SubjectPublicKeyInfo info)
return (ECPublicKeyParameters)PublicKeyFactory.CreateKey(info);
throw new Exception("Unsupported public key format");
}
public static class ECIESEncryptionUtil
{
// Encrypt using ECIESwithSHA512/NONE/NoPadding
public static string Encrypt(string plainText, ECPublicKeyParameters recipientPublicKey)
{
var ecParams = recipientPublicKey.Parameters;
var random = new SecureRandom();
// Generate ephemeral key pair
var keyGen = new ECKeyPairGenerator();
keyGen.Init(new ECKeyGenerationParameters(ecParams, random));
AsymmetricCipherKeyPair ephKp = keyGen.GenerateKeyPair();
var iesEngine = new IesEngine(
new ECDHBasicAgreement(),
new Kdf2BytesGenerator(new Sha512Digest()),
new HMac(new Sha512Digest())
);
var derivation = new byte[0];
var encoding = new byte[0];
var iesParams = new IesWithCipherParameters(derivation, encoding, 256, 512);
iesEngine.Init(true, ephKp.Private, recipientPublicKey, iesParams);
byte[] input = Encoding.UTF8.GetBytes(plainText);
byte[] ciphertext = iesEngine.ProcessBlock(input, 0, input.Length);
// Prepend ephemeral public key
byte[] ephPublicBytes = ((ECPublicKeyParameters)ephKp.Public).Q.GetEncoded(false);
byte[] result = new byte[ephPublicBytes.Length + ciphertext.Length];
Array.Copy(ephPublicBytes, 0, result, 0, ephPublicBytes.Length);
Array.Copy(ciphertext, 0, result, ephPublicBytes.Length, ciphertext.Length);
return Convert.ToBase64String(result);
}
}
After generating the encrypted string from C# when we're using the string for decryption from the Java code, we're getting the error as "Invalid MAC".