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 Remoting не поддерживается в .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


person user3771120    schedule 12.04.2021    source источник


Ответы (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; }
}
person Whit Waldo    schedule 13.07.2021