每当我尝试使用Json.NET序列化X509Certificate2实例(不使用其ISerializable实现,但选择忽略它)时,Json.NET会抛出异常。
异常消息是“System.Security.Cryptography.X509Certificates.X509Certificate2上已存在名为'CertContext'的成员。使用JsonPropertyAttribute指定其他名称。”
我写了一个程序来重现它:
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
internal class Program
{
private static void Main(string[] args)
{
var resolver = new DefaultContractResolver
{
IgnoreSerializableInterface = true,
DefaultMembersSearchFlags =
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.GetProperty
};
JsonConvert.SerializeObject(new X509Certificate2(), new JsonSerializerSettings {ContractResolver = resolver});
}
}
在调查之后,我注意到X509Certificate2实现了一个名为“CertContext”的属性,该属性在其基类X509Certificate中隐藏了一个具有相同名称的方法。我怎么能告诉Json.NET只是采取最衍生的属性,就像它通常那样?
答案 0 :(得分:1)
我最近遇到了序列化X509Certificate2
实例的需求,以便将差异比较为两个行为不同的假设相同的环境。我能够通过以下JsonSerializerSettings
实现序列化:
new JsonSerializerSettings {
Error = (s, a) => a.ErrorContext.Handled = true,
ContractResolver = new DefaultContractResolver {
IgnoreSerializableInterface = true
}
}
这是一个完整的工作示例,JSON序列化本地计算机商店中的第一个证书并将其打开:
namespace ConsoleApp1
{
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
class Program
{
static void Main(string[] args)
{
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates[0];
var path = Path.GetTempFileName();
File.WriteAllText(
path,
JsonConvert.SerializeObject(
cert, new JsonSerializerSettings {
Formatting = Formatting.Indented,
// Ignore serializtion errors
Error = (s, a) => a.ErrorContext.Handled = true,
ContractResolver = new DefaultContractResolver {
// Ensures all properties are serialized
IgnoreSerializableInterface = true
}
}
)
);
Process.Start(path);
}
finally
{
store.Close();
}
}
}
}
答案 1 :(得分:0)
要实现此目的,您可以使用转换器:
public class X509Certificate2JsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(X509Certificate2);
}
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue, JsonSerializer serializer)
{
var deserializedRaw = serializer.Deserialize<byte[]>(reader);
var deserialized = new X509Certificate2(deserializedRaw);
return deserialized;
}
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
byte[] certData = ((X509Certificate2)value).Export(X509ContentType.Pfx);
serializer.Serialize(writer, certData);
}
}
并对其进行测试:
public class CertificateHolder : IDisposable
{
[JsonConverter(typeof(X509Certificate2JsonConverter))]
public X509Certificate2 Certificate { get; set; }
public void Dispose() => Certificate?.Dispose();
public static X509Certificate2 CreateCertificate()
{
var ecdsa = ECDsa.Create();
var req = new CertificateRequest("cn=foobar", ecdsa, HashAlgorithmName.SHA256);
var c = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5));
return c;
}
}
[TestFixture]
class X509Certificate2JsonConverterTests
{
[Test]
public void Serialization()
{
var holder = new CertificateHolder();
holder.Certificate = CertificateHolder.CreateCertificate();
var str = JsonConvert.SerializeObject(holder);
var holderRestored = JsonConvert.DeserializeObject<CertificateHolder>(str);
var holderStr = holder.Certificate.ToString();
var holderRestoredStr = holderRestored.Certificate.ToString();
Assert.AreEqual(holderStr, holderRestoredStr);
holder.Dispose();
holderRestored.Dispose();
}
}