Exemplo n.º 1
0
        private DateTime ExtractSigningTime(SignerInformation signerInfo)
        {
            BC::Asn1.Cms.Attribute singingTimeAttr = signerInfo.SignedAttributes?[CmsAttributes.SigningTime];
            if (singingTimeAttr != null)
            {
                DateTime date = new BC::Asn1.Cms.Time(((DerSet)singingTimeAttr.AttrValues)[0].ToAsn1Object()).Date;
                if (date.Kind == DateTimeKind.Unspecified)
                {
                    return(new DateTime(date.Ticks, DateTimeKind.Utc));
                }
                else
                {
                    return(date.ToUniversalTime());
                }
            }
            else
            {
#if NETFRAMEWORK
                trace.TraceEvent(TraceEventType.Warning, 0, "The message to complete does not contain a signing time");
#else
                logger.LogWarning("The message to complete does not contain a signing time");
#endif
                return(default);
Exemplo n.º 2
0
        protected void Sign(Stream signed, Stream unsigned, X509Certificate2 selectedCert)
        {
            BC::X509.X509Certificate bcSelectedCert = DotNetUtilities.FromX509Certificate(selectedCert);
            trace.TraceEvent(TraceEventType.Information, 0, "Signing the message in name of {0}", selectedCert.Subject);

            //Signing time
            DateTime signingTime = DateTime.UtcNow;

            CmsSignedDataStreamGenerator signedGenerator = new CmsSignedDataStreamGenerator();

            //For compatibility we don't add it to the CMS (most implementations, including BC, don't support OCSP here)
            //IX509Store crlStore = X509StoreFactory.Create("CRL/COLLECTION", new X509CollectionStoreParameters(crl's));
            //signedGenerator.AddCrls(crlStore);

            //add signed attributes to the signature (own signing time)
            IDictionary signedAttrDictionary = new Hashtable();
            BC::Asn1.Cms.Attribute signTimeattr = new BC::Asn1.Cms.Attribute(CmsAttributes.SigningTime,
                    new DerSet(new BC::Asn1.Cms.Time(signingTime)));
            signedAttrDictionary.Add(signTimeattr.AttrType, signTimeattr);
            BC::Asn1.Cms.AttributeTable signedAttrTable = new BC.Asn1.Cms.AttributeTable(signedAttrDictionary);

            //Add the signatures
            SignatureAlgorithm signAlgo;
            if (((RSACryptoServiceProvider)selectedCert.PrivateKey).CspKeyContainerInfo.Exportable) {
                signAlgo =  EteeActiveConfig.Seal.NativeSignatureAlgorithm;
                signedGenerator.AddSigner(DotNetUtilities.GetKeyPair(selectedCert.PrivateKey).Private,
                    bcSelectedCert, signAlgo.EncryptionAlgorithm.Value, signAlgo.DigestAlgorithm.Value,
                    signedAttrTable, null);
            } else {
                signAlgo = EteeActiveConfig.Seal.WindowsSignatureAlgorithm;
                signedGenerator.AddSigner(new ProxyRsaKeyParameters((RSACryptoServiceProvider)selectedCert.PrivateKey),
                    bcSelectedCert, signAlgo.EncryptionAlgorithm.Value, signAlgo.DigestAlgorithm.Value,
                    signedAttrTable, null);
            }
            trace.TraceEvent(TraceEventType.Verbose, 0, "Added Signer [EncAlgo={0} ({1}), DigestAlgo={2} ({3})",
                signAlgo.EncryptionAlgorithm.FriendlyName,
                signAlgo.EncryptionAlgorithm.Value,
                signAlgo.DigestAlgorithm.FriendlyName,
                signAlgo.DigestAlgorithm.Value);

            Stream signingStream = signedGenerator.Open(signed, true);
            trace.TraceEvent(TraceEventType.Verbose, 0, "Create embedded signed message (still empty)");
            try
            {
                unsigned.CopyTo(signingStream);
                trace.TraceEvent(TraceEventType.Verbose, 0, "Message copied and digest calculated");
            }
            finally
            {
                signingStream.Close();
                trace.TraceEvent(TraceEventType.Verbose, 0, "Signature block added");
            }
        }
Exemplo n.º 3
0
        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();
        }