Example #1
0
        /// <summary>
        /// Find a typed extension in a extension collection.
        /// </summary>
        /// <typeparam name="T">The type of the extension.</typeparam>
        /// <param name="extensions">The extensions to search.</param>
        public static T FindExtension <T>(this X509ExtensionCollection extensions) where T : X509Extension
        {
            if (extensions == null)
            {
                throw new ArgumentNullException(nameof(extensions));
            }
            lock (extensions.SyncRoot)
            {
                // search known custom extensions
                if (typeof(T) == typeof(X509AuthorityKeyIdentifierExtension))
                {
                    var extension = extensions.Cast <X509Extension>().FirstOrDefault(e => (
                                                                                         e.Oid.Value == X509AuthorityKeyIdentifierExtension.AuthorityKeyIdentifierOid ||
                                                                                         e.Oid.Value == X509AuthorityKeyIdentifierExtension.AuthorityKeyIdentifier2Oid)
                                                                                     );
                    if (extension != null)
                    {
                        return(new X509AuthorityKeyIdentifierExtension(extension, extension.Critical) as T);
                    }
                }

                if (typeof(T) == typeof(X509SubjectAltNameExtension))
                {
                    var extension = extensions.Cast <X509Extension>().FirstOrDefault(e => (
                                                                                         e.Oid.Value == X509SubjectAltNameExtension.SubjectAltNameOid ||
                                                                                         e.Oid.Value == X509SubjectAltNameExtension.SubjectAltName2Oid)
                                                                                     );
                    if (extension != null)
                    {
                        return(new X509SubjectAltNameExtension(extension, extension.Critical) as T);
                    }
                }

                if (typeof(T) == typeof(X509CrlNumberExtension))
                {
                    var extension = extensions.Cast <X509Extension>().FirstOrDefault(e => (
                                                                                         e.Oid.Value == X509CrlNumberExtension.CrlNumberOid)
                                                                                     );
                    if (extension != null)
                    {
                        return(new X509CrlNumberExtension(extension, extension.Critical) as T);
                    }
                }

                // search builtin extension
                return(extensions.OfType <T>().FirstOrDefault());
            }
        }
        /// <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)
        {
            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;
            AsnWriter writer = AsnSerializer.Serialize(req, ruleSet);

            byte[] encodedBytes = writer.Encode();

            // Make sure everything normalizes
            req = AsnSerializer.Deserialize <Rfc3161TimeStampReq>(encodedBytes, ruleSet);

            return(new Rfc3161TimestampRequest
            {
                _encodedBytes = writer.Encode(),
                _parsedData = req,
            });
        }
        private static byte[] Encode(
            Oid policyId,
            Oid hashAlgorithmId,
            ReadOnlyMemory <byte> messageHash,
            ReadOnlyMemory <byte> serialNumber,
            DateTimeOffset timestamp,
            bool isOrdering,
            long?accuracyInMicroseconds,
            ReadOnlyMemory <byte>?nonce,
            ReadOnlyMemory <byte>?tsaName,
            X509ExtensionCollection extensions)
        {
            if (policyId == null)
            {
                throw new ArgumentNullException(nameof(policyId));
            }
            if (hashAlgorithmId == null)
            {
                throw new ArgumentNullException(nameof(hashAlgorithmId));
            }

            var tstInfo = new Rfc3161TstInfo
            {
                // The only legal value as of 2017.
                Version        = 1,
                Policy         = policyId,
                MessageImprint =
                {
                    HashAlgorithm  =
                    {
                        Algorithm  = hashAlgorithmId,
                        Parameters = AlgorithmIdentifierAsn.ExplicitDerNull,
                    },

                    HashedMessage  = messageHash,
                },
                SerialNumber = serialNumber,
                GenTime      = timestamp,
                Ordering     = isOrdering,
                Nonce        = nonce,
            };

            if (accuracyInMicroseconds != null)
            {
                tstInfo.Accuracy = new Rfc3161Accuracy(accuracyInMicroseconds.Value);
            }

            if (tsaName != null)
            {
                tstInfo.Tsa = GeneralNameAsn.Decode(tsaName.Value, AsnEncodingRules.DER);
            }

            if (extensions != null)
            {
                tstInfo.Extensions = extensions.OfType <X509Extension>().
                                     Select(ex => new X509ExtensionAsn(ex, copyValue: false)).ToArray();
            }

            using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
            {
                tstInfo.Encode(writer);
                return(writer.Encode());
            }
        }
        /// <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;
            AsnWriter writer = AsnSerializer.Serialize(req, ruleSet);

            byte[] encodedBytes = writer.Encode();

            // Make sure everything normalizes
            req = AsnSerializer.Deserialize <Rfc3161TimeStampReq>(encodedBytes, ruleSet);

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