diff --git a/crypto/src/tsp/GenTimeAccuracy.cs b/crypto/src/tsp/GenTimeAccuracy.cs
new file mode 100644
index 000000000..8a2f29989
--- /dev/null
+++ b/crypto/src/tsp/GenTimeAccuracy.cs
@@ -0,0 +1,33 @@
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Tsp;
+
+namespace Org.BouncyCastle.Tsp
+{
+ public class GenTimeAccuracy
+ {
+ private Accuracy accuracy;
+
+ public GenTimeAccuracy(
+ Accuracy accuracy)
+ {
+ this.accuracy = accuracy;
+ }
+
+ public int Seconds { get { return GetTimeComponent(accuracy.Seconds); } }
+
+ public int Millis { get { return GetTimeComponent(accuracy.Millis); } }
+
+ public int Micros { get { return GetTimeComponent(accuracy.Micros); } }
+
+ private int GetTimeComponent(
+ DerInteger time)
+ {
+ return time == null ? 0 : time.Value.IntValue;
+ }
+
+ public override string ToString()
+ {
+ return Seconds + "." + Millis.ToString("000") + Micros.ToString("000");
+ }
+ }
+}
diff --git a/crypto/src/tsp/TSPAlgorithms.cs b/crypto/src/tsp/TSPAlgorithms.cs
new file mode 100644
index 000000000..e3dfc7916
--- /dev/null
+++ b/crypto/src/tsp/TSPAlgorithms.cs
@@ -0,0 +1,48 @@
+using System.Collections;
+
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Oiw;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.TeleTrust;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tsp
+{
+ /**
+ * Recognised hash algorithms for the time stamp protocol.
+ */
+ public abstract class TspAlgorithms
+ {
+ public static readonly string MD5 = PkcsObjectIdentifiers.MD5.Id;
+
+ public static readonly string Sha1 = OiwObjectIdentifiers.IdSha1.Id;
+
+ public static readonly string Sha224 = NistObjectIdentifiers.IdSha224.Id;
+ public static readonly string Sha256 = NistObjectIdentifiers.IdSha256.Id;
+ public static readonly string Sha384 = NistObjectIdentifiers.IdSha384.Id;
+ public static readonly string Sha512 = NistObjectIdentifiers.IdSha512.Id;
+
+ public static readonly string RipeMD128 = TeleTrusTObjectIdentifiers.RipeMD128.Id;
+ public static readonly string RipeMD160 = TeleTrusTObjectIdentifiers.RipeMD160.Id;
+ public static readonly string RipeMD256 = TeleTrusTObjectIdentifiers.RipeMD256.Id;
+
+ public static readonly string Gost3411 = CryptoProObjectIdentifiers.GostR3411.Id;
+
+ public static readonly IList Allowed;
+
+ static TspAlgorithms()
+ {
+ string[] algs = new string[]
+ {
+ Gost3411, MD5, Sha1, Sha224, Sha256, Sha384, Sha512, RipeMD128, RipeMD160, RipeMD256
+ };
+
+ Allowed = Platform.CreateArrayList();
+ foreach (string alg in algs)
+ {
+ Allowed.Add(alg);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tsp/TSPException.cs b/crypto/src/tsp/TSPException.cs
new file mode 100644
index 000000000..3917e96a7
--- /dev/null
+++ b/crypto/src/tsp/TSPException.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace Org.BouncyCastle.Tsp
+{
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+ [Serializable]
+#endif
+ public class TspException
+ : Exception
+ {
+ public TspException()
+ {
+ }
+
+ public TspException(
+ string message)
+ : base(message)
+ {
+ }
+
+ public TspException(
+ string message,
+ Exception e)
+ : base(message, e)
+ {
+ }
+ }
+}
diff --git a/crypto/src/tsp/TSPUtil.cs b/crypto/src/tsp/TSPUtil.cs
new file mode 100644
index 000000000..1026914f4
--- /dev/null
+++ b/crypto/src/tsp/TSPUtil.cs
@@ -0,0 +1,202 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Oiw;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.TeleTrust;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Cms;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Tsp
+{
+ public class TspUtil
+ {
+ private static ISet EmptySet = CollectionUtilities.ReadOnly(new HashSet());
+ private static IList EmptyList = CollectionUtilities.ReadOnly(Platform.CreateArrayList());
+
+ private static readonly IDictionary digestLengths = Platform.CreateHashtable();
+ private static readonly IDictionary digestNames = Platform.CreateHashtable();
+
+ static TspUtil()
+ {
+ digestLengths.Add(PkcsObjectIdentifiers.MD5.Id, 16);
+ digestLengths.Add(OiwObjectIdentifiers.IdSha1.Id, 20);
+ digestLengths.Add(NistObjectIdentifiers.IdSha224.Id, 28);
+ digestLengths.Add(NistObjectIdentifiers.IdSha256.Id, 32);
+ digestLengths.Add(NistObjectIdentifiers.IdSha384.Id, 48);
+ digestLengths.Add(NistObjectIdentifiers.IdSha512.Id, 64);
+ digestLengths.Add(TeleTrusTObjectIdentifiers.RipeMD128.Id, 16);
+ digestLengths.Add(TeleTrusTObjectIdentifiers.RipeMD160.Id, 20);
+ digestLengths.Add(TeleTrusTObjectIdentifiers.RipeMD256.Id, 32);
+ digestLengths.Add(CryptoProObjectIdentifiers.GostR3411.Id, 32);
+
+ digestNames.Add(PkcsObjectIdentifiers.MD5.Id, "MD5");
+ digestNames.Add(OiwObjectIdentifiers.IdSha1.Id, "SHA1");
+ digestNames.Add(NistObjectIdentifiers.IdSha224.Id, "SHA224");
+ digestNames.Add(NistObjectIdentifiers.IdSha256.Id, "SHA256");
+ digestNames.Add(NistObjectIdentifiers.IdSha384.Id, "SHA384");
+ digestNames.Add(NistObjectIdentifiers.IdSha512.Id, "SHA512");
+ digestNames.Add(PkcsObjectIdentifiers.Sha1WithRsaEncryption.Id, "SHA1");
+ digestNames.Add(PkcsObjectIdentifiers.Sha224WithRsaEncryption.Id, "SHA224");
+ digestNames.Add(PkcsObjectIdentifiers.Sha256WithRsaEncryption.Id, "SHA256");
+ digestNames.Add(PkcsObjectIdentifiers.Sha384WithRsaEncryption.Id, "SHA384");
+ digestNames.Add(PkcsObjectIdentifiers.Sha512WithRsaEncryption.Id, "SHA512");
+ digestNames.Add(TeleTrusTObjectIdentifiers.RipeMD128.Id, "RIPEMD128");
+ digestNames.Add(TeleTrusTObjectIdentifiers.RipeMD160.Id, "RIPEMD160");
+ digestNames.Add(TeleTrusTObjectIdentifiers.RipeMD256.Id, "RIPEMD256");
+ digestNames.Add(CryptoProObjectIdentifiers.GostR3411.Id, "GOST3411");
+ }
+
+
+ /**
+ * Fetches the signature time-stamp attributes from a SignerInformation object.
+ * Checks that the MessageImprint for each time-stamp matches the signature field.
+ * (see RFC 3161 Appendix A).
+ *
+ * @param signerInfo a SignerInformation to search for time-stamps
+ * @return a collection of TimeStampToken objects
+ * @throws TSPValidationException
+ */
+ public static ICollection GetSignatureTimestamps(
+ SignerInformation signerInfo)
+ {
+ IList timestamps = Platform.CreateArrayList();
+
+ Asn1.Cms.AttributeTable unsignedAttrs = signerInfo.UnsignedAttributes;
+ if (unsignedAttrs != null)
+ {
+ foreach (Asn1.Cms.Attribute tsAttr in unsignedAttrs.GetAll(
+ PkcsObjectIdentifiers.IdAASignatureTimeStampToken))
+ {
+ foreach (Asn1Encodable asn1 in tsAttr.AttrValues)
+ {
+ try
+ {
+ Asn1.Cms.ContentInfo contentInfo = Asn1.Cms.ContentInfo.GetInstance(
+ asn1.ToAsn1Object());
+ TimeStampToken timeStampToken = new TimeStampToken(contentInfo);
+ TimeStampTokenInfo tstInfo = timeStampToken.TimeStampInfo;
+
+ byte[] expectedDigest = DigestUtilities.CalculateDigest(
+ GetDigestAlgName(tstInfo.MessageImprintAlgOid),
+ signerInfo.GetSignature());
+
+ if (!Arrays.ConstantTimeAreEqual(expectedDigest, tstInfo.GetMessageImprintDigest()))
+ throw new TspValidationException("Incorrect digest in message imprint");
+
+ timestamps.Add(timeStampToken);
+ }
+ catch (SecurityUtilityException)
+ {
+ throw new TspValidationException("Unknown hash algorithm specified in timestamp");
+ }
+ catch (Exception)
+ {
+ throw new TspValidationException("Timestamp could not be parsed");
+ }
+ }
+ }
+ }
+
+ return timestamps;
+ }
+
+ /**
+ * Validate the passed in certificate as being of the correct type to be used
+ * for time stamping. To be valid it must have an ExtendedKeyUsage extension
+ * which has a key purpose identifier of id-kp-timeStamping.
+ *
+ * @param cert the certificate of interest.
+ * @throws TspValidationException if the certicate fails on one of the check points.
+ */
+ public static void ValidateCertificate(
+ X509Certificate cert)
+ {
+ if (cert.Version != 3)
+ throw new ArgumentException("Certificate must have an ExtendedKeyUsage extension.");
+
+ Asn1OctetString ext = cert.GetExtensionValue(X509Extensions.ExtendedKeyUsage);
+ if (ext == null)
+ throw new TspValidationException("Certificate must have an ExtendedKeyUsage extension.");
+
+ if (!cert.GetCriticalExtensionOids().Contains(X509Extensions.ExtendedKeyUsage.Id))
+ throw new TspValidationException("Certificate must have an ExtendedKeyUsage extension marked as critical.");
+
+ try
+ {
+ ExtendedKeyUsage extKey = ExtendedKeyUsage.GetInstance(
+ Asn1Object.FromByteArray(ext.GetOctets()));
+
+ if (!extKey.HasKeyPurposeId(KeyPurposeID.IdKPTimeStamping) || extKey.Count != 1)
+ throw new TspValidationException("ExtendedKeyUsage not solely time stamping.");
+ }
+ catch (IOException)
+ {
+ throw new TspValidationException("cannot process ExtendedKeyUsage extension");
+ }
+ }
+
+ /// <summary>
+ /// Return the digest algorithm using one of the standard JCA string
+ /// representations rather than the algorithm identifier (if possible).
+ /// </summary>
+ internal static string GetDigestAlgName(
+ string digestAlgOID)
+ {
+ string digestName = (string) digestNames[digestAlgOID];
+
+ return digestName != null ? digestName : digestAlgOID;
+ }
+
+ internal static int GetDigestLength(
+ string digestAlgOID)
+ {
+ if (!digestLengths.Contains(digestAlgOID))
+ throw new TspException("digest algorithm cannot be found.");
+
+ return (int)digestLengths[digestAlgOID];
+ }
+
+ internal static IDigest CreateDigestInstance(
+ String digestAlgOID)
+ {
+ string digestName = GetDigestAlgName(digestAlgOID);
+
+ return DigestUtilities.GetDigest(digestName);
+ }
+
+ internal static ISet GetCriticalExtensionOids(X509Extensions extensions)
+ {
+ if (extensions == null)
+ return EmptySet;
+
+ return CollectionUtilities.ReadOnly(new HashSet(extensions.GetCriticalExtensionOids()));
+ }
+
+ internal static ISet GetNonCriticalExtensionOids(X509Extensions extensions)
+ {
+ if (extensions == null)
+ return EmptySet;
+
+ // TODO: should probably produce a set that imposes correct ordering
+ return CollectionUtilities.ReadOnly(new HashSet(extensions.GetNonCriticalExtensionOids()));
+ }
+
+ internal static IList GetExtensionOids(X509Extensions extensions)
+ {
+ if (extensions == null)
+ return EmptyList;
+
+ return CollectionUtilities.ReadOnly(Platform.CreateArrayList(extensions.GetExtensionOids()));
+ }
+ }
+}
diff --git a/crypto/src/tsp/TSPValidationException.cs b/crypto/src/tsp/TSPValidationException.cs
new file mode 100644
index 000000000..8ef2ec6cf
--- /dev/null
+++ b/crypto/src/tsp/TSPValidationException.cs
@@ -0,0 +1,44 @@
+using System;
+
+namespace Org.BouncyCastle.Tsp
+{
+ /**
+ * Exception thrown if a TSP request or response fails to validate.
+ * <p>
+ * If a failure code is associated with the exception it can be retrieved using
+ * the getFailureCode() method.</p>
+ */
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+ [Serializable]
+#endif
+ public class TspValidationException
+ : TspException
+ {
+ private int failureCode;
+
+ public TspValidationException(
+ string message)
+ : base(message)
+ {
+ this.failureCode = -1;
+ }
+
+ public TspValidationException(
+ string message,
+ int failureCode)
+ : base(message)
+ {
+ this.failureCode = failureCode;
+ }
+
+ /**
+ * Return the failure code associated with this exception - if one is set.
+ *
+ * @return the failure code if set, -1 otherwise.
+ */
+ public int FailureCode
+ {
+ get { return failureCode; }
+ }
+ }
+}
diff --git a/crypto/src/tsp/TimeStampRequest.cs b/crypto/src/tsp/TimeStampRequest.cs
new file mode 100644
index 000000000..6b9699379
--- /dev/null
+++ b/crypto/src/tsp/TimeStampRequest.cs
@@ -0,0 +1,196 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cmp;
+using Org.BouncyCastle.Asn1.Tsp;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Tsp
+{
+ /**
+ * Base class for an RFC 3161 Time Stamp Request.
+ */
+ public class TimeStampRequest
+ : X509ExtensionBase
+ {
+ private TimeStampReq req;
+ private X509Extensions extensions;
+
+ public TimeStampRequest(
+ TimeStampReq req)
+ {
+ this.req = req;
+ this.extensions = req.Extensions;
+ }
+
+ /**
+ * Create a TimeStampRequest from the past in byte array.
+ *
+ * @param req byte array containing the request.
+ * @throws IOException if the request is malformed.
+ */
+ public TimeStampRequest(
+ byte[] req)
+ : this(new Asn1InputStream(req))
+ {
+ }
+
+ /**
+ * Create a TimeStampRequest from the past in input stream.
+ *
+ * @param in input stream containing the request.
+ * @throws IOException if the request is malformed.
+ */
+ public TimeStampRequest(
+ Stream input)
+ : this(new Asn1InputStream(input))
+ {
+ }
+
+ private TimeStampRequest(
+ Asn1InputStream str)
+ {
+ try
+ {
+ this.req = TimeStampReq.GetInstance(str.ReadObject());
+ }
+ catch (InvalidCastException e)
+ {
+ throw new IOException("malformed request: " + e);
+ }
+ catch (ArgumentException e)
+ {
+ throw new IOException("malformed request: " + e);
+ }
+ }
+
+ public int Version
+ {
+ get { return req.Version.Value.IntValue; }
+ }
+
+ public string MessageImprintAlgOid
+ {
+ get { return req.MessageImprint.HashAlgorithm.ObjectID.Id; }
+ }
+
+ public byte[] GetMessageImprintDigest()
+ {
+ return req.MessageImprint.GetHashedMessage();
+ }
+
+ public string ReqPolicy
+ {
+ get
+ {
+ return req.ReqPolicy == null
+ ? null
+ : req.ReqPolicy.Id;
+ }
+ }
+
+ public BigInteger Nonce
+ {
+ get
+ {
+ return req.Nonce == null
+ ? null
+ : req.Nonce.Value;
+ }
+ }
+
+ public bool CertReq
+ {
+ get
+ {
+ return req.CertReq == null
+ ? false
+ : req.CertReq.IsTrue;
+ }
+ }
+
+ /**
+ * Validate the timestamp request, checking the digest to see if it is of an
+ * accepted type and whether it is of the correct length for the algorithm specified.
+ *
+ * @param algorithms a set of string OIDS giving accepted algorithms.
+ * @param policies if non-null a set of policies we are willing to sign under.
+ * @param extensions if non-null a set of extensions we are willing to accept.
+ * @throws TspException if the request is invalid, or processing fails.
+ */
+ public void Validate(
+ IList algorithms,
+ IList policies,
+ IList extensions)
+ {
+ if (!algorithms.Contains(this.MessageImprintAlgOid))
+ {
+ throw new TspValidationException("request contains unknown algorithm.", PkiFailureInfo.BadAlg);
+ }
+
+ if (policies != null && this.ReqPolicy != null && !policies.Contains(this.ReqPolicy))
+ {
+ throw new TspValidationException("request contains unknown policy.", PkiFailureInfo.UnacceptedPolicy);
+ }
+
+ if (this.Extensions != null && extensions != null)
+ {
+ foreach (DerObjectIdentifier oid in this.Extensions.ExtensionOids)
+ {
+ if (!extensions.Contains(oid.Id))
+ {
+ throw new TspValidationException("request contains unknown extension.",
+ PkiFailureInfo.UnacceptedExtension);
+ }
+ }
+ }
+
+ int digestLength = TspUtil.GetDigestLength(this.MessageImprintAlgOid);
+
+ if (digestLength != this.GetMessageImprintDigest().Length)
+ {
+ throw new TspValidationException("imprint digest the wrong length.",
+ PkiFailureInfo.BadDataFormat);
+ }
+ }
+
+ /**
+ * return the ASN.1 encoded representation of this object.
+ */
+ public byte[] GetEncoded()
+ {
+ return req.GetEncoded();
+ }
+
+ internal X509Extensions Extensions
+ {
+ get { return req.Extensions; }
+ }
+
+ public virtual bool HasExtensions
+ {
+ get { return extensions != null; }
+ }
+
+ public virtual X509Extension GetExtension(DerObjectIdentifier oid)
+ {
+ return extensions == null ? null : extensions.GetExtension(oid);
+ }
+
+ public virtual IList GetExtensionOids()
+ {
+ return TspUtil.GetExtensionOids(extensions);
+ }
+
+ protected override X509Extensions GetX509Extensions()
+ {
+ return Extensions;
+ }
+ }
+}
diff --git a/crypto/src/tsp/TimeStampRequestGenerator.cs b/crypto/src/tsp/TimeStampRequestGenerator.cs
new file mode 100644
index 000000000..2c698e476
--- /dev/null
+++ b/crypto/src/tsp/TimeStampRequestGenerator.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Tsp;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Math;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tsp
+{
+ /**
+ * Generator for RFC 3161 Time Stamp Request objects.
+ */
+ public class TimeStampRequestGenerator
+ {
+ private DerObjectIdentifier reqPolicy;
+
+ private DerBoolean certReq;
+
+ private IDictionary extensions = Platform.CreateHashtable();
+ private IList extOrdering = Platform.CreateArrayList();
+
+ public void SetReqPolicy(
+ string reqPolicy)
+ {
+ this.reqPolicy = new DerObjectIdentifier(reqPolicy);
+ }
+
+ public void SetCertReq(
+ bool certReq)
+ {
+ this.certReq = DerBoolean.GetInstance(certReq);
+ }
+
+ /**
+ * add a given extension field for the standard extensions tag (tag 3)
+ * @throws IOException
+ */
+ [Obsolete("Use method taking DerObjectIdentifier")]
+ public void AddExtension(
+ string oid,
+ bool critical,
+ Asn1Encodable value)
+ {
+ this.AddExtension(oid, critical, value.GetEncoded());
+ }
+
+ /**
+ * add a given extension field for the standard extensions tag
+ * The value parameter becomes the contents of the octet string associated
+ * with the extension.
+ */
+ [Obsolete("Use method taking DerObjectIdentifier")]
+ public void AddExtension(
+ string oid,
+ bool critical,
+ byte[] value)
+ {
+ DerObjectIdentifier derOid = new DerObjectIdentifier(oid);
+ extensions[derOid] = new X509Extension(critical, new DerOctetString(value));
+ extOrdering.Add(derOid);
+ }
+
+ /**
+ * add a given extension field for the standard extensions tag (tag 3)
+ * @throws IOException
+ */
+ public virtual void AddExtension(
+ DerObjectIdentifier oid,
+ bool critical,
+ Asn1Encodable extValue)
+ {
+ this.AddExtension(oid, critical, extValue.GetEncoded());
+ }
+
+ /**
+ * add a given extension field for the standard extensions tag
+ * The value parameter becomes the contents of the octet string associated
+ * with the extension.
+ */
+ public virtual void AddExtension(
+ DerObjectIdentifier oid,
+ bool critical,
+ byte[] extValue)
+ {
+ extensions.Add(oid, new X509Extension(critical, new DerOctetString(extValue)));
+ extOrdering.Add(oid);
+ }
+
+ public TimeStampRequest Generate(
+ string digestAlgorithm,
+ byte[] digest)
+ {
+ return this.Generate(digestAlgorithm, digest, null);
+ }
+
+ public TimeStampRequest Generate(
+ string digestAlgorithmOid,
+ byte[] digest,
+ BigInteger nonce)
+ {
+ if (digestAlgorithmOid == null)
+ {
+ throw new ArgumentException("No digest algorithm specified");
+ }
+
+ DerObjectIdentifier digestAlgOid = new DerObjectIdentifier(digestAlgorithmOid);
+
+ AlgorithmIdentifier algID = new AlgorithmIdentifier(digestAlgOid, DerNull.Instance);
+ MessageImprint messageImprint = new MessageImprint(algID, digest);
+
+ X509Extensions ext = null;
+
+ if (extOrdering.Count != 0)
+ {
+ ext = new X509Extensions(extOrdering, extensions);
+ }
+
+ DerInteger derNonce = nonce == null
+ ? null
+ : new DerInteger(nonce);
+
+ return new TimeStampRequest(
+ new TimeStampReq(messageImprint, reqPolicy, derNonce, certReq, ext));
+ }
+
+ public virtual TimeStampRequest Generate(DerObjectIdentifier digestAlgorithm, byte[] digest)
+ {
+ return Generate(digestAlgorithm.Id, digest);
+ }
+
+ public virtual TimeStampRequest Generate(DerObjectIdentifier digestAlgorithm, byte[] digest, BigInteger nonce)
+ {
+ return Generate(digestAlgorithm.Id, digest, nonce);
+ }
+ }
+}
diff --git a/crypto/src/tsp/TimeStampResponse.cs b/crypto/src/tsp/TimeStampResponse.cs
new file mode 100644
index 000000000..069521111
--- /dev/null
+++ b/crypto/src/tsp/TimeStampResponse.cs
@@ -0,0 +1,184 @@
+using System;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cmp;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Tsp;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tsp
+{
+ /**
+ * Base class for an RFC 3161 Time Stamp Response object.
+ */
+ public class TimeStampResponse
+ {
+ private TimeStampResp resp;
+ private TimeStampToken timeStampToken;
+
+ public TimeStampResponse(
+ TimeStampResp resp)
+ {
+ this.resp = resp;
+
+ if (resp.TimeStampToken != null)
+ {
+ timeStampToken = new TimeStampToken(resp.TimeStampToken);
+ }
+ }
+
+ /**
+ * Create a TimeStampResponse from a byte array containing an ASN.1 encoding.
+ *
+ * @param resp the byte array containing the encoded response.
+ * @throws TspException if the response is malformed.
+ * @throws IOException if the byte array doesn't represent an ASN.1 encoding.
+ */
+ public TimeStampResponse(
+ byte[] resp)
+ : this(readTimeStampResp(new Asn1InputStream(resp)))
+ {
+ }
+
+ /**
+ * Create a TimeStampResponse from an input stream containing an ASN.1 encoding.
+ *
+ * @param input the input stream containing the encoded response.
+ * @throws TspException if the response is malformed.
+ * @throws IOException if the stream doesn't represent an ASN.1 encoding.
+ */
+ public TimeStampResponse(
+ Stream input)
+ : this(readTimeStampResp(new Asn1InputStream(input)))
+ {
+ }
+
+ private static TimeStampResp readTimeStampResp(
+ Asn1InputStream input)
+ {
+ try
+ {
+ return TimeStampResp.GetInstance(input.ReadObject());
+ }
+ catch (ArgumentException e)
+ {
+ throw new TspException("malformed timestamp response: " + e, e);
+ }
+ catch (InvalidCastException e)
+ {
+ throw new TspException("malformed timestamp response: " + e, e);
+ }
+ }
+
+ public int Status
+ {
+ get { return resp.Status.Status.IntValue; }
+ }
+
+ public string GetStatusString()
+ {
+ if (resp.Status.StatusString == null)
+ {
+ return null;
+ }
+
+ StringBuilder statusStringBuf = new StringBuilder();
+ PkiFreeText text = resp.Status.StatusString;
+ for (int i = 0; i != text.Count; i++)
+ {
+ statusStringBuf.Append(text[i].GetString());
+ }
+
+ return statusStringBuf.ToString();
+ }
+
+ public PkiFailureInfo GetFailInfo()
+ {
+ if (resp.Status.FailInfo == null)
+ {
+ return null;
+ }
+
+ return new PkiFailureInfo(resp.Status.FailInfo);
+ }
+
+ public TimeStampToken TimeStampToken
+ {
+ get { return timeStampToken; }
+ }
+
+ /**
+ * Check this response against to see if it a well formed response for
+ * the passed in request. Validation will include checking the time stamp
+ * token if the response status is GRANTED or GRANTED_WITH_MODS.
+ *
+ * @param request the request to be checked against
+ * @throws TspException if the request can not match this response.
+ */
+ public void Validate(
+ TimeStampRequest request)
+ {
+ TimeStampToken tok = this.TimeStampToken;
+
+ if (tok != null)
+ {
+ TimeStampTokenInfo tstInfo = tok.TimeStampInfo;
+
+ if (request.Nonce != null && !request.Nonce.Equals(tstInfo.Nonce))
+ {
+ throw new TspValidationException("response contains wrong nonce value.");
+ }
+
+ if (this.Status != (int) PkiStatus.Granted && this.Status != (int) PkiStatus.GrantedWithMods)
+ {
+ throw new TspValidationException("time stamp token found in failed request.");
+ }
+
+ if (!Arrays.ConstantTimeAreEqual(request.GetMessageImprintDigest(), tstInfo.GetMessageImprintDigest()))
+ {
+ throw new TspValidationException("response for different message imprint digest.");
+ }
+
+ if (!tstInfo.MessageImprintAlgOid.Equals(request.MessageImprintAlgOid))
+ {
+ throw new TspValidationException("response for different message imprint algorithm.");
+ }
+
+ Asn1.Cms.Attribute scV1 = tok.SignedAttributes[PkcsObjectIdentifiers.IdAASigningCertificate];
+ Asn1.Cms.Attribute scV2 = tok.SignedAttributes[PkcsObjectIdentifiers.IdAASigningCertificateV2];
+
+ if (scV1 == null && scV2 == null)
+ {
+ throw new TspValidationException("no signing certificate attribute present.");
+ }
+
+ if (scV1 != null && scV2 != null)
+ {
+ /*
+ * RFC 5035 5.4. If both attributes exist in a single message,
+ * they are independently evaluated.
+ */
+ }
+
+ if (request.ReqPolicy != null && !request.ReqPolicy.Equals(tstInfo.Policy))
+ {
+ throw new TspValidationException("TSA policy wrong for request.");
+ }
+ }
+ else if (this.Status == (int) PkiStatus.Granted || this.Status == (int) PkiStatus.GrantedWithMods)
+ {
+ throw new TspValidationException("no time stamp token found and one expected.");
+ }
+ }
+
+ /**
+ * return the ASN.1 encoded representation of this object.
+ */
+ public byte[] GetEncoded()
+ {
+ return resp.GetEncoded();
+ }
+ }
+}
diff --git a/crypto/src/tsp/TimeStampResponseGenerator.cs b/crypto/src/tsp/TimeStampResponseGenerator.cs
new file mode 100644
index 000000000..8d798de67
--- /dev/null
+++ b/crypto/src/tsp/TimeStampResponseGenerator.cs
@@ -0,0 +1,210 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cmp;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.Tsp;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Tsp
+{
+ /**
+ * Generator for RFC 3161 Time Stamp Responses.
+ */
+ public class TimeStampResponseGenerator
+ {
+ private PkiStatus status;
+
+ private Asn1EncodableVector statusStrings;
+
+ private int failInfo;
+ private TimeStampTokenGenerator tokenGenerator;
+ private IList acceptedAlgorithms;
+ private IList acceptedPolicies;
+ private IList acceptedExtensions;
+
+ public TimeStampResponseGenerator(
+ TimeStampTokenGenerator tokenGenerator,
+ IList acceptedAlgorithms)
+ : this(tokenGenerator, acceptedAlgorithms, null, null)
+ {
+ }
+
+ public TimeStampResponseGenerator(
+ TimeStampTokenGenerator tokenGenerator,
+ IList acceptedAlgorithms,
+ IList acceptedPolicy)
+ : this(tokenGenerator, acceptedAlgorithms, acceptedPolicy, null)
+ {
+ }
+
+ public TimeStampResponseGenerator(
+ TimeStampTokenGenerator tokenGenerator,
+ IList acceptedAlgorithms,
+ IList acceptedPolicies,
+ IList acceptedExtensions)
+ {
+ this.tokenGenerator = tokenGenerator;
+ this.acceptedAlgorithms = acceptedAlgorithms;
+ this.acceptedPolicies = acceptedPolicies;
+ this.acceptedExtensions = acceptedExtensions;
+
+ statusStrings = new Asn1EncodableVector();
+ }
+
+ private void AddStatusString(string statusString)
+ {
+ statusStrings.Add(new DerUtf8String(statusString));
+ }
+
+ private void SetFailInfoField(int field)
+ {
+ failInfo |= field;
+ }
+
+ private PkiStatusInfo GetPkiStatusInfo()
+ {
+ Asn1EncodableVector v = new Asn1EncodableVector(
+ new DerInteger((int)status));
+
+ if (statusStrings.Count > 0)
+ {
+ v.Add(new PkiFreeText(new DerSequence(statusStrings)));
+ }
+
+ if (failInfo != 0)
+ {
+ v.Add(new FailInfo(failInfo));
+ }
+
+ return new PkiStatusInfo(new DerSequence(v));
+ }
+
+ public TimeStampResponse Generate(
+ TimeStampRequest request,
+ BigInteger serialNumber,
+ DateTime genTime)
+ {
+ return Generate(request, serialNumber, new DateTimeObject(genTime));
+ }
+
+ /**
+ * Return an appropriate TimeStampResponse.
+ * <p>
+ * If genTime is null a timeNotAvailable error response will be returned.
+ *
+ * @param request the request this response is for.
+ * @param serialNumber serial number for the response token.
+ * @param genTime generation time for the response token.
+ * @param provider provider to use for signature calculation.
+ * @return
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchProviderException
+ * @throws TSPException
+ * </p>
+ */
+ public TimeStampResponse Generate(
+ TimeStampRequest request,
+ BigInteger serialNumber,
+ DateTimeObject genTime)
+ {
+ TimeStampResp resp;
+
+ try
+ {
+ if (genTime == null)
+ throw new TspValidationException("The time source is not available.",
+ PkiFailureInfo.TimeNotAvailable);
+
+ request.Validate(acceptedAlgorithms, acceptedPolicies, acceptedExtensions);
+
+ this.status = PkiStatus.Granted;
+ this.AddStatusString("Operation Okay");
+
+ PkiStatusInfo pkiStatusInfo = GetPkiStatusInfo();
+
+ ContentInfo tstTokenContentInfo;
+ try
+ {
+ TimeStampToken token = tokenGenerator.Generate(request, serialNumber, genTime.Value);
+ byte[] encoded = token.ToCmsSignedData().GetEncoded();
+
+ tstTokenContentInfo = ContentInfo.GetInstance(Asn1Object.FromByteArray(encoded));
+ }
+ catch (IOException e)
+ {
+ throw new TspException("Timestamp token received cannot be converted to ContentInfo", e);
+ }
+
+ resp = new TimeStampResp(pkiStatusInfo, tstTokenContentInfo);
+ }
+ catch (TspValidationException e)
+ {
+ status = PkiStatus.Rejection;
+
+ this.SetFailInfoField(e.FailureCode);
+ this.AddStatusString(e.Message);
+
+ PkiStatusInfo pkiStatusInfo = GetPkiStatusInfo();
+
+ resp = new TimeStampResp(pkiStatusInfo, null);
+ }
+
+ try
+ {
+ return new TimeStampResponse(resp);
+ }
+ catch (IOException e)
+ {
+ throw new TspException("created badly formatted response!", e);
+ }
+ }
+
+ class FailInfo
+ : DerBitString
+ {
+ internal FailInfo(
+ int failInfoValue)
+ : base(GetBytes(failInfoValue), GetPadBits(failInfoValue))
+ {
+ }
+ }
+
+ /**
+ * Generate a TimeStampResponse with chosen status and FailInfoField.
+ *
+ * @param status the PKIStatus to set.
+ * @param failInfoField the FailInfoField to set.
+ * @param statusString an optional string describing the failure.
+ * @return a TimeStampResponse with a failInfoField and optional statusString
+ * @throws TSPException in case the response could not be created
+ */
+ public TimeStampResponse GenerateFailResponse(PkiStatus status, int failInfoField, string statusString)
+ {
+ this.status = status;
+
+ this.SetFailInfoField(failInfoField);
+
+ if (statusString != null)
+ {
+ this.AddStatusString(statusString);
+ }
+
+ PkiStatusInfo pkiStatusInfo = GetPkiStatusInfo();
+
+ TimeStampResp resp = new TimeStampResp(pkiStatusInfo, null);
+
+ try
+ {
+ return new TimeStampResponse(resp);
+ }
+ catch (IOException e)
+ {
+ throw new TspException("created badly formatted response!", e);
+ }
+ }
+ }
+}
diff --git a/crypto/src/tsp/TimeStampToken.cs b/crypto/src/tsp/TimeStampToken.cs
new file mode 100644
index 000000000..51a9592dc
--- /dev/null
+++ b/crypto/src/tsp/TimeStampToken.cs
@@ -0,0 +1,305 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Ess;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Oiw;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Tsp;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Cms;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Tsp
+{
+ public class TimeStampToken
+ {
+ private readonly CmsSignedData tsToken;
+ private readonly SignerInformation tsaSignerInfo;
+// private readonly DateTime genTime;
+ private readonly TimeStampTokenInfo tstInfo;
+ private readonly CertID certID;
+
+ public TimeStampToken(
+ Asn1.Cms.ContentInfo contentInfo)
+ : this(new CmsSignedData(contentInfo))
+ {
+ }
+
+ public TimeStampToken(
+ CmsSignedData signedData)
+ {
+ this.tsToken = signedData;
+
+ if (!this.tsToken.SignedContentType.Equals(PkcsObjectIdentifiers.IdCTTstInfo))
+ {
+ throw new TspValidationException("ContentInfo object not for a time stamp.");
+ }
+
+ ICollection signers = tsToken.GetSignerInfos().GetSigners();
+
+ if (signers.Count != 1)
+ {
+ throw new ArgumentException("Time-stamp token signed by "
+ + signers.Count
+ + " signers, but it must contain just the TSA signature.");
+ }
+
+
+ IEnumerator signerEnum = signers.GetEnumerator();
+
+ signerEnum.MoveNext();
+ tsaSignerInfo = (SignerInformation) signerEnum.Current;
+
+ try
+ {
+ CmsProcessable content = tsToken.SignedContent;
+ MemoryStream bOut = new MemoryStream();
+
+ content.Write(bOut);
+
+ this.tstInfo = new TimeStampTokenInfo(
+ TstInfo.GetInstance(
+ Asn1Object.FromByteArray(bOut.ToArray())));
+
+ Asn1.Cms.Attribute attr = tsaSignerInfo.SignedAttributes[
+ PkcsObjectIdentifiers.IdAASigningCertificate];
+
+// if (attr == null)
+// {
+// throw new TspValidationException(
+// "no signing certificate attribute found, time stamp invalid.");
+// }
+//
+// SigningCertificate signCert = SigningCertificate.GetInstance(
+// attr.AttrValues[0]);
+//
+// this.certID = EssCertID.GetInstance(signCert.GetCerts()[0]);
+
+ if (attr != null)
+ {
+ SigningCertificate signCert = SigningCertificate.GetInstance(attr.AttrValues[0]);
+
+ this.certID = new CertID(EssCertID.GetInstance(signCert.GetCerts()[0]));
+ }
+ else
+ {
+ attr = tsaSignerInfo.SignedAttributes[PkcsObjectIdentifiers.IdAASigningCertificateV2];
+
+ if (attr == null)
+ throw new TspValidationException("no signing certificate attribute found, time stamp invalid.");
+
+ SigningCertificateV2 signCertV2 = SigningCertificateV2.GetInstance(attr.AttrValues[0]);
+
+ this.certID = new CertID(EssCertIDv2.GetInstance(signCertV2.GetCerts()[0]));
+ }
+ }
+ catch (CmsException e)
+ {
+ throw new TspException(e.Message, e.InnerException);
+ }
+ }
+
+ public TimeStampTokenInfo TimeStampInfo
+ {
+ get { return tstInfo; }
+ }
+
+ public SignerID SignerID
+ {
+ get { return tsaSignerInfo.SignerID; }
+ }
+
+ public Asn1.Cms.AttributeTable SignedAttributes
+ {
+ get { return tsaSignerInfo.SignedAttributes; }
+ }
+
+ public Asn1.Cms.AttributeTable UnsignedAttributes
+ {
+ get { return tsaSignerInfo.UnsignedAttributes; }
+ }
+
+ public IX509Store GetCertificates(
+ string type)
+ {
+ return tsToken.GetCertificates(type);
+ }
+
+ public IX509Store GetCrls(
+ string type)
+ {
+ return tsToken.GetCrls(type);
+ }
+
+ public IX509Store GetAttributeCertificates(
+ string type)
+ {
+ return tsToken.GetAttributeCertificates(type);
+ }
+
+ /**
+ * 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>
+ */
+ public void Validate(
+ X509Certificate cert)
+ {
+ try
+ {
+ byte[] hash = DigestUtilities.CalculateDigest(
+ certID.GetHashAlgorithmName(), cert.GetEncoded());
+
+ if (!Arrays.ConstantTimeAreEqual(certID.GetCertHash(), hash))
+ {
+ throw new TspValidationException("certificate hash does not match certID hash.");
+ }
+
+ if (certID.IssuerSerial != null)
+ {
+ if (!certID.IssuerSerial.Serial.Value.Equals(cert.SerialNumber))
+ {
+ throw new TspValidationException("certificate serial number does not match certID for signature.");
+ }
+
+ GeneralName[] names = certID.IssuerSerial.Issuer.GetNames();
+ X509Name principal = PrincipalUtilities.GetIssuerX509Principal(cert);
+ bool found = false;
+
+ for (int i = 0; i != names.Length; i++)
+ {
+ if (names[i].TagNo == 4
+ && X509Name.GetInstance(names[i].Name).Equivalent(principal))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ throw new TspValidationException("certificate name does not match certID for signature. ");
+ }
+ }
+
+ TspUtil.ValidateCertificate(cert);
+
+ cert.CheckValidity(tstInfo.GenTime);
+
+ if (!tsaSignerInfo.Verify(cert))
+ {
+ throw new TspValidationException("signature not created by certificate.");
+ }
+ }
+ catch (CmsException e)
+ {
+ if (e.InnerException != null)
+ {
+ throw new TspException(e.Message, e.InnerException);
+ }
+
+ throw new TspException("CMS exception: " + e, e);
+ }
+ catch (CertificateEncodingException e)
+ {
+ throw new TspException("problem processing certificate: " + e, e);
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new TspException("cannot find algorithm: " + e.Message, e);
+ }
+ }
+
+ /**
+ * Return the underlying CmsSignedData object.
+ *
+ * @return the underlying CMS structure.
+ */
+ public CmsSignedData ToCmsSignedData()
+ {
+ return tsToken;
+ }
+
+ /**
+ * Return a ASN.1 encoded byte stream representing the encoded object.
+ *
+ * @throws IOException if encoding fails.
+ */
+ public byte[] GetEncoded()
+ {
+ return tsToken.GetEncoded();
+ }
+
+
+ // perhaps this should be done using an interface on the ASN.1 classes...
+ private class CertID
+ {
+ private EssCertID certID;
+ private EssCertIDv2 certIDv2;
+
+ internal CertID(EssCertID certID)
+ {
+ this.certID = certID;
+ this.certIDv2 = null;
+ }
+
+ internal CertID(EssCertIDv2 certID)
+ {
+ this.certIDv2 = certID;
+ this.certID = null;
+ }
+
+ public string GetHashAlgorithmName()
+ {
+ if (certID != null)
+ return "SHA-1";
+
+ if (NistObjectIdentifiers.IdSha256.Equals(certIDv2.HashAlgorithm.ObjectID))
+ return "SHA-256";
+
+ return certIDv2.HashAlgorithm.ObjectID.Id;
+ }
+
+ public AlgorithmIdentifier GetHashAlgorithm()
+ {
+ return (certID != null)
+ ? new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1)
+ : certIDv2.HashAlgorithm;
+ }
+
+ public byte[] GetCertHash()
+ {
+ return certID != null
+ ? certID.GetCertHash()
+ : certIDv2.GetCertHash();
+ }
+
+ public IssuerSerial IssuerSerial
+ {
+ get
+ {
+ return certID != null
+ ? certID.IssuerSerial
+ : certIDv2.IssuerSerial;
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/src/tsp/TimeStampTokenGenerator.cs b/crypto/src/tsp/TimeStampTokenGenerator.cs
new file mode 100644
index 000000000..07eddd4b9
--- /dev/null
+++ b/crypto/src/tsp/TimeStampTokenGenerator.cs
@@ -0,0 +1,245 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Ess;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Tsp;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Cms;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Tsp
+{
+ public class TimeStampTokenGenerator
+ {
+ private int accuracySeconds = -1;
+ private int accuracyMillis = -1;
+ private int accuracyMicros = -1;
+ private bool ordering = false;
+ private GeneralName tsa = null;
+ private string tsaPolicyOID;
+
+ private AsymmetricKeyParameter key;
+ private X509Certificate cert;
+ private string digestOID;
+ private Asn1.Cms.AttributeTable signedAttr;
+ private Asn1.Cms.AttributeTable unsignedAttr;
+ private IX509Store x509Certs;
+ private IX509Store x509Crls;
+
+ /**
+ * basic creation - only the default attributes will be included here.
+ */
+ public TimeStampTokenGenerator(
+ AsymmetricKeyParameter key,
+ X509Certificate cert,
+ string digestOID,
+ string tsaPolicyOID)
+ : this(key, cert, digestOID, tsaPolicyOID, null, null)
+ {
+ }
+
+ /**
+ * create with a signer with extra signed/unsigned attributes.
+ */
+ public TimeStampTokenGenerator(
+ AsymmetricKeyParameter key,
+ X509Certificate cert,
+ string digestOID,
+ string tsaPolicyOID,
+ Asn1.Cms.AttributeTable signedAttr,
+ Asn1.Cms.AttributeTable unsignedAttr)
+ {
+ this.key = key;
+ this.cert = cert;
+ this.digestOID = digestOID;
+ this.tsaPolicyOID = tsaPolicyOID;
+ this.unsignedAttr = unsignedAttr;
+
+ TspUtil.ValidateCertificate(cert);
+
+ //
+ // Add the ESSCertID attribute
+ //
+ IDictionary signedAttrs;
+ if (signedAttr != null)
+ {
+ signedAttrs = signedAttr.ToDictionary();
+ }
+ else
+ {
+ signedAttrs = Platform.CreateHashtable();
+ }
+
+ try
+ {
+ byte[] hash = DigestUtilities.CalculateDigest("SHA-1", cert.GetEncoded());
+
+ EssCertID essCertid = new EssCertID(hash);
+
+ Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute(
+ PkcsObjectIdentifiers.IdAASigningCertificate,
+ new DerSet(new SigningCertificate(essCertid)));
+
+ signedAttrs[attr.AttrType] = attr;
+ }
+ catch (CertificateEncodingException e)
+ {
+ throw new TspException("Exception processing certificate.", e);
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new TspException("Can't find a SHA-1 implementation.", e);
+ }
+
+ this.signedAttr = new Asn1.Cms.AttributeTable(signedAttrs);
+ }
+
+ public void SetCertificates(
+ IX509Store certificates)
+ {
+ this.x509Certs = certificates;
+ }
+
+ public void SetCrls(
+ IX509Store crls)
+ {
+ this.x509Crls = crls;
+ }
+
+ public void SetAccuracySeconds(
+ int accuracySeconds)
+ {
+ this.accuracySeconds = accuracySeconds;
+ }
+
+ public void SetAccuracyMillis(
+ int accuracyMillis)
+ {
+ this.accuracyMillis = accuracyMillis;
+ }
+
+ public void SetAccuracyMicros(
+ int accuracyMicros)
+ {
+ this.accuracyMicros = accuracyMicros;
+ }
+
+ public void SetOrdering(
+ bool ordering)
+ {
+ this.ordering = ordering;
+ }
+
+ public void SetTsa(
+ GeneralName tsa)
+ {
+ this.tsa = tsa;
+ }
+
+ //------------------------------------------------------------------------------
+
+ public TimeStampToken Generate(
+ TimeStampRequest request,
+ BigInteger serialNumber,
+ DateTime genTime)
+ {
+ DerObjectIdentifier digestAlgOID = new DerObjectIdentifier(request.MessageImprintAlgOid);
+
+ AlgorithmIdentifier algID = new AlgorithmIdentifier(digestAlgOID, DerNull.Instance);
+ MessageImprint messageImprint = new MessageImprint(algID, request.GetMessageImprintDigest());
+
+ Accuracy accuracy = null;
+ if (accuracySeconds > 0 || accuracyMillis > 0 || accuracyMicros > 0)
+ {
+ DerInteger seconds = null;
+ if (accuracySeconds > 0)
+ {
+ seconds = new DerInteger(accuracySeconds);
+ }
+
+ DerInteger millis = null;
+ if (accuracyMillis > 0)
+ {
+ millis = new DerInteger(accuracyMillis);
+ }
+
+ DerInteger micros = null;
+ if (accuracyMicros > 0)
+ {
+ micros = new DerInteger(accuracyMicros);
+ }
+
+ accuracy = new Accuracy(seconds, millis, micros);
+ }
+
+ DerBoolean derOrdering = null;
+ if (ordering)
+ {
+ derOrdering = DerBoolean.GetInstance(ordering);
+ }
+
+ DerInteger nonce = null;
+ if (request.Nonce != null)
+ {
+ nonce = new DerInteger(request.Nonce);
+ }
+
+ DerObjectIdentifier tsaPolicy = new DerObjectIdentifier(tsaPolicyOID);
+ if (request.ReqPolicy != null)
+ {
+ tsaPolicy = new DerObjectIdentifier(request.ReqPolicy);
+ }
+
+ TstInfo tstInfo = new TstInfo(tsaPolicy, messageImprint,
+ new DerInteger(serialNumber), new DerGeneralizedTime(genTime), accuracy,
+ derOrdering, nonce, tsa, request.Extensions);
+
+ try
+ {
+ CmsSignedDataGenerator signedDataGenerator = new CmsSignedDataGenerator();
+
+ byte[] derEncodedTstInfo = tstInfo.GetDerEncoded();
+
+ if (request.CertReq)
+ {
+ signedDataGenerator.AddCertificates(x509Certs);
+ }
+
+ signedDataGenerator.AddCrls(x509Crls);
+ signedDataGenerator.AddSigner(key, cert, digestOID, signedAttr, unsignedAttr);
+
+ CmsSignedData signedData = signedDataGenerator.Generate(
+ PkcsObjectIdentifiers.IdCTTstInfo.Id,
+ new CmsProcessableByteArray(derEncodedTstInfo),
+ true);
+
+ return new TimeStampToken(signedData);
+ }
+ catch (CmsException cmsEx)
+ {
+ throw new TspException("Error generating time-stamp token", cmsEx);
+ }
+ catch (IOException e)
+ {
+ throw new TspException("Exception encoding info", e);
+ }
+ catch (X509StoreException e)
+ {
+ throw new TspException("Exception handling CertStore", e);
+ }
+// catch (InvalidAlgorithmParameterException e)
+// {
+// throw new TspException("Exception handling CertStore CRLs", e);
+// }
+ }
+ }
+}
diff --git a/crypto/src/tsp/TimeStampTokenInfo.cs b/crypto/src/tsp/TimeStampTokenInfo.cs
new file mode 100644
index 000000000..5027a87c4
--- /dev/null
+++ b/crypto/src/tsp/TimeStampTokenInfo.cs
@@ -0,0 +1,107 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Tsp;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Tsp
+{
+ public class TimeStampTokenInfo
+ {
+ private TstInfo tstInfo;
+ private DateTime genTime;
+
+ public TimeStampTokenInfo(
+ TstInfo tstInfo)
+ {
+ this.tstInfo = tstInfo;
+
+ try
+ {
+ this.genTime = tstInfo.GenTime.ToDateTime();
+ }
+ catch (Exception e)
+ {
+ throw new TspException("unable to parse genTime field: " + e.Message);
+ }
+ }
+
+ public bool IsOrdered
+ {
+ get { return tstInfo.Ordering.IsTrue; }
+ }
+
+ public Accuracy Accuracy
+ {
+ get { return tstInfo.Accuracy; }
+ }
+
+ public DateTime GenTime
+ {
+ get { return genTime; }
+ }
+
+ public GenTimeAccuracy GenTimeAccuracy
+ {
+ get
+ {
+ return this.Accuracy == null
+ ? null
+ : new GenTimeAccuracy(this.Accuracy);
+ }
+ }
+
+ public string Policy
+ {
+ get { return tstInfo.Policy.Id; }
+ }
+
+ public BigInteger SerialNumber
+ {
+ get { return tstInfo.SerialNumber.Value; }
+ }
+
+ public GeneralName Tsa
+ {
+ get { return tstInfo.Tsa; }
+ }
+
+ /**
+ * @return the nonce value, null if there isn't one.
+ */
+ public BigInteger Nonce
+ {
+ get
+ {
+ return tstInfo.Nonce == null
+ ? null
+ : tstInfo.Nonce.Value;
+ }
+ }
+
+ public AlgorithmIdentifier HashAlgorithm
+ {
+ get { return tstInfo.MessageImprint.HashAlgorithm; }
+ }
+
+ public string MessageImprintAlgOid
+ {
+ get { return tstInfo.MessageImprint.HashAlgorithm.ObjectID.Id; }
+ }
+
+ public byte[] GetMessageImprintDigest()
+ {
+ return tstInfo.MessageImprint.GetHashedMessage();
+ }
+
+ public byte[] GetEncoded()
+ {
+ return tstInfo.GetEncoded();
+ }
+
+ public TstInfo TstInfo
+ {
+ get { return tstInfo; }
+ }
+ }
+}
|