diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs index c21beabe1e577f..5c72917edb960a 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs @@ -186,10 +186,23 @@ public IntPtr Handle } } + public string Issuer + { + get + { + EnsureCertData(); + return _certData.IssuerName; + } + } - public string Issuer => IssuerName.Name; - - public string Subject => SubjectName.Name; + public string Subject + { + get + { + EnsureCertData(); + return _certData.SubjectName; + } + } public string LegacyIssuer => IssuerName.Decode(X500DistinguishedNameFlags.None); @@ -323,7 +336,7 @@ public byte[] RawData get { EnsureCertData(); - return _certData.RawData; + return _certData.RawData.CloneByteArray(); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/CertificateData.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/CertificateData.cs index dded057cc62e89..613976dc7c8854 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/CertificateData.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/CertificateData.cs @@ -50,6 +50,8 @@ public AlgorithmIdentifier(AlgorithmIdentifierAsn algorithmIdentifier) internal X500DistinguishedName Issuer; internal X500DistinguishedName Subject; internal List Extensions; + internal string IssuerName; + internal string SubjectName; internal int Version => certificate.TbsCertificate.Version; @@ -82,6 +84,8 @@ internal CertificateData(byte[] rawData) certificate.TbsCertificate.ValidateVersion(); Issuer = new X500DistinguishedName(certificate.TbsCertificate.Issuer.ToArray()); Subject = new X500DistinguishedName(certificate.TbsCertificate.Subject.ToArray()); + IssuerName = Issuer.Name; + SubjectName = Subject.Name; AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); certificate.TbsCertificate.SubjectPublicKeyInfo.Encode(writer); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509CertificateReader.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509CertificateReader.cs index b5c6809a8c2fb6..15e85b2606c106 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509CertificateReader.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509CertificateReader.cs @@ -24,6 +24,8 @@ internal sealed class OpenSslX509CertificateReader : ICertificatePal private SafeEvpPKeyHandle? _privateKey; private X500DistinguishedName? _subjectName; private X500DistinguishedName? _issuerName; + private string? _subject; + private string? _issuer; public static ICertificatePal FromHandle(IntPtr handle) { @@ -259,9 +261,37 @@ internal SafeX509Handle SafeHandle get { return _cert; } } - public string Issuer => IssuerName.Name; + public string Issuer + { + get + { + if (_issuer == null) + { + // IssuerName is mutable to callers in X509Certificate. We want to be + // able to get the issuer even if IssuerName has been mutated, so we + // don't use it here. + _issuer = Interop.Crypto.LoadX500Name(Interop.Crypto.X509GetIssuerName(_cert)).Name; + } + + return _issuer; + } + } - public string Subject => SubjectName.Name; + public string Subject + { + get + { + if (_subject == null) + { + // SubjectName is mutable to callers in X509Certificate. We want to be + // able to get the subject even if SubjectName has been mutated, so we + // don't use it here. + _subject = Interop.Crypto.LoadX500Name(Interop.Crypto.X509GetSubjectName(_cert)).Name; + } + + return _subject; + } + } public string LegacyIssuer => IssuerName.Decode(X500DistinguishedNameFlags.None); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CertTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CertTests.cs index 76dd7a64d9d8fc..59c8b93b919dae 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CertTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CertTests.cs @@ -434,6 +434,39 @@ public static void SerializedCertDisposeDoesNotRemoveKeyFile() } } + [Fact] + public static void CopyResult_RawData() + { + using (X509Certificate2 cert = new X509Certificate2(TestData.MsCertificate)) + { + byte[] first = cert.RawData; + byte[] second = cert.RawData; + Assert.NotSame(first, second); + } + } + + [Fact] + public static void MutateDistinguishedName_IssuerName_DoesNotImpactIssuer() + { + using (X509Certificate2 cert = new X509Certificate2(TestData.MsCertificate)) + { + byte[] issuerBytes = cert.IssuerName.RawData; + Array.Clear(issuerBytes, 0, issuerBytes.Length); + Assert.Equal("CN=Microsoft Code Signing PCA, O=Microsoft Corporation, L=Redmond, S=Washington, C=US", cert.Issuer); + } + } + + [Fact] + public static void MutateDistinguishedName_SubjectName_DoesNotImpactSubject() + { + using (X509Certificate2 cert = new X509Certificate2(TestData.MsCertificate)) + { + byte[] subjectBytes = cert.SubjectName.RawData; + Array.Clear(subjectBytes, 0, subjectBytes.Length); + Assert.Equal("CN=Microsoft Corporation, OU=MOPR, O=Microsoft Corporation, L=Redmond, S=Washington, C=US", cert.Subject); + } + } + public static IEnumerable StorageFlags => CollectionImportTests.StorageFlags; } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ExportTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ExportTests.cs index 30a1b19bb6893b..9cbd31aed11495 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ExportTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ExportTests.cs @@ -8,6 +8,17 @@ namespace System.Security.Cryptography.X509Certificates.Tests { public static class ExportTests { + [Fact] + public static void ExportAsCert_CreatesCopy() + { + using (X509Certificate2 cert = new X509Certificate2(TestData.MsCertificate)) + { + byte[] first = cert.Export(X509ContentType.Cert); + byte[] second = cert.Export(X509ContentType.Cert); + Assert.NotSame(first, second); + } + } + [Fact] public static void ExportAsCert() {