RevocationValues ::= SEQUENCE { crlVals [0] SEQUENCE OF CertificateList OPTIONAL, ocspVals [1] SEQUENCE OF BasicOCSPResponse OPTIONAL, otherRevVals [2] OtherRevVals OPTIONAL }
/// <exception cref="System.IO.IOException"></exception> //private IDictionary<DerObjectIdentifier, Asn1Encodable> ExtendUnsignedAttributes(IDictionary // <DerObjectIdentifier, Asn1Encodable> unsignedAttrs, X509Certificate signingCertificate // , DateTime signingDate, CertificateSource optionalCertificateSource) private IDictionary ExtendUnsignedAttributes(IDictionary unsignedAttrs , X509Certificate signingCertificate, DateTime signingDate , CertificateSource optionalCertificateSource) { ValidationContext validationContext = certificateVerifier.ValidateCertificate(signingCertificate , signingDate, optionalCertificateSource, null, null); try { IList<X509CertificateStructure> certificateValues = new AList<X509CertificateStructure >(); AList<CertificateList> crlValues = new AList<CertificateList>(); AList<BasicOcspResponse> ocspValues = new AList<BasicOcspResponse>(); foreach (CertificateAndContext c in validationContext.GetNeededCertificates()) { if (!c.Equals(signingCertificate)) { certificateValues.AddItem(X509CertificateStructure.GetInstance(((Asn1Sequence)Asn1Object.FromByteArray (c.GetCertificate().GetEncoded())))); } } foreach (X509Crl relatedcrl in validationContext.GetNeededCRL()) { crlValues.AddItem(CertificateList.GetInstance((Asn1Sequence)Asn1Object.FromByteArray(((X509Crl )relatedcrl).GetEncoded()))); } foreach (BasicOcspResp relatedocspresp in validationContext.GetNeededOCSPResp()) { ocspValues.AddItem((BasicOcspResponse.GetInstance((Asn1Sequence)Asn1Object.FromByteArray( relatedocspresp.GetEncoded())))); } CertificateList[] crlValuesArray = new CertificateList[crlValues.Count]; BasicOcspResponse[] ocspValuesArray = new BasicOcspResponse[ocspValues.Count]; RevocationValues revocationValues = new RevocationValues(Sharpen.Collections.ToArray (crlValues, crlValuesArray), Sharpen.Collections.ToArray(ocspValues, ocspValuesArray ), null); //unsignedAttrs.Put(PkcsObjectIdentifiers.IdAAEtsRevocationValues, new Attribute unsignedAttrs.Add(PkcsObjectIdentifiers.IdAAEtsRevocationValues, new BcCms.Attribute (PkcsObjectIdentifiers.IdAAEtsRevocationValues, new DerSet(revocationValues)) ); X509CertificateStructure[] certValuesArray = new X509CertificateStructure[certificateValues .Count]; //unsignedAttrs.Put(PkcsObjectIdentifiers.IdAAEtsCertValues, new Attribute(PkcsObjectIdentifiers.IdAAEtsCertValues, new DerSet(new DerSequence(Sharpen.Collections.ToArray(certificateValues unsignedAttrs.Add(PkcsObjectIdentifiers.IdAAEtsCertValues, new BcCms.Attribute(PkcsObjectIdentifiers.IdAAEtsCertValues, new DerSet(new DerSequence(Sharpen.Collections.ToArray(certificateValues , certValuesArray))))); } catch (CertificateEncodingException e) { throw new RuntimeException(e); } catch (CrlException e) { throw new RuntimeException(e); } return unsignedAttrs; }
protected void Complete(Level level, Stream embedded, Stream signed, X509Certificate2 providedSigner, out TimemarkKey timemarkKey) { trace.TraceEvent(TraceEventType.Information, 0, "Completing the message with of {0} bytes to level {1}", signed.Length, level); //Prepare generator, parser and time-mark Key CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator(); CmsSignedDataParser parser = new CmsSignedDataParser(signed); timemarkKey = new TimemarkKey(); //preset the digests so we can add the signers afterwards gen.AddDigests(parser.DigestOids); //Copy the content CmsTypedStream signedContent = parser.GetSignedContent(); Stream contentOut = gen.Open(embedded, parser.SignedContentType.Id, true); signedContent.ContentStream.CopyTo(contentOut); //Extract the signer info SignerInformationStore signerInfoStore = parser.GetSignerInfos(); IEnumerator signerInfos = signerInfoStore.GetSigners().GetEnumerator(); if (!signerInfos.MoveNext()) { trace.TraceEvent(TraceEventType.Error, 0, "The message to complete does not contain a signature"); throw new InvalidMessageException("The message does not contain a signature"); } SignerInformation signerInfo = (SignerInformation)signerInfos.Current; if (signerInfos.MoveNext()) { trace.TraceEvent(TraceEventType.Error, 0, "The message to complete does not contain more then one signature"); throw new InvalidMessageException("The message does contain multiple signatures, which isn't supported"); } //Extract the signing key timemarkKey.SignatureValue = signerInfo.GetSignature(); //Extract the unsigned attributes & signing time bool hasSigningTime; IDictionary unsignedAttributes = signerInfo.UnsignedAttributes != null ? signerInfo.UnsignedAttributes.ToDictionary() : new Hashtable(); BC::Asn1.Cms.Attribute singingTimeAttr = signerInfo.SignedAttributes != null ? signerInfo.SignedAttributes[CmsAttributes.SigningTime] : null; if (singingTimeAttr == null) { trace.TraceEvent(TraceEventType.Warning, 0, "The message to complete does not contain a signing time"); hasSigningTime = false; timemarkKey.SigningTime = DateTime.UtcNow; } else { hasSigningTime = false; timemarkKey.SigningTime = new BC::Asn1.Cms.Time(((DerSet)singingTimeAttr.AttrValues)[0].ToAsn1Object()).Date; } //Extract the signer, if available IX509Store embeddedCerts = parser.GetCertificates("Collection"); if (embeddedCerts != null && embeddedCerts.GetMatches(null).Count > 0) { //Embedded certs found, we use that IEnumerator signerCerts = embeddedCerts.GetMatches(signerInfo.SignerID).GetEnumerator(); if (!signerCerts.MoveNext()) { trace.TraceEvent(TraceEventType.Error, 0, "The message does contains certificates, but the signing certificate is missing"); throw new InvalidMessageException("The message does not contain the signer certificate"); } timemarkKey.Signer = new X509Certificate2(((BC::X509.X509Certificate)signerCerts.Current).GetEncoded()); trace.TraceEvent(TraceEventType.Verbose, 0, "The message contains certificates, of which {0} is the signer", timemarkKey.Signer.Subject); //Add the certs to the new message gen.AddCertificates(embeddedCerts); } else { //No embedded certs, lets construct it. if (providedSigner == null) { trace.TraceEvent(TraceEventType.Error, 0, "The provided message does not contain any embedded certificates"); throw new InvalidMessageException("The message does not contain any embedded certificates"); } timemarkKey.Signer = providedSigner; trace.TraceEvent(TraceEventType.Verbose, 0, "The message does not contains certificates, adding the chain of {0}", timemarkKey.Signer.Subject); //Construct the chain of certificates Chain chain = timemarkKey.Signer.BuildBasicChain(timemarkKey.SigningTime, extraStore); if (chain.ChainStatus.Count(x => x.Status != X509ChainStatusFlags.NoError) > 0) { trace.TraceEvent(TraceEventType.Error, 0, "The certification chain of {0} failed with errors", chain.ChainElements[0].Certificate.Subject); throw new InvalidMessageException(string.Format("The certificate chain of the signer {0} fails basic validation", timemarkKey.Signer.Subject)); } List<BC::X509.X509Certificate> senderChainCollection = new List<BC::X509.X509Certificate>(); foreach (ChainElement ce in chain.ChainElements) { trace.TraceEvent(TraceEventType.Verbose, 0, "Adding the certificate {0} to the message", ce.Certificate.Subject); senderChainCollection.Add(DotNetUtilities.FromX509Certificate(ce.Certificate)); } embeddedCerts = X509StoreFactory.Create("CERTIFICATE/COLLECTION", new X509CollectionStoreParameters(senderChainCollection)); //Add the certificates to the new message gen.AddCertificates(embeddedCerts); } //Getting any existing time stamps TimeStampToken tst = null; BC::Asn1.Cms.Attribute timestampAttr = (BC::Asn1.Cms.Attribute)unsignedAttributes[PkcsObjectIdentifiers.IdAASignatureTimeStampToken]; if (timestampAttr == null || ((DerSet)timestampAttr.AttrValues).Count == 0) { //there is no TST if ((level & Level.T_Level) == Level.T_Level && timestampProvider != null) { //There should be a TST if (DateTime.UtcNow > (timemarkKey.SigningTime + EteeActiveConfig.ClockSkewness + Settings.Default.TimestampGracePeriod)) { trace.TraceEvent(TraceEventType.Error, 0, "The message was created on {0}, which is beyond the allows period of {2} to time-stamp", timemarkKey.SigningTime, Settings.Default.TimestampGracePeriod); throw new InvalidMessageException("The message it to old to add a time-stamp"); } SHA256 sha = SHA256.Create(); byte[] signatureHash = sha.ComputeHash(timemarkKey.SignatureValue); trace.TraceEvent(TraceEventType.Verbose, 0, "SHA-256 hashed the signature value from {0} to {1}", Convert.ToBase64String(timemarkKey.SignatureValue), Convert.ToBase64String(signatureHash)); byte[] rawTst = timestampProvider.GetTimestampFromDocumentHash(signatureHash, "http://www.w3.org/2001/04/xmlenc#sha256"); tst = rawTst.ToTimeStampToken(); if (!tst.IsMatch(new MemoryStream(timemarkKey.SignatureValue))) { trace.TraceEvent(TraceEventType.Error, 0, "The time-stamp does not correspond to the signature value {0}", Convert.ToBase64String(timemarkKey.SignatureValue)); throw new InvalidOperationException("The time-stamp authority did not return a matching time-stamp"); } //Don't verify the time-stamp, it is done later //embed TST BC::Asn1.Cms.Attribute signatureTstAttr = new BC::Asn1.Cms.Attribute(PkcsObjectIdentifiers.IdAASignatureTimeStampToken, new DerSet(Asn1Object.FromByteArray(rawTst))); unsignedAttributes[signatureTstAttr.AttrType] = signatureTstAttr; trace.TraceEvent(TraceEventType.Verbose, 0, "Added the time-stamp: {0}", Convert.ToBase64String(rawTst)); //The certs are part of the TST, so no need to add them to the CMS } } else { //There is one, extract it we need it later DerSet rawTsts = (DerSet)timestampAttr.AttrValues; if (rawTsts.Count > 1) { trace.TraceEvent(TraceEventType.Error, 0, "There are {0} signature timestamps present", rawTsts.Count); throw new NotSupportedException("The library does not support more then one time-stamp"); } tst = rawTsts[0].GetEncoded().ToTimeStampToken(); if (!hasSigningTime) { trace.TraceEvent(TraceEventType.Information, 0, "Implicit signing time {0} is replaced with time-stamp time {1}", timemarkKey.SigningTime, tst.TimeStampInfo.GenTime); timemarkKey.SigningTime = tst.TimeStampInfo.GenTime; } if (tst.TimeStampInfo.GenTime > (timemarkKey.SigningTime + EteeActiveConfig.ClockSkewness + Settings.Default.TimestampGracePeriod)) { trace.TraceEvent(TraceEventType.Error, 0, "The message was time-stamped on {0}, which is beyond the allows period of {2} from the signing time {1}", tst.TimeStampInfo.GenTime, timemarkKey.SigningTime, Settings.Default.TimestampGracePeriod); throw new InvalidMessageException("The message wasn't timestamped on time"); } } if ((level & Level.L_Level) == Level.L_Level) { //Add revocation info IList<CertificateList> crls = null; IList<BasicOcspResponse> ocsps = null; BC::Asn1.Cms.Attribute revocationAttr = (BC::Asn1.Cms.Attribute)unsignedAttributes[PkcsObjectIdentifiers.IdAAEtsRevocationValues]; if (revocationAttr != null) { DerSet revocationInfoSet = (DerSet) revocationAttr.AttrValues; if (revocationInfoSet == null || revocationInfoSet.Count == 0) { RevocationValues revocationInfo = RevocationValues.GetInstance(revocationInfoSet[0]); crls = new List<CertificateList>(revocationInfo.GetCrlVals()); trace.TraceEvent(TraceEventType.Verbose, 0, "Found {1} CRL's in the message", crls.Count); ocsps = new List<BasicOcspResponse>(revocationInfo.GetOcspVals()); trace.TraceEvent(TraceEventType.Verbose, 0, "Found {1} OCSP's in the message", ocsps.Count); } } if (crls == null) crls = new List<CertificateList>(); if (ocsps == null) ocsps = new List<BasicOcspResponse>(); //Add the message certificate chain revocation info + check if successful var extraStore = new X509Certificate2Collection(); foreach (Org.BouncyCastle.X509.X509Certificate cert in embeddedCerts.GetMatches(null)) { extraStore.Add(new X509Certificate2(cert.GetEncoded())); } Chain chain = timemarkKey.Signer.BuildChain(timemarkKey.SigningTime, extraStore, ref crls, ref ocsps); if (chain.ChainStatus.Count(x => x.Status != X509ChainStatusFlags.NoError) > 0) { trace.TraceEvent(TraceEventType.Error, 0, "The certificate chain of the signer {0} failed with {1} issues: {2}, {3}", timemarkKey.Signer.Subject, chain.ChainStatus.Count, chain.ChainStatus[0].Status, chain.ChainStatus[0].StatusInformation); throw new InvalidMessageException(string.Format("The certificate chain of the signer {0} fails revocation validation", timemarkKey.Signer.Subject)); } //Add the time-stamp certificate chain revocation info + check if successful if (tst != null) { Timestamp ts = tst.Validate(ref crls, ref ocsps); if (ts.TimestampStatus.Count(x => x.Status != X509ChainStatusFlags.NoError) > 0) { trace.TraceEvent(TraceEventType.Error, 0, "The certificate chain of the time-stamp signer {0} failed with {1} issues: {2}, {3}", ts.CertificateChain.ChainElements[0].Certificate.Subject, ts.TimestampStatus.Count, ts.TimestampStatus[0].Status, ts.TimestampStatus[0].StatusInformation); throw new InvalidMessageException("The embedded time-stamp fails validation"); } } //Embed revocation info RevocationValues revocationValues = new RevocationValues(crls, ocsps, null); revocationAttr = new BC::Asn1.Cms.Attribute(PkcsObjectIdentifiers.IdAAEtsRevocationValues, new DerSet(revocationValues.ToAsn1Object())); unsignedAttributes[revocationAttr.AttrType] = revocationAttr; trace.TraceEvent(TraceEventType.Verbose, 0, "Added {0} OCSP's and {1} CRL's to the message", ocsps.Count, crls.Count); } //Update the unsigned attributes of the signer info signerInfo = SignerInformation.ReplaceUnsignedAttributes(signerInfo, new BC::Asn1.Cms.AttributeTable(unsignedAttributes)); //Copy the signer gen.AddSigners(new SignerInformationStore(new SignerInformation[] { signerInfo })); contentOut.Close(); }