예제 #1
0
        public static bool TryDecode(
            ReadOnlyMemory <byte> encodedBytes,
            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;

                AsnReader             reader       = new AsnReader(encodedBytes, RuleSet);
                ReadOnlyMemory <byte> firstElement = reader.PeekEncodedValue();

                Rfc3161TimeStampReq.Decode(reader, out Rfc3161TimeStampReq req);

                request = new Rfc3161TimestampRequest
                {
                    _parsedData   = req,
                    _encodedBytes = firstElement.ToArray(),
                };

                bytesConsumed = firstElement.Length;
                return(true);
            }
            catch (CryptographicException)
            {
            }

            request       = null;
            bytesConsumed = 0;
            return(false);
        }
예제 #2
0
        /// <summary>
        /// Create a timestamp request using a pre-computed hash value.
        /// </summary>
        /// <param name="hash">The pre-computed hash value to be timestamped.</param>
        /// <param name="hashAlgorithmId">
        ///   The Object Identifier (OID) for the hash algorithm which produced <paramref name="hash"/>.
        /// </param>
        /// <param name="requestedPolicyId">
        ///   The Object Identifier (OID) for a timestamp policy the Timestamp Authority (TSA) should use,
        ///   or <c>null</c> to express no preference.
        /// </param>
        /// <param name="nonce">
        ///   An optional nonce (number used once) to uniquely identify this request to pair it with the response.
        ///   The value is interpreted as an unsigned big-endian integer and may be normalized to the encoding format.
        /// </param>
        /// <param name="requestSignerCertificates">
        ///   Indicates whether the Timestamp Authority (TSA) must (<c>true</c>) or must not (<c>false</c>) include
        ///   the signing certificate in the issued timestamp token.
        /// </param>
        /// <param name="extensions">RFC3161 extensions to present with the request.</param>
        /// <returns>
        ///   An <see cref="Rfc3161TimestampRequest"/> representing the chosen values.
        /// </returns>
        /// <seealso cref="Encode"/>
        /// <seealso cref="TryEncode"/>
        public static Rfc3161TimestampRequest CreateFromHash(
            ReadOnlyMemory <byte> hash,
            Oid hashAlgorithmId,
            Oid?requestedPolicyId              = null,
            ReadOnlyMemory <byte>?nonce        = null,
            bool requestSignerCertificates     = false,
            X509ExtensionCollection?extensions = null)
        {
            // Normalize the nonce:
            if (nonce.HasValue)
            {
                ReadOnlyMemory <byte> nonceMemory = nonce.Value;
                ReadOnlySpan <byte>   nonceSpan   = nonceMemory.Span;

                // If it's empty, or it would be negative, insert the requisite byte.
                if (nonceSpan.Length == 0 || nonceSpan[0] >= 0x80)
                {
                    byte[] temp = new byte[nonceSpan.Length + 1];
                    nonceSpan.CopyTo(temp.AsSpan(1));
                    nonce = temp;
                }
                else
                {
                    int slice = 0;

                    // Find all extra leading 0x00 values and trim them off.
                    while (slice < nonceSpan.Length && nonceSpan[slice] == 0)
                    {
                        slice++;
                    }

                    // Back up one if it was all zero, or we turned the number negative.
                    if (slice == nonceSpan.Length || nonceSpan[slice] >= 0x80)
                    {
                        slice--;
                    }

                    nonce = nonceMemory.Slice(slice);
                }
            }

            var req = new Rfc3161TimeStampReq
            {
                Version        = 1,
                MessageImprint = new MessageImprint
                {
                    HashAlgorithm =
                    {
                        Algorithm  = hashAlgorithmId,
                        Parameters = AlgorithmIdentifierAsn.ExplicitDerNull,
                    },

                    HashedMessage = hash,
                },
                ReqPolicy = requestedPolicyId,
                CertReq   = requestSignerCertificates,
                Nonce     = nonce,
            };

            if (extensions != null)
            {
                req.Extensions =
                    extensions.OfType <X509Extension>().Select(e => new X509ExtensionAsn(e)).ToArray();
            }

            // The RFC implies DER (see TryParse), and DER is the most widely understood given that
            // CER isn't specified.
            const AsnEncodingRules ruleSet = AsnEncodingRules.DER;

            using (AsnWriter writer = new AsnWriter(ruleSet))
            {
                req.Encode(writer);

                byte[] encodedBytes = writer.Encode();

                // Make sure everything normalizes
                req = Rfc3161TimeStampReq.Decode(encodedBytes, ruleSet);

                return(new Rfc3161TimestampRequest
                {
                    _encodedBytes = writer.Encode(),
                    _parsedData = req,
                });
            }
        }