/// <summary> /// Verifies that timestamp is valid. /// </summary> /// <returns> /// <see cref="TimestampObject" /> /// </returns> /// <exception cref="System.ArgumentNullException"> /// Hash algorithm not provided. /// or /// Data for timestamping not provided. /// </exception> public TimestampObject Verify() { /* Check that everything has been provided */ if (0 == this.hashAlgorithm) { throw new ArgumentNullException("Hash algorithm not provided."); } if (null == this.timestampData) { throw new ArgumentNullException("Data for timestamping not provided."); } if (null == this.timestampResponse) { throw new ArgumentNullException("Timestamp not provided."); } /* Get hashed data */ byte[] hashedData = timestampData.GetHashedData(this.hashAlgorithm); /* Generate request */ TimeStampRequestGenerator requestGenerator = new TimeStampRequestGenerator(); TimeStampRequest request = requestGenerator.Generate(new Oid(this.hashAlgorithm.ToString()).Value, hashedData); /* * Check this response against to see if it a well formed response for * the passed in request. It validates message imprint digests and message imprint algorithms. * * @param request the request to be checked against * @throws TspException if the request can not match this response. */ this.timestampResponse.Validate(request); TimeStampToken token = timestampResponse.TimeStampToken; TimestampObject timestamp = new TimestampObject(); /* Validate certificate */ X509Certificate2 certificate = ValidateCertificate(token, timestamp, minimumCertificateValidityPeriod); timestamp.HashAlgorithm = this.hashAlgorithm; timestamp.Timestamp = this.timestampResponse.GetEncoded(); return(timestamp); }
/// <summary> /// Creates timestamp from provided data. /// </summary> /// <returns> /// <see cref="TimestampObject" /> /// </returns> /// <exception cref="System.ArgumentNullException"> /// Hash algorithm not provided. /// or /// TSA URL not provided. /// or /// Timestamp output format not provided. /// or /// Data for timestamping not provided.</exception> /// <exception cref="AbsoluteTimestamp.TimestampException">Cannot connect to TSA server.</exception> /// <exception cref="TspValidationException"></exception> public TimestampObject CreateTimestamp() { /* Check that everything has been provided */ if (0 == this.hashAlgorithm) { throw new ArgumentNullException("Hash algorithm not provided."); } if (String.IsNullOrWhiteSpace(this.tsaPrimaryUrl) && String.IsNullOrWhiteSpace(this.tsaSecondaryUrl)) { throw new ArgumentNullException("TSA URL not provided."); } if (0 == this.outputFormat) { throw new ArgumentNullException("Timestamp output format not provided."); } if (null == this.timestampData) { throw new ArgumentNullException("Data for timestamping not provided."); } /* Get hashed data */ byte[] hashedData = this.timestampData.GetHashedData(this.hashAlgorithm); /* Generate request */ TimeStampRequestGenerator requestGenerator = new TimeStampRequestGenerator(); requestGenerator.SetCertReq(true); TimeStampRequest request = requestGenerator.Generate(new Oid(this.hashAlgorithm.ToString()).Value, hashedData); /* Get response */ TimeStampResponse response = GetTimeStampResponse(request); /* Validate response */ if (!(response.Status == 0 || response.Status == 1)) { throw new TspValidationException( string.Format("Invalid response, response status={0}, response status string={1}, response failure info={2}", response.Status, response.GetStatusString(), response.GetFailInfo().IntValue)); } /* * Check this response against to see if it a well formed response for * the passed in request. It validates message imprint digests and message imprint algorithms. * * @param request the request to be checked against * @throws TspException if the request can not match this response. */ response.Validate(request); TimeStampToken token = response.TimeStampToken; X509Certificate2 certificate = null; TimestampObject timestamp = new TimestampObject(); /* Validate certificate */ certificate = TimestampVerifier.ValidateCertificate(token, timestamp, minimumCertificateValidityPeriod); timestamp.HashAlgorithm = this.hashAlgorithm; timestamp.Timestamp = Utils.GetTimestampForOutput(response, this.outputFormat, this.timestampData); return(timestamp); }
/// <summary> /// Extracts and validates the certificate from token. /// </summary> /// <param name="token">The token.</param> /// <param name="timestamp">The timestamp.</param> /// <param name="validityPeriod">Required certificate validity</param> /// <returns><see cref="X509Certificate2" /></returns> /// <exception cref="TspValidationException">Invalid response, more than one certificate found</exception> /// <exception cref="System.Security.Cryptography.CryptographicException">Certificate chain validation failed.</exception> public static X509Certificate2 ValidateCertificate(TimeStampToken token, TimestampObject timestamp, int validityPeriod) { X509Certificate2 tsaCertificate = null; SignerID signer = token.SignerID; ICollection certificates = token.GetCertificates("Collection").GetMatches(signer); if (certificates.Count > 1) { throw new TspValidationException("Invalid response, more than one certificate found"); } foreach (Org.BouncyCastle.X509.X509Certificate cert in certificates) { /* * Validate the time stamp token. * <p> * To be valid the token must be signed by the passed in certificate and * the certificate must be the one referred to by the SigningCertificate * attribute included in the hashed attributes of the token. The * certificate must also have the ExtendedKeyUsageExtension with only * KeyPurposeID.IdKPTimeStamping and have been valid at the time the * timestamp was created. * </p> * <p> * A successful call to validate means all the above are true. * </p> */ token.Validate(cert); /// <summary> /// Return true if the nominated time is within the start and end times nominated on the certificate. /// </summary> /// <param name="time">The time to test validity against.</param> /// <returns>True if certificate is valid for nominated time.</returns> if (!cert.IsValid(token.TimeStampInfo.GenTime)) { throw new TspValidationException("Certificate is not valid at the time of timestamp creation."); } /* Set warning if signing certificate is expired or is about to expire */ if (!cert.IsValidNow) { timestamp.Warning = "Signing certificate expired."; } else if (validityPeriod > 0) { int expireDays = (cert.NotAfter - DateTime.Now).Days; if (expireDays <= validityPeriod) { timestamp.Warning = string.Format("Signing certificate is going to expire in {0} days on {1}.", expireDays, cert.NotAfter); } } /* * verify that the given certificate successfully handles and confirms * the signature associated with this signer and, if a signingTime * attribute is available, that the certificate was valid at the time the * signature was generated. */ token.ToCmsSignedData().GetSignerInfos().GetFirstSigner(signer).Verify(cert); tsaCertificate = new X509Certificate2(cert.GetEncoded()); /* Microsoft validation */ X509Chain chain = new X509Chain(); chain.ChainPolicy.VerificationTime = token.TimeStampInfo.GenTime; if (!chain.Build(tsaCertificate)) { /* Search for revoked certificate */ bool allRevokedCertificatesAreValid = true; foreach (X509ChainElement element in chain.ChainElements) { if (IsRevoked(element) && !IsValidAfterRevocation(element.Certificate, token.TimeStampInfo.GenTime)) { allRevokedCertificatesAreValid = false; break; } } string failReason = ""; foreach (X509ChainStatus status in chain.ChainStatus) { if (status.Status != X509ChainStatusFlags.Revoked || !allRevokedCertificatesAreValid) { failReason += status.Status + ": " + status.StatusInformation; } } if (failReason != "") { throw new CryptographicException("Certificate chain validation failed.\n" + failReason); } } } timestamp.Time = token.TimeStampInfo.GenTime; timestamp.TsaIssuer = token.SignerID.Issuer.ToString(); timestamp.TsaCertificate = tsaCertificate; timestamp.MessageImprint = BitConverter.ToString(token.TimeStampInfo.TstInfo.MessageImprint.GetHashedMessage()); return(tsaCertificate); }