From: Jeremy Barton Date: Wed, 27 Jul 2022 16:56:05 +0000 (-0700) Subject: Add BuildCrlDistributionPointExtension X-Git-Tag: accepted/tizen/unified/riscv/20231226.055536~7501 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=fc7577bea2baaa61685ca80257b860151daf5567;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Add BuildCrlDistributionPointExtension --- diff --git a/src/libraries/Common/src/System/Security/Cryptography/Oids.Shared.cs b/src/libraries/Common/src/System/Security/Cryptography/Oids.Shared.cs index 87cb468..59ad3a9 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Oids.Shared.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Oids.Shared.cs @@ -32,6 +32,7 @@ namespace System.Security.Cryptography private static volatile Oid? s_authorityKeyIdentifierOid; private static volatile Oid? s_authorityInformationAccessOid; private static volatile Oid? s_crlNumberOid; + private static volatile Oid? s_crlDistributionPointOid; private static volatile Oid? s_commonNameOid; private static volatile Oid? s_countryOrRegionOid; private static volatile Oid? s_localityNameOid; @@ -66,6 +67,7 @@ namespace System.Security.Cryptography internal static Oid SubjectAltNameOid => s_subjectAltNameOid ??= InitializeOid(SubjectAltName); internal static Oid AuthorityInformationAccessOid => s_authorityInformationAccessOid ??= InitializeOid(AuthorityInformationAccess); internal static Oid CrlNumberOid => s_crlNumberOid ??= InitializeOid(CrlNumber); + internal static Oid CrlDistributionPointsOid => s_crlDistributionPointOid ??= InitializeOid(CrlDistributionPoints); internal static Oid CommonNameOid => s_commonNameOid ??= InitializeOid(CommonName); internal static Oid CountryOrRegionNameOid => s_countryOrRegionOid ??= InitializeOid(CountryOrRegionName); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/CertificateAuthority.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/CertificateAuthority.cs index 0140132..4fed67e 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/CertificateAuthority.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/CertificateAuthority.cs @@ -703,33 +703,7 @@ SingleResponse ::= SEQUENCE { private static X509Extension CreateCdpExtension(string cdp) { - AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); - - // SEQUENCE OF - using (writer.PushSequence()) - { - // DistributionPoint - using (writer.PushSequence()) - { - // Because DistributionPointName is a CHOICE type this tag is explicit. - // (ITU-T REC X.680-201508 C.3.2.2(g)(3rd bullet)) - // distributionPoint [0] DistributionPointName - using (writer.PushSequence(s_context0)) - { - // [0] DistributionPointName (GeneralNames (SEQUENCE OF)) - using (writer.PushSequence(s_context0)) - { - // GeneralName ([6] IA5String) - writer.WriteCharacterString( - UniversalTagNumber.IA5String, - cdp, - new Asn1Tag(TagClass.ContextSpecific, 6)); - } - } - } - } - - return new X509Extension("2.5.29.31", writer.Encode(), false); + return CertificateRevocationListBuilder.BuildCrlDistributionPointExtension(new[] { cdp }); } private X509Extension CreateAkidExtension() diff --git a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs index 8321a27..1e63f64 100644 --- a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs +++ b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs @@ -2473,6 +2473,7 @@ namespace System.Security.Cryptography.X509Certificates public void AddEntry(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, System.DateTimeOffset? revocationTime = default(System.DateTimeOffset?), System.Security.Cryptography.X509Certificates.X509RevocationReason? reason = default(System.Security.Cryptography.X509Certificates.X509RevocationReason?)) { } public byte[] Build(System.Security.Cryptography.X509Certificates.X500DistinguishedName issuerName, System.Security.Cryptography.X509Certificates.X509SignatureGenerator generator, System.Numerics.BigInteger crlNumber, System.DateTimeOffset nextUpdate, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.X509Certificates.X509AuthorityKeyIdentifierExtension authorityKeyIdentifier, System.DateTimeOffset? thisUpdate = default(System.DateTimeOffset?)) { throw null; } public byte[] Build(System.Security.Cryptography.X509Certificates.X509Certificate2 issuerCertificate, System.Numerics.BigInteger crlNumber, System.DateTimeOffset nextUpdate, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding? rsaSignaturePadding = null, System.DateTimeOffset? thisUpdate = default(System.DateTimeOffset?)) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Extension BuildCrlDistributionPointExtension(System.Collections.Generic.IEnumerable uris, bool critical = false) { throw null; } public static System.Security.Cryptography.X509Certificates.CertificateRevocationListBuilder Load(byte[] currentCrl, out System.Numerics.BigInteger currentCrlNumber) { throw null; } public static System.Security.Cryptography.X509Certificates.CertificateRevocationListBuilder Load(System.ReadOnlySpan currentCrl, out System.Numerics.BigInteger currentCrlNumber, out int bytesConsumed) { throw null; } public static System.Security.Cryptography.X509Certificates.CertificateRevocationListBuilder LoadPem(System.ReadOnlySpan currentCrl, out System.Numerics.BigInteger currentCrlNumber) { throw null; } diff --git a/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx index fb9fdee..1c1b430 100644 --- a/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx @@ -627,6 +627,12 @@ The provided certificate does not have a Subject Key Identifier extension. + + The collection of distribution URIs must be non-empty. + + + One of the provided CRL Distribution Point URIs is a null value. + Certificate '{0}' is corrupted. diff --git a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj index 8b7ade8..438f9a7 100644 --- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj +++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj @@ -418,6 +418,7 @@ + diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateRevocationListBuilder.CdpExtension.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateRevocationListBuilder.CdpExtension.cs new file mode 100644 index 0000000..a6b2380 --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateRevocationListBuilder.CdpExtension.cs @@ -0,0 +1,115 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Formats.Asn1; +using Internal.Cryptography; + +namespace System.Security.Cryptography.X509Certificates +{ + public sealed partial class CertificateRevocationListBuilder + { + /// + /// Builds a CRL Distribution Point Extension with the specified retrieval URIs. + /// + /// + /// The URIs to include as distribution points for the relevant Certificate + /// Revocation List (CRL). + /// + /// + /// to mark the extension as critical; + /// otherwise, . + /// The default is . + /// + /// + /// An object suitable for use as a CRL Distribution Point Extension. + /// + /// + /// is . + /// + /// + /// + /// contains a value. + /// + /// - or - + /// + /// is empty. + /// + /// + /// + /// One of the values in + /// contains characters outside of the International Alphabet 5 (IA5) character space + /// (which is equivalent to 7-bit US-ASCII). + /// + public static X509Extension BuildCrlDistributionPointExtension( + IEnumerable uris, + bool critical = false) + { + ArgumentNullException.ThrowIfNull(uris); + + // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + // + // DistributionPoint::= SEQUENCE { + // distributionPoint[0] DistributionPointName OPTIONAL, + // reasons[1] ReasonFlags OPTIONAL, + // cRLIssuer[2] GeneralNames OPTIONAL } + + // DistributionPointName::= CHOICE { + // fullName[0] GeneralNames, + // nameRelativeToCRLIssuer[1] RelativeDistinguishedName } + + AsnWriter? writer = null; + + foreach (string uri in uris) + { + if (uri is null) + { + throw new ArgumentException(SR.Cryptography_X509_CDP_NullValue, nameof(uris)); + } + + if (writer is null) + { + writer = new AsnWriter(AsnEncodingRules.DER); + // CRLDistributionPoints + writer.PushSequence(); + } + + // DistributionPoint + using (writer.PushSequence()) + { + // DistributionPoint/DistributionPointName EXPLICIT [0] + using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0))) + { + // DistributionPointName/GeneralName + using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0))) + { + // GeneralName/Uri + try + { + writer.WriteCharacterString( + UniversalTagNumber.IA5String, + uri, + new Asn1Tag(TagClass.ContextSpecific, 6)); + } + catch (System.Text.EncoderFallbackException e) + { + throw new CryptographicException(SR.Cryptography_Invalid_IA5String, e); + } + } + } + } + } + + if (writer is null) + { + throw new ArgumentException(SR.Cryptography_X509_CDP_MustNotBuildEmpty, nameof(uris)); + } + + // CRLDistributionPoints + writer.PopSequence(); + + byte[] encoded = writer.Encode(); + return new X509Extension(Oids.CrlDistributionPointsOid, encoded, critical); + } + } +} diff --git a/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj b/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj index f65e905..f2d2bc4 100644 --- a/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj +++ b/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj @@ -289,6 +289,7 @@ + diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CrlDistPointBuilderTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CrlDistPointBuilderTests.cs new file mode 100644 index 0000000..239cf6c --- /dev/null +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CrlDistPointBuilderTests.cs @@ -0,0 +1,111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.X509Certificates.Tests +{ + public static class CrlDistPointBuilderTests + { + [Fact] + public static void NullEnumerable() + { + Assert.Throws( + "uris", + () => CertificateRevocationListBuilder.BuildCrlDistributionPointExtension(null)); + } + + [Fact] + public static void NullUriInEnumerable() + { + Assert.Throws( + "uris", + () => CertificateRevocationListBuilder.BuildCrlDistributionPointExtension( + new[] + { + "http://cert.example/ca1.crl", + null, + "http://cdn.cert.example/ca1.crl", + })); + } + + [Fact] + public static void BuildEmpty() + { + Assert.Throws( + "uris", + () => CertificateRevocationListBuilder.BuildCrlDistributionPointExtension( + System.Linq.Enumerable.Empty())); + } + + [Fact] + public static void BuildOneEntry() + { + X509Extension ext = CertificateRevocationListBuilder.BuildCrlDistributionPointExtension( + new[] + { + "http://crl.microsoft.com/pki/crl/products/MicCodSigPCA_08-31-2010.crl", + }); + + Assert.False(ext.Critical, "ext.Critical"); + Assert.Equal("2.5.29.31", ext.Oid.Value); + + byte[] expected = ( + "304d304ba049a0478645687474703a2f2f63726c2e6d6963726f736f" + + "66742e636f6d2f706b692f63726c2f70726f64756374732f4d696343" + + "6f645369675043415f30382d33312d323031302e63726c").HexToByteArray(); + + AssertExtensions.SequenceEqual(expected, ext.RawData); + } + + [Fact] + public static void BuildTwoEntries() + { + // Recreate the encoding of the CDP extension from https://crt.sh/?id=3777044 + // (the original wasn't marked as critical, but that doesn't affect the RawData value) + X509Extension ext = CertificateRevocationListBuilder.BuildCrlDistributionPointExtension( + new[] + { + "http://crl3.digicert.com/sha2-ev-server-g1.crl", + "http://crl4.digicert.com/sha2-ev-server-g1.crl", + }, + critical: true); + + Assert.True(ext.Critical, "ext.Critical"); + Assert.Equal("2.5.29.31", ext.Oid.Value); + + byte[] expected = ( + "306C3034A032A030862E687474703A2F2F63726C332E64696769636572742E63" + + "6F6D2F736861322D65762D7365727665722D67312E63726C3034A032A030862E" + + "687474703A2F2F63726C342E64696769636572742E636F6D2F736861322D6576" + + "2D7365727665722D67312E63726C").HexToByteArray(); + + AssertExtensions.SequenceEqual(expected, ext.RawData); + } + + [Fact] + public static void UriNotValidated() + { + X509Extension ext = CertificateRevocationListBuilder.BuildCrlDistributionPointExtension( + new[] + { + "!!!!", + }); + + Assert.Equal("300C300AA008A006860421212121", ext.RawData.ByteArrayToHex()); + } + + [Fact] + public static void OnlyAscii7Permitted() + { + Assert.Throws( + () => CertificateRevocationListBuilder.BuildCrlDistributionPointExtension( + new[] + { + // http://[nihongo].example/ca4.crl + "http://\u65E5\u672C\u8A8E.example/ca4.crl", + })); + } + } +}