示例#1
0
        public ApiActivity getActivityByUniqueKey(string key)
        {
            ApiActivity ac = null;

            ac = _db.ApiActivity.FirstOrDefault(item => item.UniqueKey == key);
            return(ac);
        }
示例#2
0
        private bool signFile(ApiActivity ac)
        {
            // ---- SIGN FILE

            // prepare run
            ProcessStartInfo psi = new ProcessStartInfo();

            psi.FileName = Directory.GetCurrentDirectory() + @"\lib\signtool.exe";
            psi.RedirectStandardError  = true;
            psi.RedirectStandardOutput = true;
            psi.UseShellExecute        = false;
            if (ac.EncCertPw != null && ac.EncCertPw.Length > 0)
            {
                // Read secrets --> decrpyt certPW
                JObject secretsConfig = JObject.Parse(System.IO.File.ReadAllText(@"secrets.json")); //secrets.json file not checked in. .gitignore
                var     aesKey        = (string)secretsConfig["aesKey"];
                var     certPw        = EncryptProvider.AESDecrypt(ac.EncCertPw, aesKey);
                psi.Arguments = $"sign /debug /v /fd sha256 /f \"{ac.SystemCertFilename}\" /p {certPw} \"{ac.SystemOfficeFilename}\"";
            }
            else
            {
                psi.Arguments = $"sign /debug /v /fd sha256 /f \"{ac.SystemCertFilename}\" \"{ac.SystemOfficeFilename}\"";
            }
            _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, prepare return json
            ac = SignToolOutputParser.parseSignToolOutput(SignToolOutputParser.SignToolOperation.Sign, ac, stdOut.ToString(), stdErr.ToString());
            if (ac.Status == ApiActivity.ApiStatus.Ready)
            {
                return(true);
            }
            else
            {
                return(false);
            }
        }
        public async Task Transform_ALL()
        {
            var            basePath       = Path.Combine(Path.GetTempPath(), _context.DynamicContext.Manifest.Id);
            CommonActivity commonActivity = new CommonActivity("CommonActivity", basePath);
            await commonActivity.Initializing(_context);

            await commonActivity.Writing();

            Assert.NotNull(commonActivity);

            ApiActivity apiActivity = new ApiActivity("ApiActivity", basePath);
            await apiActivity.Initializing(_context);

            await apiActivity.Writing();

            Assert.NotNull(apiActivity);

            LayoutActivity layoutActivity = new LayoutActivity("LayoutActivity", basePath);
            await layoutActivity.Initializing(_context);

            await layoutActivity.Writing();

            Assert.NotNull(layoutActivity);

            DataModelActivity dataModelActivity = new DataModelActivity("DataModelActivity", basePath);
            await dataModelActivity.Initializing(_context);

            await dataModelActivity.Writing();

            Assert.NotNull(dataModelActivity);

            ViewModelActivity viewModelActivity = new ViewModelActivity("ViewModelActivity", basePath);
            await viewModelActivity.Initializing(_context);

            await viewModelActivity.Writing();

            Assert.NotNull(viewModelActivity);

            LanguageActivity languageActivity = new LanguageActivity("LanguageActivity", basePath);
            await languageActivity.Initializing(_context);

            await languageActivity.Writing();

            Assert.NotNull(languageActivity);

            UnitTestsActivity unitTestsActivity = new UnitTestsActivity("UnitTestsActivity", basePath);
            await unitTestsActivity.Initializing(_context);

            await unitTestsActivity.Writing();

            Assert.NotNull(unitTestsActivity);
        }
示例#4
0
        public IHttpActionResult MyActivity(string token, byte limit = 10)
        {
            if (auth.IsNotValid(token))
            {
                return(base.StatusCode(HttpStatusCode.Unauthorized));
            }

            var results = this.activityCore.UserSearch(auth.Device.UserIdentifier, limit);

            if (null != results && 0 < results.Count())
            {
                var activities = results.Select(r => ApiActivity.Map(r));
                return(this.Ok <IEnumerable <ApiActivity> >(activities));
            }
            else
            {
                return(this.BadRequest("found nothing"));
            }
        }
示例#5
0
        public static string generateUrl(UrlType urltype, ApiActivity ac, IHttpContextAccessor httpctx)
        {
            var scheme = httpctx.HttpContext.Request.Scheme;
            var host   = httpctx.HttpContext.Request.Host;
            //var apiPath = httpctx.HttpContext.Request.Path.Value.Substring(0, httpctx.HttpContext.Request.Path.Value.LastIndexOf("/"));
            var apiPath = "/api/Signer";

            if (urltype == UrlType.DownloadUrl)
            {
                var downloadApiPath = $"{apiPath}/DownloadFile";
                return($"{scheme}://{host}{downloadApiPath}/{ac.UniqueKey}");
            }
            if (urltype == UrlType.StatusUrl)
            {
                var statusApiPath = $"{apiPath}/Status";
                return($"{scheme}://{host}{statusApiPath}/{ac.UniqueKey}");
            }
            return("URL could not be generated");
        }
示例#6
0
        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()));
            }
        }
示例#7
0
        /// <summary>
        /// Adds ApiActivity Object to DB or updates existing object.
        /// </summary>
        /// <param name="ac">ApiActivity to add or update</param>
        public void addUpdateApiActivity(ApiActivity ac)
        {
            var entity = _db.ApiActivity.FirstOrDefault(item => item.Key == ac.Key);

            // entity already exists -> update
            if (entity != null)
            {
                _db.Entry(entity).CurrentValues.SetValues(ac);

                // update last updated
                entity.TsLastUpdate = DateTime.Now;

                _db.ApiActivity.Update(entity);
                _db.SaveChanges();
                _l.Debug($"Updated in DB: {ac.ToString()}");
            }
            else
            {
                // create new
                _db.ApiActivity.Add(ac);
                _db.SaveChanges();
                _l.Debug($"Added to DB: {ac.ToString()}");
            }
        }
示例#8
0
        public static ApiActivity parseSignToolOutput(SignToolOperation op, ApiActivity ac, string stdOut, string stdErr)
        {
            if (stdOut != null && stdOut.Length > 0)
            {
                //try to parse everything possible
                string patIssuedTo = @"Issued to: (.*?)$";
                Match  m2          = Regex.Match(stdOut, patIssuedTo, RegexOptions.Multiline);
                ac.CertIssuedTo = m2.Groups[1].Value.Trim();

                string patIssuedBy = @"Issued by: (.*?)$";
                Match  m3          = Regex.Match(stdOut, patIssuedBy, RegexOptions.Multiline);
                ac.CertIssuedBy = m3.Groups[1].Value.Trim();

                string patExpire = @"Expires: (.*?)$";
                Match  m4        = Regex.Match(stdOut, patExpire, RegexOptions.Multiline);
                ac.CertExpire = m4.Groups[1].Value.Trim();

                string patCertHash = @"\s+.*?[H|h]ash: (\w+)";
                Match  mCertHash   = Regex.Match(stdOut, patCertHash, RegexOptions.Multiline);
                ac.CertHash = mCertHash.Groups[1].Value.Trim();

                string patFileHash = @"[H|h]ash of file.*?: (\w+)";
                Match  mFileHash   = Regex.Match(stdOut, patFileHash, RegexOptions.Multiline);
                ac.FileHash = mFileHash.Groups[1].Value.Trim();

                // if file hash was not parsed from output of signtool.exe, create our own hash
                if (ac.FileHash == "")
                {
                    using (FileStream fs = System.IO.File.OpenRead(ac.SystemOfficeFilename))
                    {
                        var    sha      = new SHA256Managed();
                        byte[] checksum = sha.ComputeHash(fs);
                        ac.FileHash = BitConverter.ToString(checksum).Replace("-", String.Empty);
                    }
                }


                //decide if error or success
                // note: error is raised if trust chain cannot be verified. This is a normal behavior, due the fact
                // that certificates were not installed in windows cert. store, but only uploaded for checking once.
                // So if cert data can be parsed from file this is success, although stderr is populated!

                if (op == SignToolOperation.Verify)
                {
                    string patSigIdx = @"Signature Index: \d.*$";
                    Match  m5        = Regex.Match(stdOut, patSigIdx, RegexOptions.Multiline);
                    if (m5.Success) // signature could be read from file successfully!
                    {
                        ac.Status  = ApiActivity.ApiStatus.Ready;
                        ac.Message = ac.Message = $"File {ac.UserOfficeFilename} verified successfully.";
                        return(ac);
                    }
                }
                if (op == SignToolOperation.Sign)
                {
                    string patSuccSigned = @"Successfully signed:.*$";
                    Match  m6            = Regex.Match(stdOut, patSuccSigned, RegexOptions.Multiline);
                    if (m6.Success)
                    {
                        ac.Status  = ApiActivity.ApiStatus.Ready;
                        ac.Message = $"File {ac.UserOfficeFilename} signed successfully.";
                        return(ac);
                    }
                }

                // if filehash was not on stdout, calculate seperately
            }
            if (stdErr != null && stdErr.Length > 0)
            {
                // If file was analysed for signatures, but none found -> also success
                string patSigNotFound = @"SignTool Error: No signature found";
                Match  mSigNotFound   = Regex.Match(stdErr, patSigNotFound, RegexOptions.Multiline);
                if (mSigNotFound.Success) // signature could be read from file successfully!
                {
                    ac.Status  = ApiActivity.ApiStatus.Ready;
                    ac.Message = ac.Message = $"File verified successfully: No signature found";
                    return(ac);
                }

                // all other errors
                ac.Status  = ApiActivity.ApiStatus.Error;
                ac.Message = stdErr.Trim();
            }

            return(ac);
        }
示例#9
0
        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()));
            }
        }
示例#10
0
        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()));
            }
        }
示例#11
0
        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()));
        }
示例#12
0
        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()));
            }
        }