简体   繁体   中英

.Net AES encrypt mismatch after decrypt on Java

I have to send the AES encrypted json as content to web server. But after decryption, the content has extra trash symbols appeared at the beggining of the line.

My test method creates the object that is serialized and being send:

[TestMethod]
        public void SendModeChangeWorksAsExpected()
        {
            var snpashot2Send = new ModeChangedReport
            {
                
                ControlWorkMode = ModeEnumeration.Stopped,
                //Controls
                ControlDate = DateTime.Now,
                IsSent = false,
                SentTime = null,
                ReportType = ReportType.ModeChanged,

                Line = new Line
                {
                    AgencyId = "a799eb4f-86da-4af1-a221-9ed8b741b5ce"
                }
            };

            //Создаём шифрованное значение
            var encryptedString = _agencyReportEncriptingTranslator.ConvertModeChange2CypheredString(snpashot2Send);
            //Отправляем в Агентство и получаем результат
            var value = _agencyClient.SendModeChangeReport(encryptedString);
        }

Here are the serialization and encrypt methods:

public string ConvertModeChange2CypheredString(ModeChangedReport report)
        {
            if (report == null) throw new ArgumentNullException(nameof(report));

            //obj to json
            var json = new ModeChangedReportJson
            {
                LineId = report.Line.AgencyId,
                Mode = CreateModeFromIktToUzbekistan(report.ControlWorkMode),
                ActionDate = ConvertDateToAgencyString(report.ControlDate)
            };

            //Serialization
            var retString = _agencyJsonSerializer.SerializeReport2Json(json);

            //Шифруем сериализованный json
            var cypheredValue = _encryptionService.EncryptString(retString);
            return cypheredValue;
        }

Encrypt method:

public string EncryptString(string plaintext)
        {
            var plainTextBytes = Encoding.UTF8.GetBytes(plaintext);
            var cypheredTextBytes = Encrypt(plainTextBytes);
            var converted2Base64Value = Convert.ToBase64String(cypheredTextBytes);
            return converted2Base64Value;
        }

private byte[] Encrypt(byte[] bytes)
        {
            #region Args Validation

            if (bytes == null || bytes.Length < 1)
            {
                throw new ArgumentException("Invalid bytes to encrypt");
            }

            if (_key == null || _key.Length < 1)
            {
                throw new InvalidOperationException("Invalid encryption key");
            }


            #endregion

            byte[] encrypted;

            try
            {
                using (AesManaged aes = new AesManaged())
                {
                    aes.Key = _key;
                    aes.IV = _iv;
                    aes.Padding = PaddingMode.PKCS7;
                    aes.Mode = CipherMode.CBC;

                    ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, _iv);
                    using (MemoryStream ms = new MemoryStream())
                    {
                        ms.Write(aes.IV, 0, aes.IV.Length);
                        using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                        {
                            cs.Write(bytes, 0, bytes.Length);
                        }
                        encrypted = ms.ToArray();
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }

            return encrypted;
        }

Http client send method:

public bool SendModeChangeReport(string cypheredValue)
        {
            var token = GetAccessToken();

            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.AuthorizationToken);
                client.DefaultRequestHeaders.Add("DEVICE_ID", _agencyAppSettings.DeviceId);

                var content2Post = new StringContent(cypheredValue, Encoding.UTF8, "application/json");
                using (var response = client.PostAsync(_agencyAppSettings.SendModeChangedReportUrl, content2Post).Result)
                {
                    string tokenResponse = null;
                    try
                    {
                        tokenResponse = response.Content.ReadAsStringAsync().Result;
                        response.EnsureSuccessStatusCode();
                        return true;
                    }
                    catch (Exception ex)
                    {
                        _eventLogManager.LogError("При попытке отправить отчёт о смене режима, произошла ошибка: "
                            + $"Код: {response.StatusCode}. Контент: {tokenResponse}. Ошибка: {ex.Message}.");
                        return false;
                    }
                }
            }
        }

But after decryption on receiving server, the string grows with extra trash characters at the begining, like G���h R��EQ�Z {"lineid":"a799eb4f-86da-4af1-a221-9ed8b741b5ce"...

The decrypt method of the server (Java):

在此处输入图像描述

I think that the problem is the padding difference: PKCS7 on my side, and PKCS5 on server.

How can I solve this problem with the extra chars appear on server side?

Those aren't trash characters, they're the Unicode Replacement Character returned when bytes are decoded into text using the wrong character set.

The very fact you got readable text means decrypting succeeded. It's decoding the bytes into text that failed.

The bug is in the Java code. It's using the String(byte[]) which, according to the docs:

Constructs a new String by decoding the specified array of bytes using the platform's default charset.

That's obviously not UTF8. The String​(byte[] bytes,Charset charset) or String​(byte[] bytes,String charsetName) constructors should be used instead, passing the correct character set, eg :

byte[] decryptedBytes = cipher.doFinal(....);
return new String(decryptedBytes, StandardCharsets.UTF_8);

The hacky alternative is to change the remote server's default character set to UTF8.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM