Beispiel #1
0
        private AsnWriter WritePkcs8()
        {
            PrivateKeyInfoAsn info = new PrivateKeyInfoAsn
            {
                PrivateKeyAlgorithm =
                {
                    Algorithm = AlgorithmId,
                },
                PrivateKey = PrivateKeyBytes,
            };

            if (AlgorithmParameters?.Length > 0)
            {
                info.PrivateKeyAlgorithm.Parameters = AlgorithmParameters;
            }

            if (Attributes.Count > 0)
            {
                info.Attributes = PkcsHelpers.NormalizeAttributeSet(CmsSigner.BuildAttributes(Attributes).ToArray());
            }

            // Write in BER in case any of the provided fields was BER.
            AsnWriter writer = new AsnWriter(AsnEncodingRules.BER);

            info.Encode(writer);
            return(writer);
        }
        public bool MatchesCertificate(X509Certificate2 certificate)
        {
            switch (Type)
            {
            case SubjectIdentifierType.IssuerAndSerialNumber:
            {
                X509IssuerSerial issuerSerial     = (X509IssuerSerial)Value;
                byte[]           serialNumber     = issuerSerial.SerialNumber.ToSerialBytes();
                string           issuer           = issuerSerial.IssuerName;
                byte[]           certSerialNumber = certificate.GetSerialNumber();

                return(PkcsHelpers.AreByteArraysEqual(certSerialNumber, serialNumber) && certificate.Issuer == issuer);
            }

            case SubjectIdentifierType.SubjectKeyIdentifier:
            {
                string skiString    = (string)Value;
                byte[] ski          = skiString.ToSkiBytes();
                byte[] candidateSki = PkcsPal.Instance.GetSubjectKeyIdentifier(certificate);

                return(PkcsHelpers.AreByteArraysEqual(ski, candidateSki));
            }

            default:
                // SubjectIdentifier can only be created by this package so if this an invalid type, it's the package's fault.
                Debug.Fail($"Invalid SubjectIdentifierType: {Type}");
                throw new CryptographicException();
            }
        }
Beispiel #3
0
        private bool VerifyData(ReadOnlySpan <byte> data)
        {
            Oid hashAlgorithmId = TokenInfo.HashAlgorithmId;
            HashAlgorithmName hashAlgorithmName = PkcsHelpers.GetDigestAlgorithm(hashAlgorithmId);

            using (IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithmName))
            {
                hasher.AppendData(data);

                // SHA-2-512 is the biggest hash we currently know about.
                Span <byte> stackSpan = stackalloc byte[512 / 8];

                if (hasher.TryGetHashAndReset(stackSpan, out int bytesWritten))
                {
                    return(VerifyHash(stackSpan.Slice(0, bytesWritten), hashAlgorithmId.Value));
                }

                // Something we understood, but is bigger than 512-bit.
                // Allocate at runtime, trip in a debug build so we can re-evaluate this.
                Debug.Fail(
                    $"TryGetHashAndReset did not fit in {stackSpan.Length} for hash {hashAlgorithmId.Value}");

                return(VerifyHash(hasher.GetHashAndReset(), hashAlgorithmId.Value));
            }
        }
        internal Pkcs12SafeContents(ContentInfoAsn contentInfoAsn)
        {
            IsReadOnly = true;

            switch (contentInfoAsn.ContentType)
            {
            case Oids.Pkcs7Encrypted:
                ConfidentialityMode = Pkcs12ConfidentialityMode.Password;
                _encrypted          = contentInfoAsn.Content;
                break;

            case Oids.Pkcs7Enveloped:
                ConfidentialityMode = Pkcs12ConfidentialityMode.PublicKey;
                _encrypted          = contentInfoAsn.Content;
                break;

            case Oids.Pkcs7Data:
                ConfidentialityMode = Pkcs12ConfidentialityMode.None;
                _bags = ReadBags(PkcsHelpers.DecodeOctetStringAsMemory(contentInfoAsn.Content));
                break;

            default:
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
            }
        }
Beispiel #5
0
        public byte[] Encode()
        {
            if (!_hasData)
            {
                throw new InvalidOperationException(SR.Cryptography_Cms_MessageNotSigned);
            }

            return(PkcsHelpers.EncodeContentInfo(_signedData, Oids.Pkcs7Signed));
        }
Beispiel #6
0
        public X509Certificate2 GetCertificate()
        {
            if (!IsX509Certificate)
            {
                throw new InvalidOperationException(SR.Cryptography_Pkcs12_CertBagNotX509);
            }

            return(new X509Certificate2(PkcsHelpers.DecodeOctetString(_decoded.CertValue)));
        }
        //
        // Private methods.
        //

        private static DateTime Decode(byte[]?rawData)
        {
            if (rawData == null)
            {
                return(default(DateTime));
            }

            return(PkcsHelpers.DecodeUtcTime(rawData));
        }
 public sealed unsafe override byte[] Encrypt(CmsRecipientCollection recipients, ContentInfo contentInfo, AlgorithmIdentifier contentEncryptionAlgorithm, X509Certificate2Collection originatorCerts, CryptographicAttributeObjectCollection unprotectedAttributes)
 {
     using (SafeCryptMsgHandle hCryptMsg = EncodeHelpers.CreateCryptMsgHandleToEncode(recipients, contentInfo.ContentType, contentEncryptionAlgorithm, originatorCerts, unprotectedAttributes))
     {
         byte[] encodedContent;
         if (contentInfo.ContentType.Value !.Equals(Oids.Pkcs7Data, StringComparison.OrdinalIgnoreCase))
         {
             encodedContent = PkcsHelpers.EncodeOctetString(contentInfo.Content);
         }
Beispiel #9
0
        private static byte[]? Decode(byte[]?rawData)
        {
            if (rawData == null)
            {
                return(null);
            }

            return(PkcsHelpers.DecodeOctetString(rawData));
        }
Beispiel #10
0
        //
        // Private methods.
        //

        private static string Decode(byte[] rawData)
        {
            if (rawData == null)
            {
                return(null);
            }

            byte[] octets = PkcsHelpers.DecodeOctetString(rawData);
            return(octets.OctetStringToUnicode());
        }
Beispiel #11
0
        private static byte[] Encode(string documentDescription)
        {
            if (documentDescription == null)
            {
                throw new ArgumentNullException(nameof(documentDescription));
            }

            byte[] octets = documentDescription.UnicodeToOctetString();
            return(PkcsHelpers.EncodeOctetString(octets));
        }
Beispiel #12
0
        private static Oid?Decode(byte[]?rawData)
        {
            if (rawData == null)
            {
                return(null);
            }

            string contentTypeValue = PkcsHelpers.DecodeOid(rawData);

            return(new Oid(contentTypeValue));
        }
        private Exception?TryDecryptAgree(KeyAgreeRecipientInfo keyAgreeRecipientInfo, SafeProvOrNCryptKeyHandle hKey, CryptKeySpec keySpec, X509Certificate2Collection originatorCerts, X509Certificate2Collection extraStore)
        {
            unsafe
            {
                KeyAgreeRecipientInfoPalWindows pal = (KeyAgreeRecipientInfoPalWindows)(keyAgreeRecipientInfo.Pal);
                return(pal.WithCmsgCmsRecipientInfo <Exception?>(
                           delegate(CMSG_KEY_AGREE_RECIPIENT_INFO * pKeyAgreeRecipientInfo)
                {
                    CMSG_CTRL_KEY_AGREE_DECRYPT_PARA decryptPara = default(CMSG_CTRL_KEY_AGREE_DECRYPT_PARA);
                    decryptPara.cbSize = Marshal.SizeOf <CMSG_CTRL_KEY_AGREE_DECRYPT_PARA>();
                    decryptPara.hProv = hKey;
                    decryptPara.dwKeySpec = keySpec;
                    decryptPara.pKeyAgree = pKeyAgreeRecipientInfo;
                    decryptPara.dwRecipientIndex = pal.Index;
                    decryptPara.dwRecipientEncryptedKeyIndex = pal.SubIndex;
                    CMsgKeyAgreeOriginatorChoice originatorChoice = pKeyAgreeRecipientInfo->dwOriginatorChoice;
                    switch (originatorChoice)
                    {
                    case CMsgKeyAgreeOriginatorChoice.CMSG_KEY_AGREE_ORIGINATOR_CERT:
                        {
                            X509Certificate2Collection candidateCerts = new X509Certificate2Collection();
                            candidateCerts.AddRange(PkcsHelpers.GetStoreCertificates(StoreName.AddressBook, StoreLocation.CurrentUser, openExistingOnly: true));
                            candidateCerts.AddRange(PkcsHelpers.GetStoreCertificates(StoreName.AddressBook, StoreLocation.LocalMachine, openExistingOnly: true));
                            candidateCerts.AddRange(originatorCerts);
                            candidateCerts.AddRange(extraStore);
                            SubjectIdentifier originatorId = pKeyAgreeRecipientInfo->OriginatorCertId.ToSubjectIdentifier();
                            X509Certificate2?originatorCert = candidateCerts.TryFindMatchingCertificate(originatorId);
                            if (originatorCert == null)
                            {
                                return ErrorCode.CRYPT_E_NOT_FOUND.ToCryptographicException();
                            }
                            using (SafeCertContextHandle hCertContext = originatorCert.CreateCertContextHandle())
                            {
                                CERT_CONTEXT *pOriginatorCertContext = hCertContext.DangerousGetCertContext();
                                decryptPara.OriginatorPublicKey = pOriginatorCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey;

                                // Do not factor this call out of the switch statement as leaving this "using" block will free up
                                // native memory that decryptPara points to.
                                return TryExecuteDecryptAgree(ref decryptPara);
                            }
                        }

                    case CMsgKeyAgreeOriginatorChoice.CMSG_KEY_AGREE_ORIGINATOR_PUBLIC_KEY:
                        {
                            decryptPara.OriginatorPublicKey = pKeyAgreeRecipientInfo->OriginatorPublicKeyInfo.PublicKey;
                            return TryExecuteDecryptAgree(ref decryptPara);
                        }

                    default:
                        return new CryptographicException(SR.Format(SR.Cryptography_Cms_Invalid_Originator_Identifier_Choice, originatorChoice));
                    }
                }));
            }
        }
Beispiel #14
0
        internal Pkcs12CertBag(X509Certificate2 cert)
            : base(
                Oids.Pkcs12CertBag,
                EncodeBagValue(
                    Oids.Pkcs12X509CertBagType,
                    PkcsHelpers.EncodeOctetString(cert.RawData)),
                skipCopy: true)
        {
            _decoded = CertBagAsn.Decode(EncodedBagValue, AsnEncodingRules.BER);

            IsX509Certificate = true;
        }
Beispiel #15
0
        public byte[] Encode()
        {
            if (!_hasData)
            {
                throw new InvalidOperationException(SR.Cryptography_Cms_MessageNotSigned);
            }

            using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
            {
                _signedData.Encode(writer);
                return(PkcsHelpers.EncodeContentInfo(writer.Encode(), Oids.Pkcs7Signed));
            }
        }
Beispiel #16
0
        protected Pkcs12SafeBag(string bagIdValue, ReadOnlyMemory <byte> encodedBagValue, bool skipCopy = false)
        {
            if (string.IsNullOrEmpty(bagIdValue))
            {
                throw new ArgumentNullException(nameof(bagIdValue));
            }

            // Read to ensure that there is precisely one legally encoded value.
            PkcsHelpers.EnsureSingleBerValue(encodedBagValue.Span);

            _bagIdValue     = bagIdValue;
            EncodedBagValue = skipCopy ? encodedBagValue : encodedBagValue.ToArray();
        }
Beispiel #17
0
        public override void AddCertsFromStoreForDecryption(X509Certificate2Collection certs)
        {
            certs.AddRange(PkcsHelpers.GetStoreCertificates(StoreName.My, StoreLocation.CurrentUser, openExistingOnly: false));

            try
            {
                // This store exists on macOS, but not Linux
                certs.AddRange(
                    PkcsHelpers.GetStoreCertificates(StoreName.My, StoreLocation.LocalMachine, openExistingOnly: false));
            }
            catch (CryptographicException)
            {
            }
        }
Beispiel #18
0
        private static unsafe void AddCryptAttribute(CryptographicAttributeObjectCollection collection, CRYPT_ATTRIBUTE *pCryptAttribute)
        {
            string oidValue = pCryptAttribute->pszObjId.ToStringAnsi();
            Oid    oid      = new Oid(oidValue);
            AsnEncodedDataCollection attributeCollection = new AsnEncodedDataCollection();

            for (int i = 0; i < pCryptAttribute->cValue; i++)
            {
                byte[]         encodedAttribute = pCryptAttribute->rgValue[i].ToByteArray();
                AsnEncodedData attributeObject  = PkcsHelpers.CreateBestPkcs9AttributeObjectAvailable(oid, encodedAttribute);
                attributeCollection.Add(attributeObject);
            }

            collection.Add(new CryptographicAttributeObject(oid, attributeCollection));
        }
Beispiel #19
0
        public Pkcs12SecretBag AddSecret(Oid secretType, ReadOnlyMemory <byte> secretValue)
        {
            if (secretType is null)
            {
                throw new ArgumentNullException(nameof(secretType));
            }

            // Read to ensure that there is precisely one legally encoded value.
            PkcsHelpers.EnsureSingleBerValue(secretValue.Span);

            Pkcs12SecretBag bag = new Pkcs12SecretBag(secretType, secretValue);

            AddSafeBag(bag);
            return(bag);
        }
Beispiel #20
0
        public sealed unsafe override byte[] Encrypt(CmsRecipientCollection recipients, ContentInfo contentInfo, AlgorithmIdentifier contentEncryptionAlgorithm, X509Certificate2Collection originatorCerts, CryptographicAttributeObjectCollection unprotectedAttributes)
        {
            using (SafeCryptMsgHandle hCryptMsg = EncodeHelpers.CreateCryptMsgHandleToEncode(recipients, contentInfo.ContentType, contentEncryptionAlgorithm, originatorCerts, unprotectedAttributes))
            {
                byte[] encodedContent;
                if (contentInfo.ContentType.Value.Equals(Oids.Pkcs7Data, StringComparison.OrdinalIgnoreCase))
                {
                    encodedContent = PkcsHelpers.EncodeOctetString(contentInfo.Content);
                }
                else
                {
                    encodedContent = contentInfo.Content;

                    if (encodedContent.Length > 0)
                    {
                        // Windows will throw if it encounters indefinite length encoding.
                        // Let's reencode if that is the case
                        ReencodeIfUsingIndefiniteLengthEncodingOnOuterStructure(ref encodedContent);
                    }
                }

                if (encodedContent.Length > 0)
                {
                    // Pin to avoid copy during heap compaction
                    fixed(byte *pinnedContent = encodedContent)
                    {
                        try
                        {
                            if (!Interop.Crypt32.CryptMsgUpdate(hCryptMsg, encodedContent, encodedContent.Length, fFinal: true))
                            {
                                throw Marshal.GetLastWin32Error().ToCryptographicException();
                            }
                        }
                        finally
                        {
                            if (!object.ReferenceEquals(encodedContent, contentInfo.Content))
                            {
                                Array.Clear(encodedContent, 0, encodedContent.Length);
                            }
                        }
                    }
                }

                byte[] encodedMessage = hCryptMsg.GetMsgParamAsByteArray(CryptMsgParamType.CMSG_CONTENT_PARAM);
                return(encodedMessage);
            }
        }
Beispiel #21
0
        private static unsafe void AddCryptAttribute(CryptographicAttributeObjectCollection collection, CRYPT_ATTRIBUTE *pCryptAttribute)
        {
            string oidValue = pCryptAttribute->pszObjId.ToStringAnsi();
            Oid    oid      = new Oid(oidValue);
            AsnEncodedDataCollection attributeCollection = new AsnEncodedDataCollection();

            for (int i = 0; i < pCryptAttribute->cValue; i++)
            {
                // CreateBestPkcs9AttributeObjectAvailable is expected to create a copy of the data so that it has ownership
                // of the underlying data.
                ReadOnlySpan <byte> encodedAttribute = pCryptAttribute->rgValue[i].DangerousAsSpan();
                AsnEncodedData      attributeObject  = PkcsHelpers.CreateBestPkcs9AttributeObjectAvailable(oid, encodedAttribute);
                attributeCollection.Add(attributeObject);
            }

            collection.Add(new CryptographicAttributeObject(oid, attributeCollection));
        }
Beispiel #22
0
        private byte[] EncryptContent(
            ContentInfo contentInfo,
            AlgorithmIdentifier contentEncryptionAlgorithm,
            out byte[] cek,
            out byte[] parameterBytes)
        {
            using (SymmetricAlgorithm alg = OpenAlgorithm(contentEncryptionAlgorithm))
            using (ICryptoTransform encryptor = alg.CreateEncryptor())
            {
                cek = alg.Key;

                if (alg is RC2)
                {
                    Rc2CbcParameters rc2Params = new Rc2CbcParameters(alg.IV, alg.KeySize);

                    using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
                    {
                        rc2Params.Encode(writer);
                        parameterBytes = writer.Encode();
                    }
                }
                else
                {
                    parameterBytes = PkcsHelpers.EncodeOctetString(alg.IV);
                }

                byte[] toEncrypt = contentInfo.Content;

                if (contentInfo.ContentType.Value == Oids.Pkcs7Data)
                {
                    return encryptor.OneShot(toEncrypt);
                }
                else
                {
                    if (contentInfo.Content.Length == 0)
                    {
                        return encryptor.OneShot(contentInfo.Content);
                    }
                    else
                    {
                        AsnReader reader = new AsnReader(contentInfo.Content, AsnEncodingRules.BER);
                        return encryptor.OneShot(reader.PeekContentBytes().ToArray());
                    }
                }
            }
        }
Beispiel #23
0
        public byte[] Encode()
        {
            if (!_hasData)
            {
                throw new InvalidOperationException(SR.Cryptography_Cms_MessageNotSigned);
            }

            try
            {
                using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
                {
                    _signedData.Encode(writer);
                    return(PkcsHelpers.EncodeContentInfo(writer.Encode(), Oids.Pkcs7Signed));
                }
            }
            catch (CryptographicException) when(!Detached)
            {
                // If we can't write the contents back out then the most likely culprit is an
                // indefinite length encoding in the content field.  To preserve as much input data
                // as possible while still maintaining our expectations of sorting any SET OF values,
                // do the following:
                // * Write the DER normalized version of the SignedData in detached mode.
                // * BER-decode that structure
                // * Copy the content field over
                // * BER-write the modified structure.

                SignedDataAsn copy = _signedData;

                copy.EncapContentInfo.Content = null;
                Debug.Assert(_signedData.EncapContentInfo.Content != null);

                using (AsnWriter detachedWriter = new AsnWriter(AsnEncodingRules.DER))
                {
                    copy.Encode(detachedWriter);
                    copy = SignedDataAsn.Decode(detachedWriter.Encode(), AsnEncodingRules.BER);
                }

                copy.EncapContentInfo.Content = _signedData.EncapContentInfo.Content;

                using (AsnWriter attachedWriter = new AsnWriter(AsnEncodingRules.BER))
                {
                    copy.Encode(attachedWriter);
                    return(PkcsHelpers.EncodeContentInfo(attachedWriter.Encode(), Oids.Pkcs7Signed));
                }
            }
        }
        public static Rfc3161TimestampRequest CreateFromHash(
            ReadOnlyMemory <byte> hash,
            HashAlgorithmName hashAlgorithm,
            Oid requestedPolicyId              = null,
            ReadOnlyMemory <byte>?nonce        = null,
            bool requestSignerCertificates     = false,
            X509ExtensionCollection extensions = null)
        {
            string oidStr = PkcsHelpers.GetOidFromHashAlgorithm(hashAlgorithm);

            return(CreateFromHash(
                       hash,
                       new Oid(oidStr, oidStr),
                       requestedPolicyId,
                       nonce,
                       requestSignerCertificates,
                       extensions));
        }
            internal static byte[]? DecryptCekCore(
                X509Certificate2?cert,
                RSA?privateKey,
                ReadOnlySpan <byte> encryptedKey,
                string?keyEncryptionAlgorithm,
                ReadOnlyMemory <byte>?algorithmParameters,
                out Exception?exception)
            {
                RSAEncryptionPadding?encryptionPadding;

                switch (keyEncryptionAlgorithm)
                {
                case Oids.Rsa:
                    encryptionPadding = RSAEncryptionPadding.Pkcs1;
                    break;

                case Oids.RsaOaep:
                    if (!PkcsHelpers.TryGetRsaOaepEncryptionPadding(algorithmParameters, out encryptionPadding, out exception))
                    {
                        return(null);
                    }
                    break;

                default:
                    exception = new CryptographicException(
                        SR.Cryptography_Cms_UnknownAlgorithm,
                        keyEncryptionAlgorithm);

                    return(null);
                }

                if (privateKey != null)
                {
                    return(DecryptKey(privateKey, encryptionPadding, encryptedKey, out exception));
                }
                else
                {
                    Debug.Assert(cert != null);
                    using (RSA? rsa = cert.GetRSAPrivateKey())
                    {
                        return(DecryptKey(rsa, encryptionPadding, encryptedKey, out exception));
                    }
                }
            }
Beispiel #26
0
        public void RemoveSignature(int index)
        {
            if (!_hasData)
            {
                throw new InvalidOperationException(SR.Cryptography_Cms_MessageNotSigned);
            }

            if (index < 0 || index >= _signedData.SignerInfos.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
            }

            AlgorithmIdentifierAsn signerAlgorithm = _signedData.SignerInfos[index].DigestAlgorithm;

            PkcsHelpers.RemoveAt(ref _signedData.SignerInfos, index);

            ConsiderDigestRemoval(signerAlgorithm);
            UpdateMetadata();
        }
Beispiel #27
0
        public Pkcs8PrivateKeyInfo(
            Oid algorithmId,
            ReadOnlyMemory <byte>?algorithmParameters,
            ReadOnlyMemory <byte> privateKey,
            bool skipCopies = false)
        {
            if (algorithmId == null)
            {
                throw new ArgumentNullException(nameof(algorithmId));
            }

            if (algorithmParameters?.Length > 0)
            {
                // Read to ensure that there is precisely one legally encoded value.
                PkcsHelpers.EnsureSingleBerValue(algorithmParameters.Value.Span);
            }

            AlgorithmId         = algorithmId;
            AlgorithmParameters = skipCopies ? algorithmParameters : algorithmParameters?.ToArray();
            PrivateKeyBytes     = skipCopies ? privateKey : privateKey.ToArray();
            Attributes          = new CryptographicAttributeObjectCollection();
        }
Beispiel #28
0
        public bool VerifySignatureForHash(
            ReadOnlySpan <byte> hash,
            HashAlgorithmName hashAlgorithm,
            [NotNullWhen(true)] out X509Certificate2?signerCertificate,
            X509Certificate2Collection?extraCandidates = null)
        {
            signerCertificate = null;

            X509Certificate2?cert = GetSignerCertificate(extraCandidates);

            if (cert == null)
            {
                return(false);
            }

            if (VerifyHash(hash, PkcsHelpers.GetOidFromHashAlgorithm(hashAlgorithm)))
            {
                signerCertificate = cert;
                return(true);
            }

            return(false);
        }
        public bool VerifySignatureForHash(
            ReadOnlySpan <byte> hash,
            HashAlgorithmName hashAlgorithm,
            out X509Certificate2 signerCertificate,
            X509Certificate2Collection extraCandidates = null)
        {
            signerCertificate = null;

            X509Certificate2 cert = GetSignerCertificate(extraCandidates);

            if (cert == null)
            {
                return(false);
            }

            bool ret = VerifyHash(hash, PkcsHelpers.GetOidFromHashAlgorithm(hashAlgorithm));

            if (ret)
            {
                signerCertificate = cert;
            }

            return(ret);
        }
Beispiel #30
0
        public void SealWithMac(
            ReadOnlySpan <char> password,
            HashAlgorithmName hashAlgorithm,
            int iterationCount)
        {
            if (iterationCount < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(iterationCount));
            }
            if (IsSealed)
            {
                throw new InvalidOperationException(SR.Cryptography_Pkcs12_PfxIsSealed);
            }

            byte[]? rentedAuthSafe = null;
            Span <byte> authSafeSpan = default;

            byte[]? rentedMac = null;
            Span <byte> macSpan = default;
            Span <byte> salt    = stackalloc byte[0];

            try
            {
                AsnWriter contentsWriter = new AsnWriter(AsnEncodingRules.BER);

                using (IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithm))
                {
                    contentsWriter.PushSequence();
                    if (_contents != null)
                    {
                        foreach (ContentInfoAsn contentInfo in _contents)
                        {
                            contentInfo.Encode(contentsWriter);
                        }
                    }
                    contentsWriter.PopSequence();

                    rentedAuthSafe = CryptoPool.Rent(contentsWriter.GetEncodedLength());

                    if (!contentsWriter.TryEncode(rentedAuthSafe, out int written))
                    {
                        Debug.Fail("TryEncode failed with a pre-allocated buffer");
                        throw new InvalidOperationException();
                    }

                    authSafeSpan = rentedAuthSafe.AsSpan(0, written);

                    // Get an array of the proper size for the hash.
                    byte[] macKey = hasher.GetHashAndReset();
                    rentedMac = CryptoPool.Rent(macKey.Length);
                    macSpan   = rentedMac.AsSpan(0, macKey.Length);

                    // Since the biggest supported hash is SHA-2-512 (64 bytes), the
                    // 128-byte cap here shouldn't ever come into play.
                    Debug.Assert(macKey.Length <= 128);
                    salt = stackalloc byte[Math.Min(macKey.Length, 128)];
                    RandomNumberGenerator.Fill(salt);

                    Pkcs12Kdf.DeriveMacKey(
                        password,
                        hashAlgorithm,
                        iterationCount,
                        salt,
                        macKey);

                    using (IncrementalHash mac = IncrementalHash.CreateHMAC(hashAlgorithm, macKey))
                    {
                        mac.AppendData(authSafeSpan);

                        if (!mac.TryGetHashAndReset(macSpan, out int bytesWritten) || bytesWritten != macSpan.Length)
                        {
                            Debug.Fail($"TryGetHashAndReset wrote {bytesWritten} of {macSpan.Length} bytes");
                            throw new CryptographicException();
                        }
                    }
                }

                // https://tools.ietf.org/html/rfc7292#section-4
                //
                // PFX ::= SEQUENCE {
                //   version    INTEGER {v3(3)}(v3,...),
                //   authSafe   ContentInfo,
                //   macData    MacData OPTIONAL
                // }
                AsnWriter writer = new AsnWriter(AsnEncodingRules.BER);
                {
                    writer.PushSequence();

                    writer.WriteInteger(3);

                    writer.PushSequence();
                    {
                        writer.WriteObjectIdentifierForCrypto(Oids.Pkcs7Data);

                        Asn1Tag contextSpecific0 = new Asn1Tag(TagClass.ContextSpecific, 0);

                        writer.PushSequence(contextSpecific0);
                        {
                            writer.WriteOctetString(authSafeSpan);
                            writer.PopSequence(contextSpecific0);
                        }

                        writer.PopSequence();
                    }

                    // https://tools.ietf.org/html/rfc7292#section-4
                    //
                    // MacData ::= SEQUENCE {
                    //   mac        DigestInfo,
                    //   macSalt    OCTET STRING,
                    //   iterations INTEGER DEFAULT 1
                    //   -- Note: The default is for historical reasons and its use is
                    //   -- deprecated.
                    // }
                    writer.PushSequence();
                    {
                        writer.PushSequence();
                        {
                            writer.PushSequence();
                            {
                                writer.WriteObjectIdentifierForCrypto(PkcsHelpers.GetOidFromHashAlgorithm(hashAlgorithm));
                                writer.PopSequence();
                            }

                            writer.WriteOctetString(macSpan);
                            writer.PopSequence();
                        }

                        writer.WriteOctetString(salt);

                        if (iterationCount > 1)
                        {
                            writer.WriteInteger(iterationCount);
                        }

                        writer.PopSequence();
                    }

                    writer.PopSequence();
                    _sealedData = writer.Encode();
                }
            }
            finally
            {
                CryptographicOperations.ZeroMemory(macSpan);
                CryptographicOperations.ZeroMemory(authSafeSpan);

                if (rentedMac != null)
                {
                    // Already cleared
                    CryptoPool.Return(rentedMac, clearSize: 0);
                }

                if (rentedAuthSafe != null)
                {
                    // Already cleared
                    CryptoPool.Return(rentedAuthSafe, clearSize: 0);
                }
            }
        }