System.Runtime.Serialization.InvalidDataContractException 'System.Security.Cryptography.X509Certificates.X509Certificate2'

У меня есть три приложения без сохранения состояния: Application-A, Application-B и Application-C. Приложение B и Приложение C обменивались данными с приложением A с помощью удаленного взаимодействия V1.

Недавно я перенес Application-C на .Net 5.0. Поскольку удаленное взаимодействие V1 не поддерживается в .Net Core, я сделал два слушателя в Application-A(служебное приложение), как показано ниже, для поддержки слушателей V1 и V2_1.

      protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new[]
    {
        new ServiceInstanceListener(this.CreateHeadersAwareServiceInstanceListener),
        new ServiceInstanceListener((c) =>
        {
            var settings = new FabricTransportRemotingListenerSettings {UseWrappedMessage = true};
            return new FabricTransportServiceRemotingListener(c, this,settings);

        },"ServiceEndpointV2_1")
    };
}

public async Task<ClientInfo> GetClientInfo(string clinetId, CancellationToken cancellationToken = default(CancellationToken))
{
        var data = await _logic.GetClientData(clinetId, cancellationToken);
        return data.FromInternalModel();
}

Приложение B должно взаимодействовать с приложением A с помощью удаленного взаимодействия V1, а приложение C будет взаимодействовать с приложением A с использованием стека удаленного взаимодействия V2_1. Ниже показано, как Application-C вызывает Application-A.

       var client ServiceProxy.Create<IBackEndService>(new Uri(_serviceURL), listenerName: "ServiceEndpointV2_1");
await clinet.GetClientInfo(clinetId);

Метод GetClinetInfo возвращает объект ClinetInfo, а ClientInfo выглядит как

          [DataContract]
    public class ClientInfo : IExtensibleDataObject
    {

        [DataMember]
        public string ClinetId { get; set; }

        [DataMember]
        public ClientAddress Address { get; set; }

        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public X509Certificate2 ClientCertificate { get; set; }

        public virtual ExtensionDataObject ExtensionData { get; set; }
    }

Когда Application-C вызывает GetClinetInfo(), я получаю исключение ниже

      Exception Summary:
======================================
Message:Type 'System.Security.Cryptography.X509Certificates.X509Certificate2' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required.
UTC Timestamp: 04/12/2021 11:34:39.298
Machine Name: id-b3000000
Assembly Full Name: Temp.MyAppDomain, Version=3.16.0.0, Culture=neutral, PublicKeyToken=null
Assembly Version: 3.16.0.0
App Domain Name: Temp.MyAppDomain 
Windows Identity: 
Additional Context: 

Exception Information Details:
======================================
Exception Type: System.Runtime.Serialization.InvalidDataContractException
Message: Type 'System.Security.Cryptography.X509Certificates.X509Certificate2' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required.
Help Link: 
HResult: -2146233088
Source: Microsoft.ServiceFabric.Services
Target Site: Void MoveNext()

Stack Trace Information Details: 
======================================
   at Microsoft.ServiceFabric.Services.Communication.Client.ServicePartitionClient`1.InvokeWithRetryAsync[TResult](Func`2 func, CancellationToken cancellationToken, Type[] doNotRetryExceptionTypes)
   at Microsoft.ServiceFabric.Services.Remoting.V2.Client.ServiceRemotingPartitionClient.InvokeAsync(IServiceRemotingRequestMessage remotingRequestMessage, String methodName, CancellationToken cancellationToken)
   at Microsoft.ServiceFabric.Services.Remoting.Builder.ProxyBase.InvokeAsyncV2(Int32 interfaceId, Int32 methodId, String methodName, IServiceRemotingRequestMessageBody requestMsgBodyValue, CancellationToken cancellationToken)
   at Microsoft.ServiceFabric.Services.Remoting.Builder.ProxyBase.ContinueWithResultV2[TRetval](Int32 interfaceId, Int32 methodId, Task`1 task)
   at Temp.MyAppDomain.Utils.DataProvider.GetMyData(String deviceId) in F:\Test\GH-LSU0BCDS-JOB1\Sources\MyLogic\Utils\MyProvider.cs:line 40
   at Temp.MyAppDomain.Logic.GetMyCertificate() in F:\Test\GH-LSU0BCDS-JOB1\Sources\MyLogic\MyLogic.cs:line 462
   at Temp.MyAppDomain.Logic.VerifySignature(Message message) in F:\Test\GH-LSU0BCDS-JOB1\Sources\MyLogic\MyLogic.cs:line 387

1 ответ

Как указывает исключение, сертификат, который вы хотите отправить между службами, не сериализуем, поэтому попытка невозможна. Просто потому, что вы отмечаете это [DataMember]волшебным образом не делает его чем-то, что можно сериализовать.

Вместо этого я предлагаю вам преобразовать ваш сертификат во что-то, что легко сериализуемо, например строку, и вместо этого использовать это значение:

      [DataContract]
public class ClientInfo : IExtensibleDataObject
{

    [DataMember]
    public string ClinetId { get; set; }

    [DataMember]
    public ClientAddress Address { get; set; }

    [DataMember]
    public string Name { get; set; }

    private X509Certificate2 _clientCertificate;
    [IgnoreDataMember]
    public X509Certificate2 ClientCertificate 
    {
         get => _clientCertificate;
         set 
         {
              _clientCertificate = value;

              //Set the PEM value - from https://stackoverflow.com/a/4740292/845356
              var sb = new StringBuilder();
              sb.AppendLine("-----BEGIN CERTIFICATE-----");
              sb.AppendLine(Convert.ToBase64String(value.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks));
              sb.AppendLine("-----END CERTIFICATE-----");
              CertificateAsPem = sb.ToString();
         }
    }

    [DataMember]
    public string CertificateAsPem { get; set; }

    public virtual ExtensionDataObject ExtensionData { get; set; }
}
Другие вопросы по тегам