У вас есть несколько проблем здесь. Давайте рассмотрим их по порядку.
Во-первых, тип Microsoft.Research.SEAL.Ciphertext
не имеет ни конструктора без параметров, ни конструктора с одним параметром, как видно из справочного источника :
public class Ciphertext : NativeObject
{
public Ciphertext(MemoryPoolHandle pool = null)
{
// Contents omitted
}
public Ciphertext(SEALContext context, MemoryPoolHandle pool = null)
{
// Contents omitted
}
// Additional constructors, methods and members omitted.
Первый параметр конструктора является необязательным, но это не означает, что он не содержит параметров, это просто означает, что компилятор предоставляет значение, когда его нет в коде. Но когда конструктор вызывается через отражение (что и делает Json. NET), все равно необходимо предоставить значение; см. Отражение - вызовите конструктор с параметрами для получения подробной информации. Отсутствие настоящего конструктора без параметров для этого типа является причиной того, что Newtonsoft. Json: невозможно найти конструктор для использования для типа Microsoft.Research.SEAL.Ciphertext. исключение.
(В комментариях было указано, что ваша проблема в том, что sampleClass
не хватает конструктора по умолчанию, но этот комментарий был неправильным.)
Поскольку вы не можете изменить Ciphertext
стандартным способом предоставления Ваш собственный метод построения должен использовать CustomCreationConverter<T>
примерно так:
public class CiphertextConverter : CustomCreationConverter<Ciphertext>
{
public override Ciphertext Create(Type objectType)
{
return new Ciphertext(); // Use the default value for the optional parameter
}
}
И затем выполнить:
var settings = new JsonSerializerSettings
{
Converters = { new CiphertextConverter() },
};
var array = JsonConvert.DeserializeObject<sampleClass []>(requestBody, settings);
Однако это не работает, что твоя следующая проблема. Поскольку большинство свойств *1031* publi Ciphertext
доступны только для чтения, тип не может быть десериализован из них.
Сбой демонстрационной скрипки № 1 здесь .
Итак, что делать? Как оказалось, Ciphertext
имеет два метода
Возможно, они позволят нам сериализовать Ciphertext
в MemoryStream
, а затем вставить содержимое в JSON в виде строки Base64 с помощью преобразователя, такого как:
public class CiphertextConverter : JsonConverter<Ciphertext>
{
readonly SEALContext context;
public CiphertextConverter(SEALContext context) => this.context = context ?? throw new ArgumentNullException(nameof(context));
public override Ciphertext ReadJson(JsonReader reader, Type objectType, Ciphertext existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var data = serializer.Deserialize<byte []>(reader);
if (data == null)
return null;
var cipherText = new Ciphertext();
using (var stream = new MemoryStream(data))
cipherText.Load(context, stream);
return cipherText;
}
public override void WriteJson(JsonWriter writer, Ciphertext value, JsonSerializer serializer)
{
using (var stream = new MemoryStream())
{
value.Save(stream, ComprModeType.Deflate); // TODO: test to see whether Deflate gives better size vs speed performance in practice.
writer.WriteValue(stream.ToArray());
}
}
}
А затем используйте преобразователь во время сериализации и десериализации следующим образом:
var settings = new JsonSerializerSettings
{
Converters = { new CiphertextConverter(GlobalContext.Context) },
};
var array = JsonConvert.DeserializeObject<sampleClass []>(requestBody, settings);
Но подождите - что это за объект GlobalContext.Context
? Это приводит нас к вашей третьей проблеме, а именно к тому, что вам нужны совместимые SEALContext
объекты как на стороне клиента, так и на стороне сервера для передачи Ciphertext
между ними через сериализацию. Теперь, глядя через демонстрационное приложение Cloud Functions , это правильное предположение, поскольку у этого приложения есть совместимые контексты как на стороне клиента, так и на стороне сервера:
Так что я собираюсь предположить, что вы тоже. Учитывая это, приведенный выше конвертер следует использовать как для сериализации, так и для десериализации.
Для целей тестирования я адаптировал метод испытания CiphertextTests.SaveLoadTest()
и класс GlobalContext
из https://github.com/microsoft/SEAL/tree/master/dotnet/tests для создания следующего тестового жгута:
public class TestClass
{
[TestMethod]
public void JsonNetSaveLoadTest()
{
Debug.WriteLine("Testing Json.NET");
Func<Ciphertext, SEALContext, Ciphertext> roundtrip = (cipher, context) =>
{
var clientArray = new [] { new sampleClass { ciphertext = cipher } };
var settings = new JsonSerializerSettings
{
Converters = { new CiphertextConverter(GlobalContext.Context) },
};
var requestBody = JsonConvert.SerializeObject(clientArray, settings);
Debug.Write(" ");
Debug.WriteLine(requestBody);
Debug.WriteLine(" requestBody.Length={0}", requestBody.Length);
var array = JsonConvert.DeserializeObject<sampleClass []>(requestBody, settings);
Assert.IsTrue(array.Length == clientArray.Length);
var reserializedJson = JsonConvert.SerializeObject(array, settings);
Debug.Write(" ");
Debug.WriteLine(reserializedJson);
Assert.IsTrue(requestBody == reserializedJson);
return array[0].ciphertext;
};
SaveLoadTest(roundtrip);
Console.WriteLine(" passed.");
}
// Adapted from https://github.com/microsoft/SEAL/blob/master/dotnet/tests/CiphertextTests.cs#L113
[TestMethod]
public void DirectSaveLoadTest()
{
Debug.WriteLine("Testing direct save and load:");
Func<Ciphertext, SEALContext, Ciphertext> roundtrip = (cipher, context) =>
{
Ciphertext loaded = new Ciphertext();
Assert.AreEqual(0ul, loaded.Size);
Assert.AreEqual(0ul, loaded.PolyModulusDegree);
Assert.AreEqual(0ul, loaded.CoeffModCount);
using (MemoryStream mem = new MemoryStream())
{
cipher.Save(mem);
mem.Seek(offset: 0, loc: SeekOrigin.Begin);
loaded.Load(context, mem);
}
return loaded;
};
SaveLoadTest(roundtrip);
Debug.WriteLine(" passed.");
}
// Adapted from https://github.com/microsoft/SEAL/blob/master/dotnet/tests/CiphertextTests.cs#L113
static void SaveLoadTest(Func<Ciphertext, SEALContext, Ciphertext> roundtrip)
{
SEALContext context = GlobalContext.Context;
KeyGenerator keygen = new KeyGenerator(context);
Encryptor encryptor = new Encryptor(context, keygen.PublicKey);
Plaintext plain = new Plaintext("2x^3 + 4x^2 + 5x^1 + 6");
Ciphertext cipher = new Ciphertext();
encryptor.Encrypt(plain, cipher);
Assert.AreEqual(2ul, cipher.Size);
Assert.AreEqual(8192ul, cipher.PolyModulusDegree);
Assert.AreEqual(4ul, cipher.CoeffModCount);
var loaded = roundtrip(cipher, context);
Assert.AreEqual(2ul, loaded.Size);
Assert.AreEqual(8192ul, loaded.PolyModulusDegree);
Assert.AreEqual(4ul, loaded.CoeffModCount);
Assert.IsTrue(ValCheck.IsValidFor(loaded, context));
ulong ulongCount = cipher.Size * cipher.PolyModulusDegree * cipher.CoeffModCount;
for (ulong i = 0; i < ulongCount; i++)
{
Assert.AreEqual(cipher[i], loaded[i]);
}
}
}
static class GlobalContext
{
// Copied from https://github.com/microsoft/SEAL/blob/master/dotnet/tests/GlobalContext.cs
static GlobalContext()
{
EncryptionParameters encParams = new EncryptionParameters(SchemeType.BFV)
{
PolyModulusDegree = 8192,
CoeffModulus = CoeffModulus.BFVDefault(polyModulusDegree: 8192)
};
encParams.SetPlainModulus(65537ul);
BFVContext = new SEALContext(encParams);
encParams = new EncryptionParameters(SchemeType.CKKS)
{
PolyModulusDegree = 8192,
CoeffModulus = CoeffModulus.BFVDefault(polyModulusDegree: 8192)
};
CKKSContext = new SEALContext(encParams);
}
public static SEALContext BFVContext { get; private set; } = null;
public static SEALContext CKKSContext { get; private set; } = null;
public static SEALContext Context => BFVContext;
}
Рабочая демонстрационная скрипка № 2 здесь .
Примечания:
Пока он публикуется c, нет необходимости отмечать конструктор без параметров sampleClass
с [JsonConstructor]
.
В результате тестирования строки Base64, сгенерированные для Ciphertext
, выглядят довольно длинными и составляют примерно 0,5 МБ на Ciphertext
. Поскольку Json. NET полностью материализует каждую строку во время синтаксического анализа, это не очень эффективно при обработке таких огромных строк. Вам придется пересмотреть свою архитектуру, если вы превысили максимальную эффективную длину строки или опыт фрагментация кучи больших объектов .
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ
Я не специалист по безопасности. Я не могу сказать вам, может ли передача сериализованного Ciphertext
по проводнику утечь информацию. Также я не могу посоветовать вам, как выбрать подходящий SEALContext
для вашего приложения - или даже о том, может ли наличие совместимых контекстов на стороне клиента и сервера привести к утечке информации. Этот ответ только объясняет, как сериализовать указанный c объект SEAL через Json. NET.