/// <summary> /// Signs the package with a timestamp. /// </summary> /// <param name="timestampServer">The URI of the timestamp server.</param> /// <param name="timestampAlgorithm">The hash algorithm to timestamp with.</param> /// <returns>A result of the timestamp operation.</returns> public Task <TimestampResult> SignAsync(Uri timestampServer, HashAlgorithmName timestampAlgorithm) { if (timestampServer == null) { throw new ArgumentNullException(nameof(timestampServer)); } if (!timestampServer.IsAbsoluteUri) { throw new ArgumentException("The timestamp server must be an absolute URI.", nameof(timestampServer)); } using (var nonce = new TimestampNonceFactory()) { #if NET462 return(Win32TimeStamp(timestampServer, timestampAlgorithm, nonce)); #elif NETSTANDARD2_0 return(BouncyCastleTimeStamp(timestampServer, timestampAlgorithm, nonce)); #else throw new PlatformNotSupportedException("Timestamping is not supported on this platform."); #endif } }
/// <summary> /// Signs the package with a timestamp. /// </summary> /// <param name="timestampServer">The URI of the timestamp server.</param> /// <param name="timestampAlgorithm">The hash algorithm to timestamp with.</param> /// <returns>A result of the timestamp operation.</returns> public Task <TimestampResult> SignAsync(Uri timestampServer, HashAlgorithmName timestampAlgorithm) { if (timestampServer == null) { throw new ArgumentNullException(nameof(timestampServer)); } if (!timestampServer.IsAbsoluteUri) { throw new ArgumentException("The timestamp server must be an absolute URI.", nameof(timestampServer)); } var oid = HashAlgorithmTranslator.TranslateFromNameToOid(timestampAlgorithm); using (var nonce = new TimestampNonceFactory()) { var parameters = new CRYPT_TIMESTAMP_PARA(); parameters.cExtension = 0; parameters.fRequestCerts = true; parameters.Nonce.cbData = nonce.Size; parameters.Nonce.pbData = nonce.Nonce; parameters.pszTSAPolicyId = null; var(signatureDocument, timestampSubject) = GetSignatureToTimestamp(_part); var winResult = Crypt32.CryptRetrieveTimeStamp( timestampServer.AbsoluteUri, CryptRetrieveTimeStampRetrievalFlags.NONE, (uint)Timeout.TotalMilliseconds, oid.Value, ref parameters, timestampSubject, (uint)timestampSubject.Length, out var context, IntPtr.Zero, IntPtr.Zero ); if (!winResult) { return(Task.FromResult(TimestampResult.Failed)); } using (context) { var refSuccess = false; try { context.DangerousAddRef(ref refSuccess); if (!refSuccess) { return(Task.FromResult(TimestampResult.Failed)); } var structure = Marshal.PtrToStructure <CRYPT_TIMESTAMP_CONTEXT>(context.DangerousGetHandle()); var encoded = new byte[structure.cbEncoded]; Marshal.Copy(structure.pbEncoded, encoded, 0, encoded.Length); ApplyTimestamp(signatureDocument, _part, encoded); return(Task.FromResult(TimestampResult.Success)); } finally { if (refSuccess) { context.DangerousRelease(); } } } } }
private async Task <TimestampResult> BouncyCastleTimeStamp(Uri timestampServer, HashAlgorithmName timestampAlgorithm, TimestampNonceFactory nonce) { var oid = HashAlgorithmTranslator.TranslateFromNameToOid(timestampAlgorithm); var requestGenerator = new TimeStampRequestGenerator(); var(signatureDocument, timestampSubject) = GetSignatureToTimestamp(_part); using (var hash = HashAlgorithmTranslator.TranslateFromNameToxmlDSigUri(timestampAlgorithm, out _)) { var digest = hash.ComputeHash(timestampSubject); var request = requestGenerator.Generate(oid.Value, digest, new Org.BouncyCastle.Math.BigInteger(nonce.Nonce)); var encodedRequest = request.GetEncoded(); var client = new HttpClient(); var content = new ByteArrayContent(encodedRequest); content.Headers.Add("Content-Type", "application/timestamp-query"); var post = await client.PostAsync(timestampServer, content); if (post.StatusCode != HttpStatusCode.OK) { return(TimestampResult.Failed); } var responseBytes = await post.Content.ReadAsByteArrayAsync(); var responseParser = new Asn1StreamParser(responseBytes); var timeStampResponse = new TimeStampResponse(responseBytes); var tokenResponse = timeStampResponse.TimeStampToken.GetEncoded(); ApplyTimestamp(signatureDocument, _part, tokenResponse); return(TimestampResult.Success); } }