diff --git a/src/System.ServiceModel.Primitives/src/System/ServiceModel/X509CertificateEndpointIdentity.cs b/src/System.ServiceModel.Primitives/src/System/ServiceModel/X509CertificateEndpointIdentity.cs index e7665da21c0..85775fe5a38 100644 --- a/src/System.ServiceModel.Primitives/src/System/ServiceModel/X509CertificateEndpointIdentity.cs +++ b/src/System.ServiceModel.Primitives/src/System/ServiceModel/X509CertificateEndpointIdentity.cs @@ -61,8 +61,7 @@ internal X509CertificateEndpointIdentity(XmlDictionaryReader reader) reader.ReadStartElement(XD.XmlSignatureDictionary.X509Data, XD.XmlSignatureDictionary.Namespace); while (reader.IsStartElement(XD.XmlSignatureDictionary.X509Certificate, XD.XmlSignatureDictionary.Namespace)) { - reader.MoveToContent(); - X509Certificate2 certificate = new X509Certificate2(Convert.FromBase64String(reader.ReadContentAsString())); + X509Certificate2 certificate = new X509Certificate2(Convert.FromBase64String(reader.ReadElementString())); if (Certificates.Count == 0) { // This is the first certificate. We assume this as the primary diff --git a/src/System.ServiceModel.Primitives/tests/ServiceModel/X509CertificateEndpointIdentityTest.cs b/src/System.ServiceModel.Primitives/tests/ServiceModel/X509CertificateEndpointIdentityTest.cs new file mode 100644 index 00000000000..31708d5f121 --- /dev/null +++ b/src/System.ServiceModel.Primitives/tests/ServiceModel/X509CertificateEndpointIdentityTest.cs @@ -0,0 +1,107 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.ServiceModel; +using System.Xml; +using Infrastructure.Common; +using Xunit; + +public class X509CertificateEndpointIdentityTest : ConditionalWcfTest +{ + [WcfFact] + [Issue(3572, OS = OSID.OSX)] + public static void X509Certificate_RoundTrip_Succeeds() + { + // Create a self-signed certificate for testing + X509Certificate2 certificate = CreateTestCertificate(); + + // Manually construct the XML that would be sent from the server + // This ensures we're testing deserialization independently of WriteContentsTo + string certificateBase64 = Convert.ToBase64String(certificate.RawData); + string xml = $@" + + + {certificateBase64} + + +"; + + // Deserialize from XML + X509CertificateEndpointIdentity deserializedIdentity; + using (var reader = XmlReader.Create(new StringReader(xml))) + { + // This uses the internal constructor that reads from XmlDictionaryReader + // ReadIdentity expects to read the Identity element and its contents + deserializedIdentity = (X509CertificateEndpointIdentity)typeof(EndpointIdentity) + .GetMethod("ReadIdentity", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static) + .Invoke(null, new object[] { XmlDictionaryReader.CreateDictionaryReader(reader) }); + } + + // Verify the certificate was correctly deserialized + Assert.NotNull(deserializedIdentity); + X509Certificate2 deserializedCert = Assert.Single(deserializedIdentity.Certificates); + Assert.Equal(certificate.Thumbprint, deserializedCert.Thumbprint); + Assert.Equal(certificate.GetCertHash(), deserializedCert.GetCertHash()); + } + + [WcfFact] + [Issue(3572, OS = OSID.OSX)] + public static void X509Certificate_Multiple_RoundTrip_Succeeds() + { + // Create two test certificates + X509Certificate2 primaryCert = CreateTestCertificate(); + X509Certificate2 supportingCert = CreateTestCertificate(); + + // Manually construct the XML that would be sent from the server with multiple certificates + // This ensures we're testing deserialization independently of WriteContentsTo + string primaryCertBase64 = Convert.ToBase64String(primaryCert.RawData); + string supportingCertBase64 = Convert.ToBase64String(supportingCert.RawData); + string xml = $@" + + + {primaryCertBase64} + {supportingCertBase64} + + +"; + + // Deserialize from XML + X509CertificateEndpointIdentity deserializedIdentity; + using (var reader = XmlReader.Create(new StringReader(xml))) + { + // This uses the internal constructor that reads from XmlDictionaryReader + // ReadIdentity expects to read the Identity element and its contents + deserializedIdentity = (X509CertificateEndpointIdentity)typeof(EndpointIdentity) + .GetMethod("ReadIdentity", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static) + .Invoke(null, new object[] { XmlDictionaryReader.CreateDictionaryReader(reader) }); + } + + // Verify both certificates were correctly deserialized + Assert.NotNull(deserializedIdentity); + Assert.Equal(2, deserializedIdentity.Certificates.Count); + Assert.Equal(primaryCert.Thumbprint, deserializedIdentity.Certificates[0].Thumbprint); + Assert.Equal(supportingCert.Thumbprint, deserializedIdentity.Certificates[1].Thumbprint); + } + + private static X509Certificate2 CreateTestCertificate() + { + // Create a simple self-signed certificate for testing + using (RSA rsa = RSA.Create(2048)) + { + var request = new CertificateRequest( + "CN=Test Certificate", + rsa, + HashAlgorithmName.SHA256, + RSASignaturePadding.Pkcs1); + + return request.CreateSelfSigned( + DateTimeOffset.UtcNow.AddDays(-1), + DateTimeOffset.UtcNow.AddDays(365)); + } + } +}