/// <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, }); }