using System; using System.Collections; using System.Collections.Generic; using Sleis.Infrastructure; using Sleis.Models; using Sleis.Models.CustomFields; using Sleis.Utility; using Sleis.Validation.Spring; using Sleis.ViewModels; using Spring.Expressions; using Spring.Objects; using Sleis.Pdf; using Sleis.Data; using NHibernate; using NHibernate.Linq; using System.IO; using Org.BouncyCastle.X509; using iTextSharp.text.pdf; using System.Security.Cryptography.X509Certificates; using System.Web; namespace Sleis.Service { public class DocumentService : BaseService { public UserData UserData { get; set; } public IReportService ReportService { get; set; } public PdfMaker PdfMaker { get; set; } public SubmissionDocumentData SubmissionDocumentData { get; set; } public SubmissionCorData SubmissionCorData { get; set; } public RepudiateRequestData RepudiateRequestData { get; set; } public string RepudiationRequestEmailTemplate { get; set; } public string ReportRepudiatedEmailTemplate { get; set; } public new void Init() { base.Init(); ArgumentValidationUtility.ThrowOnNull(SubmissionDocumentData, "SubmissionDocumentData"); ArgumentValidationUtility.ThrowOnNull(PdfMaker, "PdfMaker"); } public List GetSubmissionDocumentHistory(int reportId) { List list = SubmissionDocumentData.GetSubmissionDocumentHistory(reportId); foreach (SubmissionDocumentModel document in list) { document.SubmittingUser = UserData.GetById(document.SubmittinguserId); document.SubmissionCor = SubmissionCorData.GetSingle(x => x.Submission.Id == document.Id); if (document.SubmissionCor != null && document.SubmissionCor.SignedContent != null) { VerifyPdfSignature(document.SubmissionCor); } } return list; } /// /// Generates Pdf /// Saves Pdf to temp /// Saves Pdf to the Db /// /// /// /// public SubmissionDocumentModel Make(int facilityId, int reportId, UserModel user) { Log.DebugFormat("Make(facilityId: {0} reportId: {1}", facilityId, reportId); ArgumentValidationUtility.ThrowOnNull(user, "user"); ReportValidationView subject = ReportService.GetCompleteReport(facilityId, reportId); if (subject == null) { throw new ArgumentNullException("subject"); } //Get PDF content byte[] content = PdfMaker.Make(new ObjectWrapper(subject)); //Create a document SubmissionDocumentModel doc = new SubmissionDocumentModel(); doc.ReportId = subject.FacilityIdentity.Report.Id; doc.UnsignedContent = content; //TODO: externalize doc.Name = String.Format("{0}_{1}_Emissions_Report.pdf", subject.FacilityIdentity.Report.Number, ((FacilityModel)subject.FacilityIdentity.Facility).FacilityIdentifier); doc.ContenType = "application/pdf"; doc.GeneratedOn = DateTime.Now; doc.Number = GenerateSubmissionId(subject); doc.Status = SubmissionDocumentStatus.Initiated; doc.SubmittinguserId = user.Id; //Save the document return SubmissionDocumentData.Create(doc); } public SubmissionDocumentModel Sign(int documentId, UserModel user, Dictionary args) { Log.DebugFormat("Sign(documentId: {0}, user: {1}", documentId, user); ArgumentValidationUtility.ThrowOnNull(user, "user"); ArgumentValidationUtility.ThrowOnNull(args, "args"); SubmissionDocumentModel doc = SubmissionDocumentData.GetById(documentId); doc.SignedOn = DateTime.Now; doc.Status = SubmissionDocumentStatus.Submitted; doc.SubmittinguserId = user.Id; //Now that we have everything lined up, lets commit the whole thing using (ISession session = SubmissionDocumentData.GetSession()) { using (ITransaction txn = session.BeginTransaction()) { SubmissionDocumentData.Create(doc, session); SubmissionCorData.Create(new SubmissionCor(doc, PdfMaker.Sign(doc.UnsignedContent, args), DateTime.Now, user), session); ReportService.SetReportAsSubmitted(doc.ReportId, doc.SignedOn.Value, session); txn.Commit(); } } return doc; } public bool VerifyPdfSignature(SubmissionCor submissionCor) { Log.Debug("Verifying Signature - Submission Cor - Id :" + submissionCor.Id); submissionCor.SignatureValid = VerifyPdfSignature(submissionCor.SignedContent); return submissionCor.SignatureValid; } /// /// Verifies the signature of a prevously signed PDF document using the specified public key /// /// a Previously signed pdf document /// Public key to be used to verify the signature in .cer format public bool VerifyPdfSignature(byte[] pdfFile) { try { var parser = new X509CertificateParser(); var certificate = PdfMaker.SignatureMaker.Cert.Chain; //var certificate = parser.ReadCertificate(publicKeyStream); //publicKeyStream.Dispose(); X509Certificate2 signature = null;//Your X509Certificate2 certificate instance here. //var akp = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(signature.PrivateKey).Private; //IExternalSignature es = new PrivateKeySignature(akp, "SHA-256"); PdfReader reader = new PdfReader(pdfFile); AcroFields af = reader.AcroFields; var names = af.GetSignatureNames(); if (names.Count == 0) { throw new InvalidOperationException("No Signature present in pdf file."); } foreach (string name in names) { if (!af.SignatureCoversWholeDocument(name)) { throw new InvalidOperationException(string.Format("The signature: {0} does not covers the whole document.", name)); } PdfPKCS7 pk = af.VerifySignature(name); var cal = pk.SignDate; var pkc = pk.Certificates; if (!pk.Verify()) { throw new InvalidOperationException("The signature could not be verified."); } //if (!pk.VerifyTimestampImprint()) //{ // throw new InvalidOperationException("The signature timestamp could not be verified."); //} //Object[] fails = CertificateVerification.VerifyCertificates(pkc, new X509Certificate[] { certificate }, null, cal); Object[] fails = PdfPKCS7.VerifyCertificates(pkc, new List(certificate), null, cal); if (fails != null) { throw new InvalidOperationException("The file is not signed using the specified key-pair."); } } } catch (Exception err) { Log.Error(err); return false; } return true; } public SubmissionDocumentModel View(int documentId) { Log.DebugFormat("View(facilityId: {0} reportId: {1}", documentId); SubmissionDocumentModel doc = SubmissionDocumentData.GetById(documentId); doc.SubmissionCor = SubmissionCorData.GetSingle(x => x.Submission.Id == documentId); return doc; } //TODO: Figure out something more consistent private string GenerateSubmissionId(ReportValidationView model) { ArgumentValidationUtility.ThrowOnNull(model, "model"); return String.Format("S{0}-F{1}-R{2}", DateTime.Now.ToString("yyyyMMddHHmmss"), ((FacilityModel)model.FacilityIdentity.Facility).FacilityIdentifier, model.FacilityIdentity.Report.Number) .Replace(" ", String.Empty); } public void SetStatus(int Id, SubmissionDocumentStatus submissionDocumentStatus) { SubmissionDocumentModel doc = SubmissionDocumentData.GetById(Id); doc.Status = submissionDocumentStatus; SubmissionDocumentData.Update(doc); } public void SaveRepudiateRequest(RepudiateRequestModel model) { using (ISession session = RepudiateRequestData.GetSession()) { using (ITransaction tran = session.BeginTransaction()) { SubmissionDocumentModel submission = SubmissionDocumentData.GetById(model.SubmissionId); var emailTo = SessionUtility.CurrentUser.Email; if (submission.SubmissionCor == null) submission.SubmissionCor = SubmissionCorData.GetSingle(x => x.Submission.Id == model.SubmissionId); //add COR Signator to email if available. if (submission.SubmissionCor != null) { submission.SubmissionCor.CreatedBy = UserData.GetById(submission.SubmissionCor.CreatedBy.Id); if(emailTo != submission.SubmissionCor.CreatedBy.Email) emailTo += "," + submission.SubmissionCor.CreatedBy.Email; } RepudiateRequestData.Create(model, session); Dictionary emailArgs = new Dictionary(); emailArgs.Add("Name", SessionUtility.CurrentUser.FullName); emailArgs.Add("ReportYear", model.Report.Number); emailArgs.Add("ReportSubmissionDate", submission.GeneratedOn); emailArgs.Add("SubmissionConfirmationNumber", submission.Number); //using (MemoryStream doc = new MemoryStream(submission.UnsignedContent)) //{ //Send email EmailUtility.SendArgs(emailTo, RepudiationRequestEmailTemplate, emailArgs); tran.Commit(); } } } public void RepudiateSubmissionByAgency(int submissionDocumentId, UserModel user) { var submission = SubmissionDocumentData.GetById(submissionDocumentId); var report = SubmissionDocumentData.GetSingle(x => x.Id == submission.ReportId); //update submission status SetStatus(submissionDocumentId, SubmissionDocumentStatus.Repudiated); var emailTo = SessionUtility.CurrentUser.Email; if (submission.SubmissionCor == null) submission.SubmissionCor = SubmissionCorData.GetSingle(x => x.Submission.Id == submission.Id); //add COR Signator to email if available. if (submission.SubmissionCor != null) { submission.SubmissionCor.CreatedBy = UserData.GetById(submission.SubmissionCor.CreatedBy.Id); if (emailTo != submission.SubmissionCor.CreatedBy.Email) emailTo += "," + submission.SubmissionCor.CreatedBy.Email; } Dictionary emailArgs = new Dictionary(); emailArgs.Add("Name", SessionUtility.CurrentUser.FullName); emailArgs.Add("ReportYear", report.Number); emailArgs.Add("ReportSubmissionDate", submission.GeneratedOn); emailArgs.Add("SubmissionConfirmationNumber", submission.Number); //Send email EmailUtility.SendArgs(emailTo, ReportRepudiatedEmailTemplate, emailArgs); Audit(new AppEventModel(EventType.Audit, SessionUtility.CurrentUser.Email, "Submission Repudiated. Number: " + submission.Number, HttpContext.Current.Request.UserHostAddress)); } } }