public static async Task <byte[]> SignWithRfc3161(byte[] bytesToSign, bool isDetached, X509Certificate2 certificate, Uri timeStampAuthorityUri) { // Sign our contents. var contentInfo = new ContentInfo(bytesToSign); var cms = new SignedCms(contentInfo, isDetached); var signer = new CmsSigner(certificate); // { IncludeOption = X509IncludeOption.WholeChain }; //X509IncludeOption.EndCertOnly; signer.SignedAttributes.Add(new Pkcs9SigningTime()); cms.ComputeSignature(signer, false); // Generate our nonce byte[] nonce = new byte[8]; using (var rng = RandomNumberGenerator.Create()) { rng.GetBytes(nonce); } // Get our signing information and create the RFC3161 request SignerInfo newSignerInfo = cms.SignerInfos[0]; // Now we generate our request for us to send to our RFC3161 signing authority. Rfc3161TimestampRequest request = Rfc3161TimestampRequest.CreateFromSignerInfo(newSignerInfo, HashAlgorithmName.SHA256, requestSignerCertificates: true, nonce: nonce); // You can use your own web request system, in this example we are just going to use a `HttpClient` class. var client = new HttpClient(); var content = new ReadOnlyMemoryContent(request.Encode()); content.Headers.ContentType = new MediaTypeHeaderValue("application/timestamp-query"); var httpResponse = await client.PostAsync(timeStampAuthorityUri, content).ConfigureAwait(false); // Process our response if (!httpResponse.IsSuccessStatusCode) { throw new CryptographicException("There was a error from the timestamp authority. It responded with {httpResponse.StatusCode} {(int)httpResponse.StatusCode}: {httpResponse.Content}"); } if (httpResponse.Content.Headers.ContentType.MediaType != "application/timestamp-reply") { throw new CryptographicException("The reply from the time stamp server was in a invalid format."); } var data = await httpResponse.Content.ReadAsByteArrayAsync().ConfigureAwait(false); var timestampToken = request.ProcessResponse(data, out _); // The RFC3161 sign certificate is separate to the contents that was signed, we need to add it to the unsigned attributes. newSignerInfo.UnsignedAttributes.Add(new AsnEncodedData(SignatureTimeStampOin, timestampToken.AsSignedCms().Encode())); return(cms.Encode()); }
public static void BuildFromSignerInfo() { ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3, 4 }); SignedCms cms = new SignedCms(content, false); using (X509Certificate2 signerCert = Certificates.RSAKeyTransferCapi1.TryGetCertificateWithPrivateKey()) { CmsSigner signer = new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, signerCert); signer.SignedAttributes.Add(new Pkcs9SigningTime()); cms.ComputeSignature(signer); } SignerInfo signerInfo = cms.SignerInfos[0]; byte[] sig = signerInfo.GetSignature(); Rfc3161TimestampRequest fromSigner = Rfc3161TimestampRequest.CreateFromSignerInfo(signerInfo, HashAlgorithmName.SHA256); Rfc3161TimestampRequest fromData = Rfc3161TimestampRequest.CreateFromData(sig, HashAlgorithmName.SHA256); Assert.Equal(fromData.Encode().ByteArrayToHex(), fromSigner.Encode().ByteArrayToHex()); }
private static void VerifyExpectedRequest(Rfc3161TimestampRequest request, bool viaSpan) { // Captured with Fiddler from a CryptRetrieveTimestamp call const string ExpectedHex = "30390201013031300D06096086480165030402010500042011806C2441295EA6" + "97EA96EE4247C0F9C71EE7638863CB8E29CD941A488FCB5A0101FF"; Assert.Equal(1, request.Version); Assert.Equal( "11806C2441295EA697EA96EE4247C0F9C71EE7638863CB8E29CD941A488FCB5A", request.GetMessageHash().ByteArrayToHex()); Assert.False(request.GetNonce().HasValue, "request.GetNonce().HasValue"); Assert.Equal("2.16.840.1.101.3.4.2.1", request.HashAlgorithmId.Value); Assert.Null(request.RequestedPolicyId); Assert.True(request.RequestSignerCertificate, "request.RequestSignerCertificate"); Assert.False(request.HasExtensions); if (viaSpan) { // Twice as big as it needs to be. byte[] buf = new byte[ExpectedHex.Length]; const byte FillByte = 0x55; buf.AsSpan().Fill(FillByte); // Too small Assert.False(request.TryEncode(Span <byte> .Empty, out int bytesWritten)); Assert.Equal(0, bytesWritten); const int WriteOffset = 7; // Too small Span <byte> dest = new Span <byte>(buf, WriteOffset, (ExpectedHex.Length / 2) - 1); Assert.False(request.TryEncode(dest, out bytesWritten)); Assert.Equal(0, bytesWritten); Assert.Equal(new string('5', buf.Length * 2), buf.ByteArrayToHex()); // Bigger than needed dest = new Span <byte>(buf, WriteOffset, buf.Length - WriteOffset); Assert.True(request.TryEncode(dest, out bytesWritten)); Assert.Equal(ExpectedHex.Length / 2, bytesWritten); Assert.Equal(ExpectedHex, dest.Slice(0, bytesWritten).ByteArrayToHex()); Assert.Equal(FillByte, buf[WriteOffset - 1]); Assert.Equal(FillByte, buf[WriteOffset + bytesWritten]); // Reset dest.Fill(FillByte); // Perfectly sized dest = dest.Slice(0, bytesWritten); Assert.True(request.TryEncode(dest, out bytesWritten)); Assert.Equal(ExpectedHex.Length / 2, bytesWritten); Assert.Equal(ExpectedHex, dest.ByteArrayToHex()); Assert.Equal(FillByte, buf[WriteOffset - 1]); Assert.Equal(FillByte, buf[WriteOffset + bytesWritten]); } else { byte[] encoded = request.Encode(); Assert.Equal(ExpectedHex, encoded.ByteArrayToHex()); } }