/// <summary>
        /// Timestamps data present in the TimestampRequest.
        /// </summary>
        internal async Task <SignedCms> GetTimestampAsync(TimestampRequest request, ILogger logger, CancellationToken token)
        {
            token.ThrowIfCancellationRequested();

            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            // Allows us to track the request.
            var nonce = GenerateNonce();
            var rfc3161TimestampRequest = Rfc3161TimestampRequestFactory.Create(
                request.HashedMessage,
                request.HashAlgorithm.ConvertToSystemSecurityHashAlgorithmName(),
                requestedPolicyId: null,
                nonce: nonce,
                requestSignerCertificates: true,
                extensions: null);

            // Request a timestamp
            // The response status need not be checked here as lower level api will throw if the response is invalid
            IRfc3161TimestampToken timestampToken = await rfc3161TimestampRequest.SubmitRequestAsync(
                _timestamperUrl,
                RequestTimeout);

            // quick check for response validity
            var normalizedNonce = rfc3161TimestampRequest.GetNonce();

            ValidateTimestampResponse(normalizedNonce, request.HashedMessage, timestampToken);

            var timestampCms = timestampToken.AsSignedCms();

            ValidateTimestampCms(request.SigningSpecifications, timestampCms, timestampToken);

            // If the timestamp signed CMS already has a complete chain for the signing certificate,
            // it's ready to be added to the signature to be timestamped.
            // However, a timestamp service is not required to include all certificates in a complete
            // chain for the signing certificate in the SignedData.certificates collection.
            // Some timestamp services include all certificates except the root in the
            // SignedData.certificates collection.
            var signerInfo = timestampCms.SignerInfos[0];

            using (var chain = CertificateChainUtility.GetCertificateChain(
                       signerInfo.Certificate,
                       timestampCms.Certificates,
                       logger,
                       CertificateType.Timestamp))
            {
                return(EnsureCertificatesInCertificatesCollection(timestampCms, chain));
            }
        }
        public static IRfc3161TimestampToken Create(
            IRfc3161TimestampTokenInfo tstInfo,
            X509Certificate2 signerCertificate,
            X509Certificate2Collection additionalCerts,
            byte[] encoded)
        {
            if (tstInfo == null)
            {
                throw new ArgumentNullException(nameof(tstInfo));
            }

            if (signerCertificate == null)
            {
                throw new ArgumentNullException(nameof(signerCertificate));
            }

            if (additionalCerts == null)
            {
                throw new ArgumentNullException(nameof(additionalCerts));
            }

            if (encoded == null)
            {
                throw new ArgumentNullException(nameof(encoded));
            }
#if IS_SIGNING_SUPPORTED
            IRfc3161TimestampToken iRfc3161TimestampToken = null;
#if IS_DESKTOP
            iRfc3161TimestampToken = new Rfc3161TimestampTokenNet472Wrapper(
                tstInfo,
                signerCertificate,
                additionalCerts,
                encoded);
#else
            iRfc3161TimestampToken = new Rfc3161TimestampTokenNetstandard21Wrapper(
                tstInfo,
                signerCertificate,
                additionalCerts,
                encoded);
#endif
            return(iRfc3161TimestampToken);
#else
            throw new NotSupportedException();
#endif
        }
        private static void ValidateTimestampResponse(byte[] nonce, byte[] messageHash, IRfc3161TimestampToken timestampToken)
        {
            if (!nonce.SequenceEqual(timestampToken.TokenInfo.GetNonce()))
            {
                throw new TimestampException(NuGetLogCode.NU3026, Strings.TimestampFailureNonceMismatch);
            }

            if (!timestampToken.TokenInfo.HasMessageHash(messageHash))
            {
                throw new TimestampException(NuGetLogCode.NU3019, Strings.SignError_TimestampIntegrityCheckFailed);
            }
        }
        private static void ValidateTimestampCms(SigningSpecifications spec, SignedCms timestampCms, IRfc3161TimestampToken timestampToken)
        {
            var signerInfo = timestampCms.SignerInfos[0];

            try
            {
                signerInfo.CheckSignature(verifySignatureOnly: true);
            }
            catch (Exception e)
            {
                throw new TimestampException(NuGetLogCode.NU3021, Strings.SignError_TimestampSignatureValidationFailed, e);
            }

            if (signerInfo.Certificate == null)
            {
                throw new TimestampException(NuGetLogCode.NU3020, Strings.SignError_TimestampNoCertificate);
            }

            if (!CertificateUtility.IsSignatureAlgorithmSupported(signerInfo.Certificate))
            {
                var certificateSignatureAlgorithm = GetNameOrOidString(signerInfo.Certificate.SignatureAlgorithm);

                var supportedSignatureAlgorithms = string.Join(", ", spec.AllowedSignatureAlgorithms);

                var errorMessage = string.Format(CultureInfo.CurrentCulture,
                                                 Strings.TimestampCertificateUnsupportedSignatureAlgorithm,
                                                 certificateSignatureAlgorithm,
                                                 supportedSignatureAlgorithms);

                throw new TimestampException(NuGetLogCode.NU3022, errorMessage);
            }

            if (!CertificateUtility.IsCertificatePublicKeyValid(signerInfo.Certificate))
            {
                throw new TimestampException(NuGetLogCode.NU3023, Strings.SignError_TimestampCertificateFailsPublicKeyLengthRequirement);
            }

            if (!spec.AllowedHashAlgorithmOids.Contains(signerInfo.DigestAlgorithm.Value))
            {
                var digestAlgorithm = GetNameOrOidString(signerInfo.DigestAlgorithm);

                var supportedSignatureAlgorithms = string.Join(", ", spec.AllowedHashAlgorithms);

                var errorMessage = string.Format(CultureInfo.CurrentCulture,
                                                 Strings.TimestampSignatureUnsupportedDigestAlgorithm,
                                                 digestAlgorithm,
                                                 supportedSignatureAlgorithms);

                throw new TimestampException(NuGetLogCode.NU3024, errorMessage);
            }

            if (CertificateUtility.IsCertificateValidityPeriodInTheFuture(signerInfo.Certificate))
            {
                throw new TimestampException(NuGetLogCode.NU3025, Strings.SignError_TimestampNotYetValid);
            }

            if (!CertificateUtility.IsDateInsideValidityPeriod(signerInfo.Certificate, timestampToken.TokenInfo.Timestamp))
            {
                throw new TimestampException(NuGetLogCode.NU3036, Strings.SignError_TimestampGeneralizedTimeInvalid);
            }
        }