Initial Commit

This commit is contained in:
2023-06-21 12:46:23 -04:00
commit c70248a520
1352 changed files with 336780 additions and 0 deletions

View File

@@ -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");
}
}
}

View File

@@ -0,0 +1,47 @@
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;
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 ArrayList Allowed;
static TspAlgorithms()
{
string[] algs = new string[]
{
Gost3411, MD5, Sha1, Sha224, Sha256, Sha384, Sha512, RipeMD128, RipeMD160, RipeMD256
};
Allowed = new ArrayList();
foreach (string alg in algs)
{
Allowed.Add(alg);
}
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
namespace Org.BouncyCastle.Tsp
{
public class TspException
: Exception
{
public TspException()
{
}
public TspException(
string message)
: base(message)
{
}
public TspException(
string message,
Exception e)
: base(message, e)
{
}
}
}

View File

@@ -0,0 +1,115 @@
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.Security;
using Org.BouncyCastle.X509;
namespace Org.BouncyCastle.Tsp
{
public class TspUtil
{
private static readonly IDictionary digestLengths = new Hashtable();
private static readonly IDictionary digestNames = new Hashtable();
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);
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");
}
/**
* 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)
{
string digestName = GetDigestAlgName(digestAlgOID);
try
{
if (digestLengths.Contains(digestAlgOID))
{
return (int) digestLengths[digestAlgOID];
}
return DigestUtilities.GetDigest(digestName).GetDigestSize();
}
catch (SecurityUtilityException e)
{
throw new TspException("digest algorithm cannot be found.", e);
}
}
}
}

View File

@@ -0,0 +1,39 @@
namespace Org.BouncyCastle.Tsp
{
/**
* Exception thrown if a TSP request or response fails to validate.
* <p>
* If a failure code is assciated with the exception it can be retrieved using
* the getFailureCode() method.</p>
*/
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; }
}
}
}

View File

@@ -0,0 +1,179 @@
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;
public TimeStampRequest(
TimeStampReq req)
{
this.req = req;
}
/**
* 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; }
}
protected override X509Extensions GetX509Extensions()
{
return Extensions;
}
}
}

View File

@@ -0,0 +1,99 @@
using System;
using System.Collections;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Tsp;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Math;
namespace Org.BouncyCastle.Tsp
{
/**
* Generator for RFC 3161 Time Stamp Request objects.
*/
public class TimeStampRequestGenerator
{
private DerObjectIdentifier reqPolicy;
private DerBoolean certReq;
private Hashtable extensions = new Hashtable();
private ArrayList extOrdering = new ArrayList();
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
*/
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.
*/
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);
}
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));
}
}
}

View File

@@ -0,0 +1,173 @@
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.AreEqual(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.");
}
if (tok.SignedAttributes[PkcsObjectIdentifiers.IdAASigningCertificate] == null)
{
throw new TspValidationException("no signing certificate attribute present.");
}
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();
}
}
}

View File

@@ -0,0 +1,150 @@
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;
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 = 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)
{
TimeStampResp resp;
try
{
request.Validate(acceptedAlgorithms, acceptedPolicies, acceptedExtensions);
status = PkiStatus.Granted;
this.addStatusString("Operation Okay");
PkiStatusInfo pkiStatusInfo = getPkiStatusInfo();
ContentInfo tstTokenContentInfo;
try
{
TimeStampToken token = tokenGenerator.Generate(request, serialNumber, genTime);
byte[] encoded = token.ToCmsSignedData().GetEncoded();
tstTokenContentInfo = ContentInfo.GetInstance(Asn1Object.FromByteArray(encoded));
}
catch (IOException ioEx)
{
throw new TspException(
"Timestamp token received cannot be converted to ContentInfo", ioEx);
}
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)
{
throw new TspException("created badly formatted response!");
}
}
class FailInfo
: DerBitString
{
internal FailInfo(
int failInfoValue)
: base(GetBytes(failInfoValue), GetPadBits(failInfoValue))
{
}
}
}
}

View File

@@ -0,0 +1,304 @@
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.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.SignedContentTypeOid.Equals(PkcsObjectIdentifiers.IdCTTstInfo.Id))
{
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 GetCertificatesAndCrls(
// string type)
// {
// return tsToken.GetCertificatesAndCrls(type);
// }
public IX509Store GetCertificates(
string type)
{
return tsToken.GetCertificates(type);
}
public IX509Store GetCrls(
string type)
{
return tsToken.GetCrls(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)
{
IDigest digest;
try
{
digest = DigestUtilities.GetDigest(certID.GetHashAlgorithm());
}
catch (SecurityUtilityException e)
{
throw new TspException("cannot find algorithm: " + e.Message, e);
}
try
{
byte[] certEncoded = cert.GetEncoded();
digest.BlockUpdate(certEncoded, 0, certEncoded.Length);
byte[] hash = DigestUtilities.DoFinal(digest);
if (!Arrays.AreEqual(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);
}
}
/**
* 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 GetHashAlgorithm()
{
if (certID != null)
return "SHA-1";
if (NistObjectIdentifiers.IdSha256.Equals(certIDv2.HashAlgorithm.ObjectID))
return "SHA-256";
return certIDv2.HashAlgorithm.ObjectID.Id;
}
public byte[] GetCertHash()
{
return certID != null
? certID.GetCertHash()
: certIDv2.GetCertHash();
}
public IssuerSerial IssuerSerial
{
get
{
return certID != null
? certID.IssuerSerial
: certIDv2.IssuerSerial;
}
}
}
}
}

View File

@@ -0,0 +1,252 @@
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.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
//
Hashtable signedAttrs;
if (signedAttr != null)
{
signedAttrs = signedAttr.ToHashtable();
}
else
{
signedAttrs = new Hashtable();
}
IDigest digest;
try
{
digest = DigestUtilities.GetDigest("SHA-1");
}
catch (Exception e)
{
throw new TspException("Can't find a SHA-1 implementation.", e);
}
try
{
byte[] certEncoded = cert.GetEncoded();
digest.BlockUpdate(certEncoded, 0, certEncoded.Length);
byte[] hash = DigestUtilities.DoFinal(digest);
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);
}
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);
// }
}
}
}

View File

@@ -0,0 +1,102 @@
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 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; }
}
}
}