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