internal X500RelativeDistinguishedName(ReadOnlyMemory <byte> rawData) { RawData = rawData; ReadOnlySpan <byte> rawDataSpan = rawData.Span; AsnValueReader outer = new AsnValueReader(rawDataSpan, AsnEncodingRules.DER); // Windows does not enforce the sort order on multi-value RDNs. AsnValueReader rdn = outer.ReadSetOf(skipSortOrderValidation: true); AsnValueReader typeAndValue = rdn.ReadSequence(); Oid firstType = Oids.GetSharedOrNewOid(ref typeAndValue); ReadOnlySpan <byte> firstValue = typeAndValue.ReadEncodedValue(); typeAndValue.ThrowIfNotEmpty(); if (rdn.HasData) { do { typeAndValue = rdn.ReadSequence(); // Check that the attribute type is a valid OID, // if it's from the cache, even better (faster, lower alloc). if (Oids.GetSharedOrNullOid(ref typeAndValue) is null) { typeAndValue.ReadObjectIdentifier(); } typeAndValue.ReadEncodedValue(); typeAndValue.ThrowIfNotEmpty(); }while (rdn.HasData); } else { _singleElementType = firstType; bool overlaps = rawDataSpan.Overlaps(firstValue, out int offset); Debug.Assert(overlaps, "AsnValueReader.ReadEncodedValue returns a slice of the source"); Debug.Assert(offset > 0); _singleElementValue = rawData.Slice(offset, firstValue.Length); } }
/// <summary> /// Decodes the specified Certificate Revocation List (CRL) and produces /// a <see cref="CertificateRevocationListBuilder" /> with all of the revocation /// entries from the decoded CRL. /// </summary> /// <param name="currentCrl"> /// The DER-encoded CRL to decode. /// </param> /// <param name="currentCrlNumber"> /// When this method returns, contains the CRL sequence number from the decoded CRL. /// This parameter is treated as uninitialized. /// </param> /// <param name="bytesConsumed"> /// When this method returns, contains the number of bytes that were read from /// <paramref name="currentCrl"/> while decoding. /// </param> /// <returns> /// A new builder that has the same revocation entries as the decoded CRL. /// </returns> /// <exception cref="CryptographicException"> /// <paramref name="currentCrl" /> could not be decoded. /// </exception> public static CertificateRevocationListBuilder Load( ReadOnlySpan <byte> currentCrl, out BigInteger currentCrlNumber, out int bytesConsumed) { List <RevokedCertificate> list = new(); BigInteger crlNumber = 0; int payloadLength; try { AsnValueReader reader = new AsnValueReader(currentCrl, AsnEncodingRules.DER); payloadLength = reader.PeekEncodedValue().Length; AsnValueReader certificateList = reader.ReadSequence(); AsnValueReader tbsCertList = certificateList.ReadSequence(); AlgorithmIdentifierAsn.Decode(ref certificateList, ReadOnlyMemory <byte> .Empty, out _); if (!certificateList.TryReadPrimitiveBitString(out _, out _)) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } certificateList.ThrowIfNotEmpty(); int version = 0; if (tbsCertList.PeekTag().HasSameClassAndValue(Asn1Tag.Integer)) { // https://datatracker.ietf.org/doc/html/rfc5280#section-5.1 says the only // version values are v1 (0) and v2 (1). // // Since v1 (0) is supposed to not write down the version value, v2 (1) is the // only legal value to read. if (!tbsCertList.TryReadInt32(out version) || version != 1) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } } AlgorithmIdentifierAsn.Decode(ref tbsCertList, ReadOnlyMemory <byte> .Empty, out _); // X500DN tbsCertList.ReadSequence(); // thisUpdate ReadX509Time(ref tbsCertList); // nextUpdate ReadX509TimeOpt(ref tbsCertList); AsnValueReader revokedCertificates = default; if (tbsCertList.HasData && tbsCertList.PeekTag().HasSameClassAndValue(Asn1Tag.Sequence)) { revokedCertificates = tbsCertList.ReadSequence(); } if (version > 0 && tbsCertList.HasData) { AsnValueReader crlExtensionsExplicit = tbsCertList.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); AsnValueReader crlExtensions = crlExtensionsExplicit.ReadSequence(); crlExtensionsExplicit.ThrowIfNotEmpty(); while (crlExtensions.HasData) { AsnValueReader extension = crlExtensions.ReadSequence(); Oid? extnOid = Oids.GetSharedOrNullOid(ref extension); if (extnOid is null) { extension.ReadObjectIdentifier(); } if (extension.PeekTag().HasSameClassAndValue(Asn1Tag.Boolean)) { extension.ReadBoolean(); } if (!extension.TryReadPrimitiveOctetString(out ReadOnlySpan <byte> extnValue)) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } // Since we're only matching against OIDs that come from GetSharedOrNullOid // we can use ReferenceEquals and skip the Value string equality check in // the Oid.ValueEquals extension method (as it will always be preempted by // the ReferenceEquals or will evaulate to false). if (ReferenceEquals(extnOid, Oids.CrlNumberOid)) { AsnValueReader crlNumberReader = new AsnValueReader( extnValue, AsnEncodingRules.DER); crlNumber = crlNumberReader.ReadInteger(); crlNumberReader.ThrowIfNotEmpty(); } } } tbsCertList.ThrowIfNotEmpty(); while (revokedCertificates.HasData) { RevokedCertificate revokedCertificate = new RevokedCertificate(ref revokedCertificates, version); list.Add(revokedCertificate); } } catch (AsnContentException e) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } bytesConsumed = payloadLength; currentCrlNumber = crlNumber; return(new CertificateRevocationListBuilder(list)); }