public IActionResult Status(string key) { ApiActivity ac = new ApiActivity(); // new entry -> Status requested ac.Operation = ApiActivity.ApiOperation.Status; ac.Status = ApiActivity.ApiStatus.Ready; ac.Message = $"Status requested for key {key}, unique Key for this status request {ac.UniqueKey}"; _asvc.addUpdateApiActivity(ac); _l.Debug(ac.Message); //check if malicious content, GUID == 36 characters if (key.Length != 36) { ac.Message = $"key provided is not a valid key!"; ac.Status = ApiActivity.ApiStatus.Error; _asvc.addUpdateApiActivity(ac); _l.Error(ac.Message); return(Content(ac.getWebresult())); } //check if ID exixts / activity present // get DB entry for updating ApiActivity requestedStatus = _asvc.getActivityByUniqueKey(key); if (requestedStatus == null) { ac.Status = ApiActivity.ApiStatus.Error; ac.Message = $"Key {key} is invalid!"; _asvc.addUpdateApiActivity(ac); _l.Error(ac.Message); return(Content(ac.getWebresult())); } else { ac.Message = $"file found: returning status for key {key}"; ac.Status = ApiActivity.ApiStatus.Ready; _asvc.addUpdateApiActivity(ac); return(Content(requestedStatus.getWebresult())); } }
public IActionResult DownloadFile(string key) { ApiActivity ac = new ApiActivity(); ac.Operation = ApiActivity.ApiOperation.Download; ac.Status = ApiActivity.ApiStatus.Ready; ac.Message = $"Download of file {key} requested. Checking for file..."; _asvc.addUpdateApiActivity(ac); //check if malicious content, GUID == 36 characters if (key.Length != 36) { ac.Message = $"key provided is not a valid key!"; ac.Status = ApiActivity.ApiStatus.Error; _asvc.addUpdateApiActivity(ac); _l.Error(ac.Message); return(Content(ac.getWebresult())); } //check if file ID exixts / file present try { var activity = _asvc.getActivityByUniqueKey(key); var systemFileName = activity.SystemOfficeFilename; //if file not ready (still queued or signing) no download! if (activity.Status != ApiActivity.ApiStatus.Ready) { ac.Message = $"File Status of {key} is {activity.Status}. not ready for download yet!"; ac.Status = ApiActivity.ApiStatus.Error; _asvc.addUpdateApiActivity(ac); _l.Error(ac.Message); return(Content(ac.getWebresult())); } //check if file exists var storagePath = GHelper.getOfficeFilesSystemDir(_webHostEnv, _conf); var fullSystemFileName = Path.Combine(storagePath, systemFileName); if (System.IO.File.Exists(fullSystemFileName)) { _l.Debug($"System file {fullSystemFileName} found. Starting sending file..."); // send file for download var content = new FileStream(fullSystemFileName, FileMode.Open); var contentType = "APPLICATION/octet-stream"; // inject _signed.* in filename var fileNameSigned = Path.GetFileNameWithoutExtension(activity.UserOfficeFilename) + "_signed" + Path.GetExtension(activity.UserOfficeFilename); _l.Debug($"file {fullSystemFileName} found. Sending as {fileNameSigned}"); ac.Status = ApiActivity.ApiStatus.Ready; ac.Message = $"File {fileNameSigned} sent for download successfully!"; _asvc.addUpdateApiActivity(ac); return(File(content, contentType, fileNameSigned)); } else { //requested file in DB, but not found in filesystem ac.Status = ApiActivity.ApiStatus.Error; ac.Message = $"File for key {key} not found!"; _asvc.addUpdateApiActivity(ac); _l.Error(ac.Message); return(Content(ac.getWebresult())); } } catch (Exception) { ac.Status = ApiActivity.ApiStatus.Error; ac.Message = $"FileKey {key} is invalid!"; _asvc.addUpdateApiActivity(ac); _l.Error(ac.Message); return(Content(ac.getWebresult())); } }
public IActionResult RequestSigning(IFormFile officeFile, [FromForm] bool analyse, string profileName) { //prepare ac (used for logging / error return as well) ApiActivity ac = new ApiActivity(); ac.Operation = ApiActivity.ApiOperation.RequestSigning; ac.ClientIPAddress = HttpContext.Connection.RemoteIpAddress.ToString(); ac.StatusUrl = GHelper.generateUrl(GHelper.UrlType.StatusUrl, ac, _httpctx); ac.DownloadUrl = GHelper.generateUrl(GHelper.UrlType.DownloadUrl, ac, _httpctx); if (officeFile == null) { ac.Status = ApiActivity.ApiStatus.Error; ac.Message = "Office File not submitted. Required for signing!"; _l.Error(ac.Message); _asvc.addUpdateApiActivity(ac); return(Content(ac.getWebresult())); } ac.UserOfficeFilename = officeFile.FileName; ac.Message = $"Starting request Signing with {officeFile.FileName} and profile ID {profileName}..."; //--- check if valid profile name was provided //check if secrets file present string secretFilename = "secrets.json"; if (!System.IO.File.Exists(secretFilename)) { ac.Status = ApiActivity.ApiStatus.Error; ac.Message = "secrets file not found for reading profiles"; _l.Error(ac.Message); _asvc.addUpdateApiActivity(ac); return(Content(ac.getWebresult())); } //read secrets config JObject jsonConfig = JObject.Parse(System.IO.File.ReadAllText(secretFilename)); var profileCertFile = (string)jsonConfig["SigningProfiles"][profileName]["CertFile"]; var profileCertPw = (string)jsonConfig["SigningProfiles"][profileName]["CertPw"]; if (profileCertFile == null || profileCertPw == null) { ac.Status = ApiActivity.ApiStatus.Error; ac.Message = $"No certfile or certPW found for Profilename {profileName}"; _l.Error(ac.Message); _asvc.addUpdateApiActivity(ac); return(Content(ac.getWebresult())); } ac.UserCertFilename = profileCertFile; //check if cert file from settings is really on filesystem var systemCertFileName = Path.Combine(GHelper.getCertFilesSystemDir(_webHostEnv, _conf), ac.UserCertFilename); if (!System.IO.File.Exists(systemCertFileName)) { ac.Status = ApiActivity.ApiStatus.Error; ac.Message = $"certfile {ac.SystemCertFilename} not found for Profilename {profileName}"; _l.Error(ac.Message); _asvc.addUpdateApiActivity(ac); return(Content(ac.getWebresult())); } ac.SystemCertFilename = systemCertFileName; // Save certPW encyrpted in AC, to be decrypted by signer service later. // todo: better PW handling -> was already in cleartext in secrets file _l.Debug($"Provided cert PW = \"{profileCertPw}\""); // Read secrets JObject secretsConfig = JObject.Parse(System.IO.File.ReadAllText(@"secrets.json")); //secrets.json file not checked in. .gitignore var aesKey = (string)secretsConfig["aesKey"]; var encryptedCertPw = EncryptProvider.AESEncrypt(profileCertPw, aesKey); // save pw encrypted in DB ac.EncCertPw = encryptedCertPw; //------- CHECKS //check for valid file extension string officeFileExt = Path.GetExtension(officeFile.FileName.ToLowerInvariant()); if (!GHelper.fileHasAllowedExtension(GHelper.ExtensionType.OfficeFile, officeFileExt)) { ac.Status = ApiActivity.ApiStatus.Error; ac.Message = $"Office File extension {officeFileExt} not valid!"; _l.Error(ac.Message); _asvc.addUpdateApiActivity(ac); return(Content(ac.getWebresult())); } // check magic number file types if (!(GHelper.fileHasValidFormat(GHelper.ExtensionType.OfficeFile, officeFile.OpenReadStream()))) { ac.Status = ApiActivity.ApiStatus.Error; ac.Message = $"Office File {officeFile.FileName} not a valid office file!"; _l.Error(ac.Message); _asvc.addUpdateApiActivity(ac); return(Content(ac.getWebresult())); } // SAVE FILES //save office file with unique filename, not enumerable string uniFilenameOfficeFile = GHelper.createUniqueFileName(officeFile.FileName); string systemFolderOfficeFile = GHelper.getOfficeFilesSystemDir(_webHostEnv, _conf); string systemFileNameOfficeFile = Path.Combine(systemFolderOfficeFile, uniFilenameOfficeFile); // create dir if not exist System.IO.Directory.CreateDirectory(systemFolderOfficeFile); _l.Debug($"Saving Office file to {systemFolderOfficeFile}"); using (var fileStream = new FileStream(systemFileNameOfficeFile, FileMode.Create)) { officeFile.CopyTo(fileStream); } ac.SystemOfficeFilename = systemFileNameOfficeFile; if (analyse) { // Queue for ANALYSING ac.Status = ApiActivity.ApiStatus.QueuedAnalysis; ac.Message = "File queued for analysis"; _asvc.addUpdateApiActivity(ac); _l.Debug("Analysis requested, queuing for analysis..."); } else { // Queue for SIGNING ac.Status = ApiActivity.ApiStatus.QueuedSigning; ac.Message = "File queued for signing"; _asvc.addUpdateApiActivity(ac); _l.Debug("NO analysis requested, queuing for signing at once..."); } // RETURN STATUS _l.Debug($"Returning Queued API Status for Key {ac.UniqueKey}"); return(Content(ac.getWebresult())); }
public IActionResult CheckOfficeFile(IFormFile officeFile) { ApiActivity ac = new ApiActivity(); ac.ClientIPAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString(); ac.Operation = ApiActivity.ApiOperation.Verify; ac.Message = $"Check Office File started"; ac.Status = ApiActivity.ApiStatus.Verifying; ac.StatusUrl = GHelper.generateUrl(GHelper.UrlType.StatusUrl, ac, _httpctx); ac.DownloadUrl = GHelper.generateUrl(GHelper.UrlType.DownloadUrl, ac, _httpctx); _asvc.addUpdateApiActivity(ac); if (officeFile != null) { ac.UserOfficeFilename = officeFile.FileName; //check valid file extension _l.Debug("Checking for valid file extensions..."); string officeFileExt = Path.GetExtension(officeFile.FileName.ToLowerInvariant()); if (!GHelper.fileHasAllowedExtension(GHelper.ExtensionType.OfficeFile, officeFileExt)) { ac.Message = $"Office File extension {officeFileExt} not valid!"; ac.Status = ApiActivity.ApiStatus.Error; _asvc.addUpdateApiActivity(ac); _l.Error(ac.Message); return(Content(ac.getWebresult())); } // check magic number file types _l.Debug("Checking for magic number of file..."); if (!(GHelper.fileHasValidFormat(GHelper.ExtensionType.OfficeFile, officeFile.OpenReadStream()))) { ac.Message = $"Office File {officeFile.FileName} not a valid office file!"; ac.Status = ApiActivity.ApiStatus.Error; _asvc.addUpdateApiActivity(ac); _l.Error(ac.Message); return(Content(ac.getWebresult())); } //save office file string uniFilenameOfficeFile = GHelper.createUniqueFileName(officeFile.FileName); string systemFolderOfficeFile = GHelper.getOfficeFilesSystemDir(_webHostEnv, _conf); string systemFileNameOfficeFile = Path.Combine(systemFolderOfficeFile, uniFilenameOfficeFile); ac.SystemOfficeFilename = systemFileNameOfficeFile; // create dir if not exist System.IO.Directory.CreateDirectory(systemFolderOfficeFile); _l.Debug($"Saving file to {systemFileNameOfficeFile}"); using (var fileStream = new FileStream(systemFileNameOfficeFile, FileMode.Create)) { officeFile.CopyTo(fileStream); } //verify file // prepare run ProcessStartInfo psi = new ProcessStartInfo(); psi.FileName = _webHostEnv.ContentRootPath + @"\lib\signtool.exe"; psi.RedirectStandardError = true; psi.RedirectStandardOutput = true; psi.UseShellExecute = false; //use quoted filename otherwise systempath is revealed in error message!! psi.Arguments = $"verify /pa /debug /v \"{systemFileNameOfficeFile}\""; _l.Debug($"Executing {psi.FileName} {psi.Arguments}..."); // execute run StringBuilder stdOut = new StringBuilder(); StringBuilder stdErr = new StringBuilder(); Process p = new Process(); p.StartInfo = psi; p.Start(); while (!p.StandardOutput.EndOfStream) { stdOut.AppendLine(p.StandardOutput.ReadLine()); } while (!p.StandardError.EndOfStream) { stdErr.AppendLine(p.StandardError.ReadLine()); } p.WaitForExit(); _l.Debug("Process exited. Parsing..."); // parse result ac = SignToolOutputParser.parseSignToolOutput(SignToolOutputParser.SignToolOperation.Verify, ac, stdOut.ToString(), stdErr.ToString()); _l.Debug($"Parsed result = {ac.ToString()}"); _l.Debug($"Deleting file {systemFileNameOfficeFile}"); // delete after verify System.IO.File.Delete(Path.Combine(systemFileNameOfficeFile)); _asvc.addUpdateApiActivity(ac); return(Content(ac.getWebresult())); } else { var message = ("No Files submitted for Verifying!"); _l.Warning(message); ac.Operation = ApiActivity.ApiOperation.Verify; ac.Status = ApiActivity.ApiStatus.Error; ac.Message = message; return(Content(ac.getWebresult())); } }
public IActionResult RequestSigning(IFormFile officeFile, IFormFile certFile, [FromForm] string certPw, [FromForm] bool analyse) { //prepare ac (used for logging / error return as well) ApiActivity ac = new ApiActivity(); ac.Operation = ApiActivity.ApiOperation.RequestSigning; ac.ClientIPAddress = HttpContext.Connection.RemoteIpAddress.ToString(); ac.StatusUrl = GHelper.generateUrl(GHelper.UrlType.StatusUrl, ac, _httpctx); ac.DownloadUrl = GHelper.generateUrl(GHelper.UrlType.DownloadUrl, ac, _httpctx); if (officeFile != null && certFile != null) { ac.UserOfficeFilename = officeFile.FileName; ac.UserCertFilename = certFile.FileName; ac.Message = $"Starting request Signing with {officeFile.FileName} and cert file {certFile.FileName}..."; _asvc.addUpdateApiActivity(ac); if (certPw != null) { _l.Debug($"Provided cert PW = \"{certPw}\""); // Read secrets JObject secretsConfig = JObject.Parse(System.IO.File.ReadAllText(@"secrets.json")); //secrets.json file not checked in. .gitignore var aesKey = (string)secretsConfig["aesKey"]; var encryptedCertPw = EncryptProvider.AESEncrypt(certPw, aesKey); // save pw encrypted in DB ac.EncCertPw = encryptedCertPw; } else { _l.Debug($"No cert PW provided!"); } //------- CHECKS //check for valid file extension string officeFileExt = Path.GetExtension(officeFile.FileName.ToLowerInvariant()); if (!GHelper.fileHasAllowedExtension(GHelper.ExtensionType.OfficeFile, officeFileExt)) { ac.Status = ApiActivity.ApiStatus.Error; ac.Message = $"Office File extension {officeFileExt} not valid!"; _l.Error(ac.Message); _asvc.addUpdateApiActivity(ac); return(Content(ac.getWebresult())); } string certFileExt = Path.GetExtension(certFile.FileName.ToLowerInvariant()); if (!GHelper.fileHasAllowedExtension(GHelper.ExtensionType.CertFile, certFileExt)) { ac.Status = ApiActivity.ApiStatus.Error; ac.Message = $"Certificate File extension {certFileExt} not valid!"; _l.Error(ac.Message); _asvc.addUpdateApiActivity(ac); return(Content(ac.getWebresult())); } // check magic number file types if (!(GHelper.fileHasValidFormat(GHelper.ExtensionType.OfficeFile, officeFile.OpenReadStream()))) { ac.Status = ApiActivity.ApiStatus.Error; ac.Message = $"Office File {officeFile.FileName} not a valid office file!"; _l.Error(ac.Message); _asvc.addUpdateApiActivity(ac); return(Content(ac.getWebresult())); } if (!(GHelper.fileHasValidFormat(GHelper.ExtensionType.CertFile, certFile.OpenReadStream()))) { ac.Status = ApiActivity.ApiStatus.Error; ac.Message = $"Cert File {certFile.FileName} not a valid cert file!"; _l.Error(ac.Message); _asvc.addUpdateApiActivity(ac); return(Content(ac.getWebresult())); } // check PW field int maxPwLength = Int32.Parse(_conf.GetSection("Security")["MaxCertPwLength"]); if (certPw != null && certPw.Length > maxPwLength) { ac.Status = ApiActivity.ApiStatus.Error; ac.Message = $"Cert Pw exceeding max Length: {maxPwLength}!"; _l.Error(ac.Message); _asvc.addUpdateApiActivity(ac); return(Content(ac.getWebresult())); } // SAVE FILES //save office file with unique filename, not enumerable string uniFilenameOfficeFile = GHelper.createUniqueFileName(officeFile.FileName); string systemFolderOfficeFile = GHelper.getOfficeFilesSystemDir(_webHostEnv, _conf); string systemFileNameOfficeFile = Path.Combine(systemFolderOfficeFile, uniFilenameOfficeFile); ac.SystemOfficeFilename = systemFileNameOfficeFile; // create dir if not exist System.IO.Directory.CreateDirectory(systemFolderOfficeFile); _l.Debug($"Saving Office file to {systemFolderOfficeFile}"); using (var fileStream = new FileStream(systemFileNameOfficeFile, FileMode.Create)) { officeFile.CopyTo(fileStream); } //save cert file with unique filename, not enumerable string uniFilenameCertFile = GHelper.createUniqueFileName(certFile.FileName); string systemFolderCertFile = GHelper.getCertFilesSystemDir(_webHostEnv, _conf); string systemFileNameCertFile = Path.Combine(systemFolderCertFile, uniFilenameCertFile); systemFileNameCertFile = systemFileNameCertFile.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); ac.SystemCertFilename = systemFileNameCertFile; // create dir if not exist System.IO.Directory.CreateDirectory(systemFolderCertFile); _l.Debug($"Saving cert file to {systemFileNameCertFile}"); using (var fileStream = new FileStream(systemFileNameCertFile, FileMode.Create)) { certFile.CopyTo(fileStream); } if (analyse) { // Queue foor ANALYSING ac.Status = ApiActivity.ApiStatus.QueuedAnalysis; ac.Message = "File queued for analysis"; _asvc.addUpdateApiActivity(ac); _l.Debug("Analysis requested, queuing for analysis..."); } else { // Queue for SIGNING ac.Status = ApiActivity.ApiStatus.QueuedSigning; ac.Message = "File queued for signing"; _asvc.addUpdateApiActivity(ac); _l.Debug("NO analysis requested, queuing for signing at once..."); } // RETURN STATUS PAGE _l.Debug($"Returning Queued API Status for Key {ac.UniqueKey}"); return(Content(ac.getWebresult())); } else { ac.Status = ApiActivity.ApiStatus.Error; ac.Message = "Office File or Cert File not submitted. Both required for signing!"; _l.Error(ac.Message); _asvc.addUpdateApiActivity(ac); return(Content(ac.getWebresult())); } }