Exemplo n.º 1
0
        private SignatureSecurityInformation VerifyInMem(Stream verifiedContent, Stream signed, SignatureSecurityInformation outer)
        {
            trace.TraceEvent(TraceEventType.Information, 0, "Verifying the {0} signature in memory", outer == null ? "inner" : "outer");
            try
            {
                CmsSignedData signedData;
                try
                {
                    signedData = new CmsSignedData(signed);
                    trace.TraceEvent(TraceEventType.Verbose, 0, "Read the cms header");
                }
                catch (Exception e)
                {
                    trace.TraceEvent(TraceEventType.Error, 0, "The message isn't a CMS Signed Data message: {0}", e.Message);
                    throw new InvalidMessageException("The message isn't a triple wrapped message", e);
                }

                if (verifiedContent != null)
                {
                    signedData.SignedContent.Write(verifiedContent);
                    trace.TraceEvent(TraceEventType.Verbose, 0, "Copied the signed data");
                }

                IX509Store certs = signedData.GetCertificates("COLLECTION");
                SignerInformationStore signerInfos = signedData.GetSignerInfos();
                return Verify(signerInfos, certs, outer);
            }
            catch(CmsException cmse)
            {
                throw new InvalidMessageException("The message isn't a triple wrapped message", cmse);
            }
        }
Exemplo n.º 2
0
        private SignatureSecurityInformation VerifyStreaming(Stream verifiedContent, Stream signed, SignatureSecurityInformation outer)
        {
            trace.TraceEvent(TraceEventType.Information, 0, "Verifying the {0} signature streamed", outer == null ? "inner" : "outer");
            try
            {
                CmsSignedDataParser signedData;
                try
                {
                    signedData = new CmsSignedDataParser(signed);
                    trace.TraceEvent(TraceEventType.Verbose, 0, "Read the cms header");
                }
                catch (Exception e)
                {
                    trace.TraceEvent(TraceEventType.Error, 0, "The message isn't a CMS Signed Data message: {0}", e.Message);
                    throw new InvalidMessageException("The message isn't a triple wrapped message", e);
                }

                signedData.GetSignedContent().ContentStream.CopyTo(verifiedContent);
                trace.TraceEvent(TraceEventType.Verbose, 0, "Copied the signed data & calculated the message digest");

                IX509Store certs = signedData.GetCertificates("COLLECTION");
                SignerInformationStore signerInfos = signedData.GetSignerInfos();

                return Verify(signerInfos, certs, outer);
            }
            catch (CmsException cmse)
            {
                if (cmse.Message.Contains("RSAandMGF1 not supported"))
                {
                    throw new NotSupportedException("RSA-PSS not supported with streaming in case of raw signatures");
                }
                throw new InvalidMessageException("The message isn't a triple wrapped message", cmse);
            }
        }
Exemplo n.º 3
0
        private SignatureSecurityInformation Verify(SignerInformationStore signerInfos, IX509Store certs, SignatureSecurityInformation outer)
        {
            trace.TraceEvent(TraceEventType.Information, 0, "Verifying the {0} signature information", outer != null ? "outer" : "inner");
            SignatureSecurityInformation result = new SignatureSecurityInformation();

            //Check if signed (only allow single signatures)
            SignerInformation signerInfo = null;
            IEnumerator iterator = signerInfos.GetSigners().GetEnumerator();
            if (!iterator.MoveNext()) {
                result.securityViolations.Add(SecurityViolation.NotSigned);
                trace.TraceEvent(TraceEventType.Warning, 0, "Although it is a correct CMS file it isn't signed");
                return result;
            }

            signerInfo = (SignerInformation)iterator.Current;

            trace.TraceEvent(TraceEventType.Verbose, 0, "Found signature, with signer ID = issuer {0} and serial number {1}", signerInfo.SignerID.Issuer, signerInfo.SignerID.SerialNumber);
            if (iterator.MoveNext())
            {
                trace.TraceEvent(TraceEventType.Error, 0, "Found more then one signature, this isn't supported (yet)");
                throw new InvalidMessageException("An eHealth compliant message can have only one signer");
            }

            //check if signer used correct digest algorithm
            int i = 0;
            bool found = false;
            StringBuilder algos = new StringBuilder();
            while (!found && i < EteeActiveConfig.Unseal.SignatureAlgorithms.Count)
            {
                Oid algoDigest = EteeActiveConfig.Unseal.SignatureAlgorithms[i].DigestAlgorithm;
                Oid algoEnc = EteeActiveConfig.Unseal.SignatureAlgorithms[i++].EncryptionAlgorithm;
                algos.Append(algoDigest.Value + " (" + algoDigest.FriendlyName + ") + " + algoEnc.Value + " (" + algoEnc.FriendlyName + "), ");
                found = (algoDigest.Value == signerInfo.DigestAlgOid) && (algoEnc.Value == signerInfo.EncryptionAlgOid);
            }
            if (!found)
            {
                result.securityViolations.Add(SecurityViolation.NotAllowedSignatureDigestAlgorithm);
                trace.TraceEvent(TraceEventType.Warning, 0, "The signature digest + encryption algorithm {0} + {1} isn't allowed, only {2} are",
                    signerInfo.DigestAlgOid, signerInfo.EncryptionAlgOid, algos);
            }
            trace.TraceEvent(TraceEventType.Verbose, 0, "Verified the signature digest and encryption algorithm");

            //Find the singing certificate and relevant info
            Org.BouncyCastle.X509.X509Certificate signerCert = null;
            if (certs.GetMatches(null).Count > 0)
            {
                //We got certificates, so lets find the signer
                IEnumerator signerCerts = certs.GetMatches(signerInfo.SignerID).GetEnumerator();

                if (!signerCerts.MoveNext())
                {
                    //found no certificate
                    result.securityViolations.Add(SecurityViolation.NotFoundSigner);
                    trace.TraceEvent(TraceEventType.Warning, 0, "Could not find the signer certificate");
                    return result;
                }

                //Getting the first certificate
                signerCert = (Org.BouncyCastle.X509.X509Certificate)signerCerts.Current;
                trace.TraceEvent(TraceEventType.Verbose, 0, "Found the signer certificate: {0}", signerCert.SubjectDN.ToString());

                //Check if the outer certificate matches the inner certificate
                if (outer != null)
                {
                    Org.BouncyCastle.X509.X509Certificate authCert = DotNetUtilities.FromX509Certificate(outer.Subject.Certificate);
                    trace.TraceEvent(TraceEventType.Verbose, 0, "Comparing The signer certificate {0} ({1}) with the authentication certificate {2} ({3})",
                            signerCert.SubjectDN, signerCert.IssuerDN, authCert.SubjectDN, authCert.IssuerDN);
                    //_safe_ check if the serial numbers of the subject name are equal and they have the same issuer
                    if (!authCert.SubjectDN.GetOidList().Contains(X509Name.SerialNumber)
                        || !signerCert.SubjectDN.GetOidList().Contains(X509Name.SerialNumber)
                        || authCert.SubjectDN.GetValueList(X509Name.SerialNumber).Count != 1
                        || signerCert.SubjectDN.GetValueList(X509Name.SerialNumber).Count != 1
                        || !authCert.SubjectDN.GetValueList(X509Name.SerialNumber)[0].Equals(signerCert.SubjectDN.GetValueList(X509Name.SerialNumber)[0])
                        || !authCert.IssuerDN.Equals(signerCert.IssuerDN))
                    {
                        result.securityViolations.Add(SecurityViolation.SubjectDoesNotMachEnvelopingSubject);
                        trace.TraceEvent(TraceEventType.Warning, 0, "The signer certificate {0} ({1}) does not match the authentication certificate {2} ({3})",
                            signerCert.SubjectDN, signerCert.IssuerDN, authCert.SubjectDN, authCert.IssuerDN);
                    }
                }

                if (signerCerts.MoveNext())
                {
                    //found several certificates...
                    trace.TraceEvent(TraceEventType.Error, 0, "Several certificates correspond to the signer");
                    throw new NotSupportedException("More then one certificate found that corresponds to the sender information in the message, this isn't supported by the library");
                }
            }
            else
            {
                if (outer == null)
                {
                    trace.TraceEvent(TraceEventType.Error, 0, "The outer signature does not contain any certificates");
                    throw new InvalidMessageException("The outer signature is missing certifcates");
                }

                //The subject is the same as the outer
                result.Subject = outer.Subject;
                signerCert = DotNetUtilities.FromX509Certificate(result.Subject.Certificate);
                trace.TraceEvent(TraceEventType.Verbose, 0, "An already validated certificates was provided: {0}", signerCert.SubjectDN.ToString());

                //Additional check, is the authentication certificate also valid for signatures?
                if (!DotNetUtilities.FromX509Certificate(result.Subject.Certificate).GetKeyUsage()[1])
                {
                    result.Subject.securityViolations.Add(CertSecurityViolation.NotValidForUsage);
                    trace.TraceEvent(TraceEventType.Warning, 0, "The key usage did not have the correct usage flag set");
                }
            }

            //verify the signature itself
            result.SignatureValue = signerInfo.GetSignature();
            if (!signerInfo.Verify(signerCert.GetPublicKey()))
            {
                result.securityViolations.Add(SecurityViolation.NotSignatureValid);
                trace.TraceEvent(TraceEventType.Warning, 0, "The signature value was invalid");
            }
            trace.TraceEvent(TraceEventType.Verbose, 0, "Signature value verification finished");

            //Get the signing time
            bool hasSigningTime = false;
            DateTime signingTime = DateTime.UtcNow;
            if (signerInfo != null && signerInfo.SignedAttributes != null)
            {
                Org.BouncyCastle.Asn1.Cms.Attribute time = signerInfo.SignedAttributes[CmsAttributes.SigningTime];
                if (time != null && time.AttrValues.Count > 0)
                {
                    hasSigningTime = true;
                    result.SigningTime = Org.BouncyCastle.Asn1.Cms.Time.GetInstance(time.AttrValues[0]).Date;
                    signingTime = result.SigningTime.Value;
                    trace.TraceEvent(TraceEventType.Verbose, 0, "The CMS message contains a signing time: {0}", result.SigningTime);
                }
            }

            //Validating signature info
            if (this.level == null && result.Subject == null)
            {
                if (outer == null)
                    result.Subject = CertVerifier.VerifyAuth(signerCert, signingTime, certs, null, null, false, false);
                else
                    result.Subject = CertVerifier.VerifySign(signerCert, signingTime, certs, null, null, false, false);
                return result;
            }

            //Get the embedded CRLs and OCSPs
            IList<CertificateList> crls = new List<CertificateList>();
            IList<BasicOcspResponse> ocsps = new List<BasicOcspResponse>();
            if (signerInfo != null && signerInfo.UnsignedAttributes != null)
            {
                trace.TraceEvent(TraceEventType.Verbose, 0, "The CMS message contains unsigned attributes");
                Org.BouncyCastle.Asn1.Cms.Attribute revocationValuesList = signerInfo.UnsignedAttributes[PkcsObjectIdentifiers.IdAAEtsRevocationValues];
                if (revocationValuesList != null && revocationValuesList.AttrValues.Count > 0)
                {
                    trace.TraceEvent(TraceEventType.Verbose, 0, "The CMS message contains Revocation Values");
                    RevocationValues revocationValues = RevocationValues.GetInstance(revocationValuesList.AttrValues[0]);
                    if (revocationValues.GetCrlVals() != null)
                    {
                        crls = new List<CertificateList>(revocationValues.GetCrlVals());
                        trace.TraceEvent(TraceEventType.Verbose, 0, "Found {0} CRL's in the message", crls.Count);
                    }
                    if (revocationValues.GetOcspVals() != null)
                    {
                        ocsps = new List<BasicOcspResponse>(revocationValues.GetOcspVals());
                        trace.TraceEvent(TraceEventType.Verbose, 0, "Found {0} OCSP's in the message", ocsps.Count);
                    }
                }
            }

            //check for a time-stamp, even if not required
            DateTime validationTime;
            TimeStampToken tst = null;
            bool isSigingTimeValidated;
            if (signerInfo != null && signerInfo.UnsignedAttributes != null)
            {
                trace.TraceEvent(TraceEventType.Verbose, 0, "The CMS message contains unsigned attributes");
                Org.BouncyCastle.Asn1.Cms.Attribute tstList = signerInfo.UnsignedAttributes[PkcsObjectIdentifiers.IdAASignatureTimeStampToken];
                if (tstList != null && tstList.AttrValues.Count > 0)
                {
                    trace.TraceEvent(TraceEventType.Verbose, 0, "The CMS message contains the Signature Time Stamp Token: {0}", Convert.ToBase64String(tstList.AttrValues[0].GetEncoded()));
                    tst = tstList.AttrValues[0].GetEncoded().ToTimeStampToken();
                }
            }
            if (tst == null)
            {
                //Retrieve the time-mark if needed by the level only
                if ((this.level & Level.T_Level) == Level.T_Level)
                {
                    if (outer == null)
                    {
                        //we are in the outer signature, so we need a time-mark (or time-stamp, but we checked that already)
                        if (timemarkauthority == null)
                        {
                            trace.TraceEvent(TraceEventType.Error, 0, "Not time-mark authority is provided while there is not embedded time-stamp, the level includes T-Level and it isn't an inner signature");
                            throw new InvalidMessageException("The message does not contain a time-stamp and there is not time-mark authority provided while T-Level is required");
                        }
                        trace.TraceEvent(TraceEventType.Verbose, 0, "Requesting time-mark for message signed by {0}, signed on {1} and with signature value {2}",
                            signerCert.SubjectDN, signingTime, signerInfo.GetSignature());
                        validationTime = timemarkauthority.GetTimemark(new X509Certificate2(signerCert.GetEncoded()), signingTime, signerInfo.GetSignature()).ToUniversalTime();
                        trace.TraceEvent(TraceEventType.Verbose, 0, "The validated time is the return time-mark which is: {0}", validationTime);
                    }
                    else
                    {
                        //we are in the inner signature, we check the signing time against the outer signatures signing time
                        validationTime = outer.SigningTime.Value;
                        trace.TraceEvent(TraceEventType.Verbose, 0, "The validated time is the outer signature singing time which is: {0}", validationTime);
                    }
                }
                else
                {
                    isSigingTimeValidated = false;
                    validationTime = signingTime;
                    trace.TraceEvent(TraceEventType.Verbose, 0, "There is not validated provided, nor is it retrieved because of the level");
                }
            }
            else
            {
                //Check the time-stamp
                if (!tst.IsMatch(new MemoryStream(signerInfo.GetSignature())))
                {
                    trace.TraceEvent(TraceEventType.Warning, 0, "The time-stamp does not match the message");
                    result.securityViolations.Add(SecurityViolation.InvalidTimestamp);
                }

                Timestamp stamp;
                if ((this.level & Level.A_level) == Level.A_level)
                {
                    //TODO::follow the chain of A-timestamps until the root (now we assume the signature time-stamp is the root)
                    trace.TraceEvent(TraceEventType.Verbose, 0, "Validating the time-stamp against the current time for arbitration reasons");
                    stamp = tst.Validate(ref crls, ref ocsps, null);
                }
                else {
                    trace.TraceEvent(TraceEventType.Verbose, 0, "Validating the time-stamp against the time-stamp time since no arbitration is needed");
                    stamp = tst.Validate(ref crls, ref ocsps);
                }
                result.TimestampRenewalTime = stamp.RenewalTime;
                trace.TraceEvent(TraceEventType.Verbose, 0, "The time-stamp must be renewed on {0}", result.TimestampRenewalTime);

                //we get the time from the time-stamp
                validationTime = stamp.Time;
                trace.TraceEvent(TraceEventType.Verbose, 0, "The validated time is the time-stamp time which is on {0}", validationTime);
                if (!hasSigningTime)
                {
                    trace.TraceEvent(TraceEventType.Information, 0, "Implicit signing time {0} is replaced with time-stamp time {1}", signingTime, tst.TimeStampInfo.GenTime);
                    signingTime = stamp.Time;
                }

                if (stamp.TimestampStatus.Count(x => x.Status != X509ChainStatusFlags.NoError) > 0) {
                    trace.TraceEvent(TraceEventType.Warning, 0, "The time-stamp is invalid with {0} errors, including {1}: {2}",
                        stamp.TimestampStatus.Count, stamp.TimestampStatus[0].Status, stamp.TimestampStatus[0].StatusInformation);
                    isSigingTimeValidated = false;
                    result.securityViolations.Add(SecurityViolation.InvalidTimestamp);
                }
            }

            //lest check if the signing time is in line with the validation time (obtained from time-mark, outer signature or time-stamp)
            if (validationTime > (signingTime + EteeActiveConfig.ClockSkewness + Settings.Default.TimestampGracePeriod)
                || validationTime < (signingTime - EteeActiveConfig.ClockSkewness))
            {
                trace.TraceEvent(TraceEventType.Warning, 0, "The validated time {0} is not in line with the signing time {1} with a grace period of {2}",
                    validationTime, signingTime, Settings.Default.TimestampGracePeriod);
                isSigingTimeValidated = false;
                result.securityViolations.Add(SecurityViolation.SealingTimeInvalid);
            }
            else
            {
                trace.TraceEvent(TraceEventType.Verbose, 0, "The validated time {0} is in line with the signing time {1}", validationTime, signingTime);
                isSigingTimeValidated = true;
            }

            //If the subject is already provided, so we don't have to do the next check
            if (result.Subject != null) return result;

            //check the status on the available info, for unseal it does not matter if it is B-Level or LT-Level.
            if (outer == null)
                result.Subject = CertVerifier.VerifyAuth(signerCert, signingTime, certs, crls, ocsps, true, isSigingTimeValidated);
            else
                result.Subject = CertVerifier.VerifySign(signerCert, signingTime, certs, crls, ocsps, true, isSigingTimeValidated);

            return result;
        }