【问题标题】:Why does Json.NET fail to serialize a X509Certificate2?为什么 Json.NET 无法序列化 X509Certificate2?
【发布时间】:2014-02-10 17:38:53
【问题描述】:

每当我尝试使用 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 只采用最派生的属性,就像通常那样?

【问题讨论】:

    标签: c# json.net


    【解决方案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();
                }
            }
        }
    }
    

    【讨论】:

    • 但不幸的是,当尝试反序列化这个序列化对象时,它失败了
    • @HugoNavaKopp,是的,我的用例是为了诊断目的而序列化以解决问题。未来的读者应该注意反序列化不太可能成功。
    【解决方案2】:

    为此,您可以使用转换器:

    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();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2023-03-19
      • 2014-07-02
      • 2013-07-23
      • 2023-03-24
      • 2012-10-02
      • 1970-01-01
      • 2019-04-08
      • 2010-10-08
      • 1970-01-01
      相关资源
      最近更新 更多