private static List <X500RelativeDistinguishedName> ParseAttributes(byte[] rawData) { List <X500RelativeDistinguishedName>?parsedAttributes = null; ReadOnlyMemory <byte> rawDataMemory = rawData; ReadOnlySpan <byte> rawDataSpan = rawData; try { AsnValueReader outer = new AsnValueReader(rawDataSpan, AsnEncodingRules.DER); AsnValueReader sequence = outer.ReadSequence(); outer.ThrowIfNotEmpty(); while (sequence.HasData) { ReadOnlySpan <byte> encodedValue = sequence.PeekEncodedValue(); if (!rawDataSpan.Overlaps(encodedValue, out int offset)) { Debug.Fail("AsnValueReader produced a span outside of the original bounds"); throw new UnreachableException(); } var rdn = new X500RelativeDistinguishedName(rawDataMemory.Slice(offset, encodedValue.Length)); sequence.ReadEncodedValue(); (parsedAttributes ??= new List <X500RelativeDistinguishedName>()).Add(rdn); } } catch (AsnContentException e) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } return(parsedAttributes ?? new List <X500RelativeDistinguishedName>()); }
public static Pkcs8PrivateKeyInfo Decode( ReadOnlyMemory <byte> source, out int bytesRead, bool skipCopy = false) { try { AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.BER); // By using the default/empty ReadOnlyMemory value, the Decode method will have to // make copies of the data when assigning ReadOnlyMemory fields. ReadOnlyMemory <byte> rebind = skipCopy ? source : default; int localRead = reader.PeekEncodedValue().Length; PrivateKeyInfoAsn.Decode(ref reader, rebind, out PrivateKeyInfoAsn privateKeyInfo); bytesRead = localRead; return(new Pkcs8PrivateKeyInfo( new Oid(privateKeyInfo.PrivateKeyAlgorithm.Algorithm, null), privateKeyInfo.PrivateKeyAlgorithm.Parameters, privateKeyInfo.PrivateKey, SignerInfo.MakeAttributeCollection(privateKeyInfo.Attributes))); } catch (AsnContentException e) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } }
protected void ParsePkcs12(ReadOnlySpan <byte> data) { try { // RFC7292 specifies BER instead of DER AsnValueReader reader = new AsnValueReader(data, AsnEncodingRules.BER); // Windows compatibility: Ignore trailing data. ReadOnlySpan <byte> encodedData = reader.PeekEncodedValue(); unsafe { IntPtr tmpPtr = Marshal.AllocHGlobal(encodedData.Length); Span <byte> tmpSpan = new Span <byte>((byte *)tmpPtr, encodedData.Length); encodedData.CopyTo(tmpSpan); _tmpManager = new PointerMemoryManager <byte>((void *)tmpPtr, encodedData.Length); } ReadOnlyMemory <byte> tmpMemory = _tmpManager.Memory; reader = new AsnValueReader(tmpMemory.Span, AsnEncodingRules.BER); PfxAsn.Decode(ref reader, tmpMemory, out PfxAsn pfxAsn); if (pfxAsn.AuthSafe.ContentType != Oids.Pkcs7Data) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } _pfxAsn = pfxAsn; } catch (AsnContentException e) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } }
protected void ParsePkcs12(byte[] data) { try { // RFC7292 specifies BER instead of DER AsnValueReader reader = new AsnValueReader(data, AsnEncodingRules.BER); ReadOnlySpan <byte> encodedData = reader.PeekEncodedValue(); // Windows compatibility: Ignore trailing data. if (encodedData.Length != data.Length) { reader = new AsnValueReader(encodedData, AsnEncodingRules.BER); } PfxAsn.Decode(ref reader, data, out PfxAsn pfxAsn); if (pfxAsn.AuthSafe.ContentType != Oids.Pkcs7Data) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } _pfxAsn = pfxAsn; } catch (AsnContentException e) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } }
private static unsafe int DecodeSubjectPublicKeyInfo( ReadOnlySpan <byte> source, out Oid oid, out AsnEncodedData parameters, out AsnEncodedData keyValue) { fixed(byte *ptr = &MemoryMarshal.GetReference(source)) using (MemoryManager <byte> manager = new PointerMemoryManager <byte>(ptr, source.Length)) { AsnValueReader reader = new AsnValueReader(source, AsnEncodingRules.DER); int read; SubjectPublicKeyInfoAsn spki; try { read = reader.PeekEncodedValue().Length; SubjectPublicKeyInfoAsn.Decode(ref reader, manager.Memory, out spki); } catch (AsnContentException e) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } oid = new Oid(spki.Algorithm.Algorithm, null); parameters = new AsnEncodedData(spki.Algorithm.Parameters?.ToArray() ?? Array.Empty <byte>()); keyValue = new AsnEncodedData(spki.SubjectPublicKey.ToArray()); return(read); } }
private static void ReadEncryptedPkcs8 <TRet>( string[] validOids, ReadOnlyMemory <byte> source, ReadOnlySpan <char> password, ReadOnlySpan <byte> passwordBytes, KeyReader <TRet> keyReader, out int bytesRead, out TRet ret) { int read; EncryptedPrivateKeyInfoAsn epki; try { AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.BER); read = reader.PeekEncodedValue().Length; EncryptedPrivateKeyInfoAsn.Decode(ref reader, source, out epki); } catch (AsnContentException e) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } // No supported encryption algorithms produce more bytes of decryption output than there // were of decryption input. byte[] decrypted = CryptoPool.Rent(epki.EncryptedData.Length); Memory <byte> decryptedMemory = decrypted; try { int decryptedBytes = PasswordBasedEncryption.Decrypt( epki.EncryptionAlgorithm, password, passwordBytes, epki.EncryptedData.Span, decrypted); decryptedMemory = decryptedMemory.Slice(0, decryptedBytes); ReadPkcs8( validOids, decryptedMemory, keyReader, out int innerRead, out ret); if (innerRead != decryptedMemory.Length) { ret = default !;
public static void PeekEncodedValue_InvalidLength() { byte[] badLength = "04040203".HexToByteArray(); AsnReader reader = new AsnReader(badLength, AsnEncodingRules.BER); Assert.Throws <CryptographicException>(() => reader.PeekEncodedValue()); Assert.Throws <CryptographicException>(() => reader.ReadEncodedValue()); Assert.Throws <CryptographicException>( () => { AsnValueReader valueReader = new AsnValueReader(badLength, AsnEncodingRules.BER); valueReader.PeekEncodedValue(); }); Assert.Throws <CryptographicException>( () => { AsnValueReader valueReader = new AsnValueReader(badLength, AsnEncodingRules.BER); valueReader.ReadEncodedValue(); }); }
public static bool TryDecode( ReadOnlyMemory <byte> encodedBytes, [NotNullWhen(true)] out Rfc3161TimestampRequest?request, out int bytesConsumed) { try { // RFC 3161 doesn't have a concise statement that TimeStampReq will // be DER encoded, but under the email protocol (3.1), file protocol (3.2), // socket protocol (3.3) and HTTP protocol (3.4) they all say DER for the // transmission. // // Since nothing says BER, assume DER only. const AsnEncodingRules RuleSet = AsnEncodingRules.DER; AsnValueReader reader = new AsnValueReader(encodedBytes.Span, RuleSet); ReadOnlySpan <byte> firstElement = reader.PeekEncodedValue(); Rfc3161TimeStampReq.Decode(ref reader, encodedBytes, out Rfc3161TimeStampReq req); request = new Rfc3161TimestampRequest { _parsedData = req, _encodedBytes = firstElement.ToArray(), }; bytesConsumed = firstElement.Length; return(true); } catch (CryptographicException) { } request = null; bytesConsumed = 0; return(false); }
private bool ProcessResponse( ReadOnlyMemory <byte> source, [NotNullWhen(true)] out Rfc3161TimestampToken?token, out Rfc3161RequestResponseStatus status, out int bytesConsumed, bool shouldThrow) { status = Rfc3161RequestResponseStatus.Unknown; token = null; Rfc3161TimeStampResp resp; try { AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.DER); int localBytesRead = reader.PeekEncodedValue().Length; Rfc3161TimeStampResp.Decode(ref reader, source, out resp); bytesConsumed = localBytesRead; } catch (CryptographicException) when(!shouldThrow) { bytesConsumed = 0; status = Rfc3161RequestResponseStatus.DoesNotParse; return(false); } // bytesRead will be set past this point PkiStatus pkiStatus = (PkiStatus)resp.Status.Status; if (pkiStatus != PkiStatus.Granted && pkiStatus != PkiStatus.GrantedWithMods) { if (shouldThrow) { throw new CryptographicException( SR.Format( SR.Cryptography_TimestampReq_Failure, pkiStatus, resp.Status.FailInfo.GetValueOrDefault())); } status = Rfc3161RequestResponseStatus.RequestFailed; return(false); } if (!Rfc3161TimestampToken.TryDecode(resp.TimeStampToken.GetValueOrDefault(), out token, out _)) { if (shouldThrow) { throw new CryptographicException(SR.Cryptography_TimestampReq_BadResponse); } bytesConsumed = 0; status = Rfc3161RequestResponseStatus.DoesNotParse; return(false); } status = ValidateResponse(token, shouldThrow); return(status == Rfc3161RequestResponseStatus.Accepted); }
/// <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)); }
private void Decode(ReadOnlySpan <byte> rawData) { _keyIdentifier = null; _simpleIssuer = null; _rawIssuer = null; _serialNumber = null; // https://datatracker.ietf.org/doc/html/rfc3280#section-4.2.1.1 // AuthorityKeyIdentifier ::= SEQUENCE { // keyIdentifier[0] KeyIdentifier OPTIONAL, // authorityCertIssuer[1] GeneralNames OPTIONAL, // authorityCertSerialNumber[2] CertificateSerialNumber OPTIONAL } // // KeyIdentifier::= OCTET STRING try { AsnValueReader reader = new AsnValueReader(rawData, AsnEncodingRules.DER); AsnValueReader aki = reader.ReadSequence(); reader.ThrowIfNotEmpty(); Asn1Tag nextTag = default; if (aki.HasData) { nextTag = aki.PeekTag(); } if (nextTag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) { _keyIdentifier = aki.ReadOctetString(nextTag); if (aki.HasData) { nextTag = aki.PeekTag(); } } if (nextTag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) { byte[] rawIssuer = aki.PeekEncodedValue().ToArray(); _rawIssuer = rawIssuer; AsnValueReader generalNames = aki.ReadSequence(nextTag); bool foundIssuer = false; // Walk all of the entities to make sure they decode legally, so no early abort. while (generalNames.HasData) { GeneralNameAsn.Decode(ref generalNames, rawIssuer, out GeneralNameAsn decoded); if (decoded.DirectoryName.HasValue) { if (!foundIssuer) { // Only ever try reading the first one. // Don't just use a null check or we would load the last of an odd number. foundIssuer = true; _simpleIssuer = new X500DistinguishedName( decoded.DirectoryName.GetValueOrDefault().Span); } else { _simpleIssuer = null; } } } if (aki.HasData) { nextTag = aki.PeekTag(); } } if (nextTag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) { _serialNumber = aki.ReadIntegerBytes(nextTag).ToArray(); } aki.ThrowIfNotEmpty(); } catch (AsnContentException e) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } _decoded = true; }
private static unsafe CertificateRequest LoadSigningRequest( ReadOnlySpan <byte> pkcs10, bool permitTrailingData, HashAlgorithmName signerHashAlgorithm, out int bytesConsumed, CertificateRequestLoadOptions options, RSASignaturePadding?signerSignaturePadding) { ArgumentException.ThrowIfNullOrEmpty(signerHashAlgorithm.Name, nameof(signerHashAlgorithm)); if ((options & ~AllOptions) != 0) { throw new ArgumentOutOfRangeException(nameof(options), options, SR.Argument_InvalidFlag); } bool skipSignatureValidation = (options & CertificateRequestLoadOptions.SkipSignatureValidation) != 0; bool unsafeLoadCertificateExtensions = (options & CertificateRequestLoadOptions.UnsafeLoadCertificateExtensions) != 0; try { AsnValueReader outer = new AsnValueReader(pkcs10, AsnEncodingRules.DER); int encodedLength = outer.PeekEncodedValue().Length; AsnValueReader pkcs10Asn = outer.ReadSequence(); CertificateRequest req; if (!permitTrailingData) { outer.ThrowIfNotEmpty(); } fixed(byte *p10ptr = pkcs10) { using (PointerMemoryManager <byte> manager = new PointerMemoryManager <byte>(p10ptr, encodedLength)) { ReadOnlyMemory <byte> rebind = manager.Memory; ReadOnlySpan <byte> encodedRequestInfo = pkcs10Asn.PeekEncodedValue(); CertificationRequestInfoAsn requestInfo; AlgorithmIdentifierAsn algorithmIdentifier; ReadOnlySpan <byte> signature; int signatureUnusedBitCount; CertificationRequestInfoAsn.Decode(ref pkcs10Asn, rebind, out requestInfo); AlgorithmIdentifierAsn.Decode(ref pkcs10Asn, rebind, out algorithmIdentifier); if (!pkcs10Asn.TryReadPrimitiveBitString(out signatureUnusedBitCount, out signature)) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } pkcs10Asn.ThrowIfNotEmpty(); if (requestInfo.Version < 0) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } // They haven't bumped from v0 to v1 as of 2022. const int MaxSupportedVersion = 0; if (requestInfo.Version != MaxSupportedVersion) { throw new CryptographicException( SR.Format( SR.Cryptography_CertReq_Load_VersionTooNew, requestInfo.Version, MaxSupportedVersion)); } PublicKey publicKey = PublicKey.DecodeSubjectPublicKeyInfo(ref requestInfo.SubjectPublicKeyInfo); if (!skipSignatureValidation) { // None of the supported signature algorithms support signatures that are not full bytes. // So, shortcut the verification on the bit length if (signatureUnusedBitCount != 0 || !VerifyX509Signature(encodedRequestInfo, signature, publicKey, algorithmIdentifier)) { throw new CryptographicException(SR.Cryptography_CertReq_SignatureVerificationFailed); } } X500DistinguishedName subject = new X500DistinguishedName(requestInfo.Subject.Span); req = new CertificateRequest( subject, publicKey, signerHashAlgorithm, signerSignaturePadding); if (requestInfo.Attributes is not null) { bool foundCertExt = false; foreach (AttributeAsn attr in requestInfo.Attributes) { if (attr.AttrType == Oids.Pkcs9ExtensionRequest) { if (foundCertExt) { throw new CryptographicException( SR.Cryptography_CertReq_Load_DuplicateExtensionRequests); } foundCertExt = true; if (attr.AttrValues.Length != 1) { throw new CryptographicException( SR.Cryptography_CertReq_Load_DuplicateExtensionRequests); } AsnValueReader extsReader = new AsnValueReader( attr.AttrValues[0].Span, AsnEncodingRules.DER); AsnValueReader exts = extsReader.ReadSequence(); extsReader.ThrowIfNotEmpty(); // Minimum length is 1, so do..while do { X509ExtensionAsn.Decode(ref exts, rebind, out X509ExtensionAsn extAsn); if (unsafeLoadCertificateExtensions) { X509Extension ext = new X509Extension( extAsn.ExtnId, extAsn.ExtnValue.Span, extAsn.Critical); X509Extension?rich = X509Certificate2.CreateCustomExtensionIfAny(extAsn.ExtnId); if (rich is not null) { rich.CopyFrom(ext); req.CertificateExtensions.Add(rich); } else { req.CertificateExtensions.Add(ext); } } } while (exts.HasData); } else { if (attr.AttrValues.Length == 0) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } foreach (ReadOnlyMemory <byte> val in attr.AttrValues) { req.OtherRequestAttributes.Add( new AsnEncodedData(attr.AttrType, val.Span)); } } } } } } bytesConsumed = encodedLength; return(req); } catch (AsnContentException e) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } }