You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
326 lines
8.4 KiB
326 lines
8.4 KiB
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR) |
|
#pragma warning disable |
|
using System; |
|
using System.Collections; |
|
using System.IO; |
|
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.Ess; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.Nist; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.Oiw; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.Pkcs; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.Tsp; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.X509; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Cms; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Security; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Security.Certificates; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.X509; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.X509.Store; |
|
|
|
namespace BestHTTP.SecureProtocol.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) |
|
{ |
|
|
|
if (attr.AttrValues[0] is SigningCertificateV2) |
|
{ |
|
SigningCertificateV2 signCert = SigningCertificateV2.GetInstance(attr.AttrValues[0]); |
|
this.certID = new CertID(EssCertIDv2.GetInstance(signCert.GetCerts()[0])); |
|
} |
|
else |
|
{ |
|
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 GetCertificates() |
|
{ |
|
return tsToken.GetCertificates(); |
|
} |
|
|
|
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.HasValue(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(Asn1Encodable.Der); |
|
} |
|
|
|
/** |
|
* return the ASN.1 encoded representation of this object using the specified encoding. |
|
* |
|
* @param encoding the ASN.1 encoding format to use ("BER" or "DER"). |
|
*/ |
|
public byte[] GetEncoded(string encoding) |
|
{ |
|
return tsToken.GetEncoded(encoding); |
|
} |
|
|
|
// 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.Algorithm)) |
|
return "SHA-256"; |
|
|
|
return certIDv2.HashAlgorithm.Algorithm.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; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
#pragma warning restore |
|
#endif
|
|
|