예제 #1
0
        /// <summary>
        /// Protects the document.
        /// </summary>
        /// <param name="wordprocessingDocument">The wordprocessing document.</param>
        public void ProtectDocument(WordprocessingDocument wordprocessingDocument)
        {
            if (wordprocessingDocument == null)
            {
                throw new ArgumentNullException("wordprocessingDocument");
            }

            DocumentSettingsPart documentSettingsPart = wordprocessingDocument.MainDocumentPart.GetPartsOfType <DocumentSettingsPart>().FirstOrDefault();

            if (documentSettingsPart != null)
            {
                var documentProtection = documentSettingsPart.Settings.Elements <DocumentProtection>().FirstOrDefault();

                if (documentProtection != null)
                {
                    documentProtection.Enforcement = true;
                }
                else
                {
                    documentProtection = new DocumentProtection()
                    {
                        Edit = DocumentProtectionValues.Comments, Enforcement = true, CryptographicProviderType = CryptProviderValues.RsaFull, CryptographicAlgorithmClass = CryptAlgorithmClassValues.Hash, CryptographicAlgorithmType = CryptAlgorithmValues.TypeAny, CryptographicAlgorithmSid = 4, CryptographicSpinCount = (UInt32Value)100000U, Hash = "2krUoz1qWd0WBeXqVrOq81l8xpk=", Salt = "9kIgmDDYtt2r5U2idCOwMA=="
                    };
                    documentSettingsPart.Settings.Append(documentProtection);
                }
            }

            wordprocessingDocument.MainDocumentPart.Document.Save();
        }
예제 #2
0
 private void IndicateUnlocked()
 {
     _parent.PreLinkEnable();
     IndicatedUnfavoredLink();
     IsCheckedOutByMe = false;
     IsCheckedOut     = false;
     DocumentProtection.Protect(_documentCustomProperties);
 }
예제 #3
0
        /// <summary>
        /// Get from sitecore checked out status, then set control accordingly
        ///
        /// Unprotects document if locked by current user
        /// </summary>
        public void SetCheckedOutStatus()
        {
            if (_parent == null || _parent.ArticleDetails == null)
            {
                return;
            }
            DocumentCustomProperties = new DocumentCustomProperties(SitecoreAddin.ActiveDocument);
            string articleNum = _parent.GetArticleNumber();

            if (!string.IsNullOrEmpty(_articleNumber))
            { //document is linked to an article
                SetArticleNumber(articleNum);
                CheckoutStatus checkedOut;
                if (_parent.ArticleDetails.ArticleGuid != Guid.Empty)
                {
                    checkedOut = SitecoreClient.GetLockedStatus(_parent.ArticleDetails.ArticleGuid);
                }
                else
                {
                    checkedOut = SitecoreClient.GetLockedStatus(articleNum);
                }
                _articleInformationControl.IsCheckedOut = checkedOut.Locked;
                if (_articleInformationControl.IsCheckedOut)
                {
                    if (SitecoreUser.GetUser().Username == checkedOut.User)
                    { //locked by me
                        IndicateCheckedOutByMe(checkedOut);
                        _parent.articleStatusBar1.ChangeLockButtonStatus(LockStatus.Locked);
                        _articleInformationControl.UpdateControlsForLockedStatus();
                    }
                    else
                    { //locked by other
                        IndicateCheckedOutByOther(checkedOut);
                        _parent.articleStatusBar1.ChangeLockButtonStatus(LockStatus.Locked);
                        _articleInformationControl.UpdateControlsForUnlockedStatus();
                    }
                    uxLockStatusLabel.Text = @"Locked";
                }
                else
                { //unlocked
                    IndicateUnlocked();
                    _articleInformationControl.UpdateControlsForUnlockedStatus();
                }
                uxRefreshStatus.Enabled = true;
            }
            else
            { //document is not linked to an article
                DocumentProtection.Unprotect(DocumentCustomProperties);
                _articleInformationControl.IsCheckedOutByMe = false;
                _articleInformationControl.IsCheckedOut     = false;
                _parent.PreLinkEnable();
            }
        }
예제 #4
0
        /// <summary>
        /// Get from sitecore checked out status, then set control accordingly
        ///
        /// Unprotects document if locked by current user
        /// </summary>
        public void SetCheckedOutStatus()
        {
            if (_parent == null || _parent.ArticleDetails == null)
            {
                return;
            }
            _documentCustomProperties = new DocumentCustomProperties(SitecoreAddin.ActiveDocument);
            string articleNum = _parent.GetArticleNumber();

            if (!articleNum.IsNullOrEmpty())
            { //document is linked to an article
                SetArticleNumber(articleNum);
                PluginModels.CheckoutStatus checkedOut;
                if (_parent.ArticleDetails.ArticleGuid != Guid.Empty)
                {
                    checkedOut = SitecoreClient.GetLockedStatus(_parent.ArticleDetails.ArticleGuid);
                }
                else
                {
                    checkedOut = SitecoreClient.GetLockedStatus(articleNum);
                }
                IsCheckedOut = checkedOut.Locked;
                if (IsCheckedOut)
                {
                    if (SitecoreUser.GetUser().Username == checkedOut.User)
                    { //locked by me
                        IndicateCheckedOutByMe(checkedOut);
                    }
                    else
                    { //locked by other
                        IndicateCheckedOutByOther(checkedOut);
                    }
                    //uxLockStatusLabel.Text = @"Locked";
                }
                else
                { //unlocked
                    IndicateUnlocked();
                }
                //uxRefreshStatus.Enabled = true;
            }
            else
            { //document is not linked to an article
                DocumentProtection.Unprotect(_documentCustomProperties);
                IsCheckedOutByMe = false;
                IsCheckedOut     = false;

                _parent.PreLinkEnable();
            }
        }
예제 #5
0
        /// <summary>
        /// Locks/Unlocks an OpenXML document.
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            if (args.Length != 2)
            {
                Console.WriteLine("Usage: lockdoc lock|unlock filename.docx");
                return;
            }

            bool isLock = false;

            if (args[0].Equals("lock", StringComparison.OrdinalIgnoreCase))
            {
                isLock = true;
            }
            else if (!args[0].Equals("unlock", StringComparison.OrdinalIgnoreCase))
            {
                Console.Error.WriteLine("Wrong action!");
                return;
            }

            WordprocessingDocument doc = WordprocessingDocument.Open(args[1], true);

            doc.ExtendedFilePropertiesPart.Properties.DocumentSecurity =
                new DocumentFormat.OpenXml.ExtendedProperties.DocumentSecurity
                    (isLock ? "8" : "0");
            doc.ExtendedFilePropertiesPart.Properties.Save();

            DocumentProtection dp =
                doc.MainDocumentPart.DocumentSettingsPart
                .Settings.ChildElements.First <DocumentProtection>();

            if (dp != null)
            {
                dp.Remove();
            }

            if (isLock)
            {
                dp             = new DocumentProtection();
                dp.Edit        = DocumentProtectionValues.Comments;
                dp.Enforcement = DocumentFormat.OpenXml.Wordprocessing.BooleanValues.One;

                doc.MainDocumentPart.DocumentSettingsPart.Settings.AppendChild(dp);
            }

            doc.MainDocumentPart.DocumentSettingsPart.Settings.Save();

            doc.Close();
        }
예제 #6
0
        /// <summary>
        /// Enables/disables some controls since it's so similar to a PreLinkEnable state
        /// </summary>
        /// <param name="checkedOut"></param>
        public void IndicateCheckedOutByOther(CheckoutStatus checkedOut)
        {
            var lockUserInfo = SitecoreClient.GetFullNameAndEmail(checkedOut.User);

            if (lockUserInfo.Count == 2)
            {
                uxLockUser.Text = lockUserInfo[0];
                label1.Text     = lockUserInfo[1];
            }
            _articleInformationControl.IsCheckedOutByMe = false;
            _parent.PreLinkEnable();
            IndicatedUnfavoredLink();
            uxUnlockButton.Visible = true;
            uxUnlockButton.Enabled = false;
            uxLockButton.Visible   = false;
            DocumentProtection.Protect(DocumentCustomProperties);
        }
예제 #7
0
        private void IndicateUnlocked()
        {
            _parent.PreLinkEnable();
            IndicatedUnfavoredLink();

            uxLockUser.Text        = @"N\A";
            uxLockStatusLabel.Text = @"Unlocked";

            _articleInformationControl.IsCheckedOutByMe = false;
            _articleInformationControl.IsCheckedOut     = false;

            uxUnlockButton.Visible = false;
            uxLockButton.Visible   = true;
            uxLockButton.Enabled   = true;
            DocumentProtection.Protect(DocumentCustomProperties);
            _statusBar.ChangeLockButtonStatus(LockStatus.Unlocked);
        }
예제 #8
0
        public void IndicateCheckedOutByMe(PluginModels.CheckoutStatus checkedOut)
        {
            DocumentProtection.Unprotect(_documentCustomProperties);
            IsCheckedOutByMe = true;
            if (_parent.CloseOnSuccessfulLock && CheckWordDocVersion(_parent.ArticleDetails, false))
            {
                _parent.Close();
                return;
            }
            _parent.CloseOnSuccessfulLock = false;
            //uxLockStatus.BackColor = Color.FromArgb(255, 217, 234, 211);

            //uxLockUser.Text = FormatUserName(checkedOut.User);
            _parent.PostLinkEnable();
            //uxUnlockButton.Visible = true;
            //uxLockButton.Visible = false;
            //uxUnlockButton.Enabled = true;
        }
예제 #9
0
        /// <summary>
        /// Enables/disables some controls since it's so similar to a PreLinkEnable state
        /// </summary>
        /// <param name="checkedOut"></param>
        public void IndicateCheckedOutByOther(PluginModels.CheckoutStatus checkedOut)
        {
            //uxLockStatus.BackColor = Color.FromArgb(255, 244, 204, 204);

            //uxLockUser.Text = FormatUserName(checkedOut.User);

            IsCheckedOutByMe = false;

            _parent.PreLinkEnable();
            IndicatedUnfavoredLink();
            _parent.EnablePreview();
            _parent.HideCreationButtons();

            //_parent.articleStatusBar1.up
            _parent.articleStatusBar1.ChangeLockButtonStatus(LockStatus.Locked);
            //IndicatedUnfavoredLink();
            DocumentProtection.Protect(_documentCustomProperties);
        }
예제 #10
0
        protected void SendWordDocumentToSitecore(Document activeDocument, DocumentCustomProperties documentCustomProperties, string articleNumber, ArticleStruct articleDetails)
        {
            string extension = GetExtension(activeDocument);

            DocumentProtection.Protect(documentCustomProperties);

            byte[] data = _wordUtils.GetWordBytes(activeDocument);
            if (data == null)
            {
                throw new Exception("Error saving file to disk.");
            }

            DocumentProtection.Unprotect(documentCustomProperties);
            string uploader = SitecoreUser.GetUser().Username;
            int    wordSitecoreVersionNumber = articleDetails.ArticleGuid != Guid.Empty
                                                ? this.SendDocumentToSitecoreByGuid(articleDetails.ArticleGuid, data, extension, uploader)
                                                : this.SendDocumentToSitecore(articleNumber, data, extension, uploader);

            documentCustomProperties.WordSitecoreVersionNumber = wordSitecoreVersionNumber;
        }
예제 #11
0
        void OpenArticleInformationWindowIfNeeded(Word.Document doc)
        {
            var props = new DocumentCustomProperties(doc);

            if (props.PluginName != Constants.InformaPluginName)
            {
                return;
            }
            string articleNumber = props.ArticleNumber;

            if (!string.IsNullOrEmpty(articleNumber))
            {
                Log("Document opened with article number: " + articleNumber);
                if (!SitecoreUser.GetUser().IsLoggedIn)
                {
                    ArticleDetail.Open(true);
                }
                else
                {
                    CheckoutStatus checkedOut = SitecoreClient.GetLockedStatus(articleNumber);
                    if (checkedOut.User == SitecoreUser.GetUser().Username)
                    {
                        DocumentProtection.Unprotect(props);
                        return;
                    }
                    if (!checkedOut.Locked)
                    {
                        if (DialogFactory.PromptAutoLock() == DialogResult.Yes &&
                            SitecoreClient.CheckOutArticle(articleNumber, SitecoreUser.GetUser().Username))
                        {
                            DocumentProtection.Unprotect(props);
                        }
                    }
                    else
                    {
                        ArticleDetail.Open();
                    }
                }
            }
        }
예제 #12
0
        public void IndicateCheckedOutByMe(CheckoutStatus checkedOut)
        {
            DocumentProtection.Unprotect(DocumentCustomProperties);
            _articleInformationControl.IsCheckedOutByMe = true;
            if (_parent.CloseOnSuccessfulLock && _articleInformationControl.CheckWordDocVersion(_parent.ArticleDetails, false))
            {
                _parent.Close();
                return;
            }
            _parent.CloseOnSuccessfulLock = false;
            var lockUserInfo = SitecoreClient.GetFullNameAndEmail(checkedOut.User);

            if (lockUserInfo.Count == 2)
            {
                uxLockUser.Text = lockUserInfo[0];
                label1.Text     = lockUserInfo[1];
            }
            //			uxLockUser.Text = _articleInformationControl.FormatUserName(checkedOut.User);
            _parent.PostLinkEnable();
            uxUnlockButton.Visible = true;
            uxLockButton.Visible   = false;
            uxUnlockButton.Enabled = true;
            _statusBar.ChangeLockButtonStatus(LockStatus.Locked);
        }
예제 #13
0
        public static void ProtectWord(string path, string password)
        {
            string CreateSalt()
            {
                const int saltSize = 8;
                var       salt     = new byte[saltSize];

                RandomNumberGenerator.Create().GetNonZeroBytes(salt);
                return(Convert.ToBase64String(salt));
            }

            const int spinCount = 100;

            string CreateHash(string salt)
            {
                byte[] ComputeHash(HashAlgorithm ha, byte[] a, byte[] b) => ha.ComputeHash(a.Concat(b).ToArray());

                var algorithm = new SHA512Managed();
                var hash      = ComputeHash(algorithm, Convert.FromBase64String(salt), Encoding.Unicode.GetBytes(password));
                var bytes     = new byte[4];

                for (var i = 0; i < spinCount; i++)
                {
                    Array.Copy(BitConverter.GetBytes(i), bytes, bytes.Length);
                    hash = ComputeHash(algorithm, hash, bytes);
                }

                return(Convert.ToBase64String(hash));
            }

            // https://msdn.microsoft.com/library/documentformat.openxml.wordprocessing.writeprotection.cryptographicalgorithmsid.aspx
            const int sha512 = 14;

            using (var wd = WordprocessingDocument.Open(path, true))
            {
                var salt = CreateSalt();

                var dp = new DocumentProtection
                {
                    Edit = DocumentProtectionValues.ReadOnly,
                    CryptographicAlgorithmSid = sha512,
                    CryptographicSpinCount    = spinCount,
                    Salt = salt,
                    Hash = CreateHash(salt)
                };

                var dsp = wd.MainDocumentPart.DocumentSettingsPart;

                var oldDp = dsp.Settings.FirstOrDefault(s => s.GetType() == typeof(DocumentProtection));

                if (oldDp == null)
                {
                    dsp.Settings.AppendChild(dp);
                }
                else
                {
                    dsp.Settings.ReplaceChild(dp, oldDp);
                }

                dsp.Settings.Save();
            }
        }
예제 #14
0
        /*
         * Called from main function for every existing file
         * which reads the given .docx files
         * and replaces the contents as required.
         */
        public void ReadDocx(string files)
        {
            // Separate Directory and file name (without extension name)
            string fileDir             = Path.GetDirectoryName(files);
            string fileName            = Path.GetFileNameWithoutExtension(files);
            string verify_full_fileDir = fileDir + "\\" + Path.GetFileName(files);

            List <bool> bodyMatch   = new List <bool>();
            List <bool> headerMatch = new List <bool>();
            List <bool> footerMatch = new List <bool>();

            List <string> tempList = new List <string>();

            foreach (DirectoryModel row in SelectedDocs)
            {
                // If we should modify the document
                if (row.ApplyChanges)
                {
                    tempList.Add(row.DirectoryNames);
                }
            }

            // If current file is in list of to be modified documents
            if (tempList.Any(verify_full_fileDir.Contains))
            {
                byte[] byteArray = File.ReadAllBytes(files);
                using (MemoryStream stream = new MemoryStream())
                {
                    stream.Write(byteArray, 0, (int)byteArray.Length);
                    using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(stream, true))
                    {
                        DocumentProtection dp =
                            wordDoc.MainDocumentPart.DocumentSettingsPart.Settings.GetFirstChild <DocumentProtection>();
                        if (dp != null && dp.Enforcement == DocumentFormat.OpenXml.OnOffValue.FromBoolean(true))
                        {
                            dp.Remove(); // Doc is protected
                        }

                        foreach (var change in Changes)
                        {
                            if (HelperFunctions.AssertNotEmptyText(change.OldText, change.NewText))
                            {
                                bodyMatch.Add(ReplaceText(wordDoc, change.OldText, change.NewText));
                            }
                        }

                        foreach (var headerChange in HeaderChanges)
                        {
                            if (HelperFunctions.AssertNotEmptyText(headerChange.OldText, headerChange.NewText))
                            {
                                headerMatch.Add(ReplaceHeader(wordDoc, headerChange.OldText, headerChange.NewText));
                            }
                        }


                        foreach (var footerChange in FooterChanges)
                        {
                            if (HelperFunctions.AssertNotEmptyText(footerChange.OldText, footerChange.NewText))
                            {
                                footerMatch.Add(ReplaceFooter(wordDoc, footerChange.OldText, footerChange.NewText));
                            }
                        }

                        if (Metadata.Count > 0)
                        {
                            UpdateMetadata(wordDoc);
                        }
                    }

                    bool hasBodyMatch   = bodyMatch.Any(x => x);
                    bool hasHeaderMatch = headerMatch.Any(x => x);
                    bool hasFooterMatch = footerMatch.Any(x => x);

                    if (hasBodyMatch || hasHeaderMatch || hasFooterMatch)
                    {
                        // List of files that are processed.
                        FileList.Add(fileName);

                        // Check if that file has a date at the end
                        string regexTest = @"_[0-9]{2}-[0-9]{2}-[0-9]{4}"; //check for _DD-MM-YYYY
                                                                           // If the file name contains date, it will take it out and replace for a new one, if not does nothing
                        fileName  = Regex.Replace(fileName, regexTest, "");
                        fileName += "_" + CurrentDate + ".docx";           // Add new date with file etensions

                        // New file directory and old one
                        string new_fileDir  = fileDir + "\\" + fileName;
                        string full_fileDir = fileDir + "\\" + Path.GetFileName(files);
                        // Replace the old by the new
                        string newPath = files.Replace(full_fileDir, new_fileDir);
                        DirectoryList.Add(full_fileDir); // Append to directory list
                        File.WriteAllBytes(newPath, stream.ToArray());

                        Logger = new LogFile(full_fileDir, Changes, hasBodyMatch, hasHeaderMatch, hasFooterMatch, HeaderChanges, FooterChanges);
                        Logger.CreateLogFile();

                        HasChangedAnything.Add(true);
                    }

                    else
                    {
                        HasChangedAnything.Add(false);
                    }
                }
            }
        }
예제 #15
0
        public string SetLock(string szFilePath, DocumentProtectionValues objLockType, string strPwd = "")
        {
            string strResult = string.Empty;

            try
            {
                using (WordprocessingDocument _objDoc = WordprocessingDocument.Open(szFilePath, true))
                {
                    DocumentProtection documentProtection = new DocumentProtection();

                    documentProtection.Edit = objLockType;

                    // Generate the Salt
                    byte[] arrSalt             = new byte[16];
                    RandomNumberGenerator rand = new RNGCryptoServiceProvider();
                    rand.GetNonZeroBytes(arrSalt);

                    //Array to hold Key Values
                    byte[] generatedKey = new byte[4];

                    //Maximum length of the password is 15 chars.
                    int intMaxPasswordLength = 15;

                    if (!string.IsNullOrEmpty(strPwd))
                    {
                        // Truncate the password to 15 characters
                        strPwd = strPwd.Substring(0, Math.Min(strPwd.Length, intMaxPasswordLength));

                        // Construct a new NULL-terminated string consisting of single-byte characters:
                        //  -- > Get the single-byte values by iterating through the Unicode characters of the truncated Password.
                        //   --> For each character, if the low byte is not equal to 0, take it. Otherwise, take the high byte.

                        byte[] arrByteChars = new byte[strPwd.Length];

                        for (int intLoop = 0; intLoop < strPwd.Length; intLoop++)
                        {
                            int intTemp = Convert.ToInt32(strPwd[intLoop]);
                            arrByteChars[intLoop] = Convert.ToByte(intTemp & 0x00FF);
                            if (arrByteChars[intLoop] == 0)
                            {
                                arrByteChars[intLoop] = Convert.ToByte((intTemp & 0xFF00) >> 8);
                            }
                        }

                        // Compute the high-order word of the new key:

                        // --> Initialize from the initial code array (see below), depending on the strPassword’s length.
                        int intHighOrderWord = InitialCodeArray[arrByteChars.Length - 1];

                        // --> For each character in the strPassword:
                        //      --> For every bit in the character, starting with the least significant and progressing to (but excluding)
                        //          the most significant, if the bit is set, XOR the key’s high-order word with the corresponding word from
                        //          the Encryption Matrix

                        for (int intLoop = 0; intLoop < arrByteChars.Length; intLoop++)
                        {
                            int tmp = intMaxPasswordLength - arrByteChars.Length + intLoop;
                            for (int intBit = 0; intBit < 7; intBit++)
                            {
                                if ((arrByteChars[intLoop] & (0x0001 << intBit)) != 0)
                                {
                                    intHighOrderWord ^= EncryptionMatrix[tmp, intBit];
                                }
                            }
                        }

                        // Compute the low-order word of the new key:

                        // Initialize with 0
                        int intLowOrderWord = 0;

                        // For each character in the strPassword, going backwards
                        for (int intLoopChar = arrByteChars.Length - 1; intLoopChar >= 0; intLoopChar--)
                        {
                            // low-order word = (((low-order word SHR 14) AND 0x0001) OR (low-order word SHL 1) AND 0x7FFF)) XOR character
                            intLowOrderWord = (((intLowOrderWord >> 14) & 0x0001) | ((intLowOrderWord << 1) & 0x7FFF)) ^ arrByteChars[intLoopChar];
                        }

                        // Lastly,low-order word = (((low-order word SHR 14) AND 0x0001) OR (low-order word SHL 1) AND 0x7FFF)) XOR strPassword length XOR 0xCE4B.
                        intLowOrderWord = (((intLowOrderWord >> 14) & 0x0001) | ((intLowOrderWord << 1) & 0x7FFF)) ^ arrByteChars.Length ^ 0xCE4B;

                        // Combine the Low and High Order Word
                        int intCombinedkey = (intHighOrderWord << 16) + intLowOrderWord;

                        // The byte order of the result shall be reversed [Example: 0x64CEED7E becomes 7EEDCE64. end example],
                        // and that value shall be hashed as defined by the attribute values.

                        for (int intTemp = 0; intTemp < 4; intTemp++)
                        {
                            generatedKey[intTemp] = Convert.ToByte(((uint)(intCombinedkey & (0x000000FF << (intTemp * 8)))) >> (intTemp * 8));
                        }
                    }

                    // Implementation Notes List:
                    // --> In this third stage, the reversed byte order legacy hash from the second stage shall be converted to Unicode hex
                    // --> string representation
                    StringBuilder sb = new StringBuilder();
                    for (int intTemp = 0; intTemp < 4; intTemp++)
                    {
                        sb.Append(Convert.ToString(generatedKey[intTemp], 16));
                    }
                    generatedKey = Encoding.Unicode.GetBytes(sb.ToString().ToUpper());

                    // Implementation Notes List:
                    //Word appends the binary form of the salt attribute and not the base64 string representation when hashing
                    // Before calculating the initial hash, you are supposed to prepend (not append) the salt to the key
                    byte[] tmpArray1 = generatedKey;
                    byte[] tmpArray2 = arrSalt;
                    byte[] tempKey   = new byte[tmpArray1.Length + tmpArray2.Length];
                    Buffer.BlockCopy(tmpArray2, 0, tempKey, 0, tmpArray2.Length);
                    Buffer.BlockCopy(tmpArray1, 0, tempKey, tmpArray2.Length, tmpArray1.Length);
                    generatedKey = tempKey;

                    // Iterations specifies the number of times the hashing function shall be iteratively run (using each
                    // iteration's result as the input for the next iteration).
                    int iterations = 50000;

                    // Implementation Notes List:
                    //Word requires that the initial hash of the password with the salt not be considered in the count.
                    //    The initial hash of salt + key is not included in the iteration count.
                    HashAlgorithm sha1 = new SHA1Managed();
                    generatedKey = sha1.ComputeHash(generatedKey);
                    byte[] iterator = new byte[4];
                    for (int intTmp = 0; intTmp < iterations; intTmp++)
                    {
                        //When iterating on the hash, you are supposed to append the current iteration number.
                        iterator[0] = Convert.ToByte((intTmp & 0x000000FF) >> 0);
                        iterator[1] = Convert.ToByte((intTmp & 0x0000FF00) >> 8);
                        iterator[2] = Convert.ToByte((intTmp & 0x00FF0000) >> 16);
                        iterator[3] = Convert.ToByte((intTmp & 0xFF000000) >> 24);

                        generatedKey = concatByteArrays(iterator, generatedKey);
                        generatedKey = sha1.ComputeHash(generatedKey);
                    }

                    DocumentFormat.OpenXml.OnOffValue docProtection = new DocumentFormat.OpenXml.OnOffValue(true);
                    documentProtection.Enforcement = docProtection;

                    documentProtection.CryptographicAlgorithmClass = CryptAlgorithmClassValues.Hash;
                    documentProtection.CryptographicProviderType   = CryptProviderValues.RsaFull;
                    documentProtection.CryptographicAlgorithmType  = CryptAlgorithmValues.TypeAny;
                    documentProtection.CryptographicAlgorithmSid   = 4; // SHA1
                    //    The iteration count is unsigned
                    UInt32 uintVal = new UInt32();
                    uintVal = (uint)iterations;
                    documentProtection.CryptographicSpinCount = uintVal;
                    documentProtection.Hash = Convert.ToBase64String(generatedKey);
                    documentProtection.Salt = Convert.ToBase64String(arrSalt);
                    _objDoc.MainDocumentPart.DocumentSettingsPart.Settings.AppendChild(documentProtection);
                    _objDoc.MainDocumentPart.DocumentSettingsPart.Settings.Save();
                    _objDoc.Close();
                }
            }
            catch (Exception ex)
            {
                strResult = ex.Message;
            }

            return(strResult);
        }
예제 #16
0
        internal static void ProtectWord(string path, string password)
        {
            Func<string> createSalt = () =>
            {
                const int saltSize = 8;
                var salt = new byte[saltSize];
                RandomNumberGenerator.Create().GetNonZeroBytes(salt);
                return Convert.ToBase64String(salt);
            };

            const int spinCount = 100;

            Func<string, string> createHash = salt =>
            {
                Func<HashAlgorithm, byte[], byte[], byte[]> computeHash = (ha, a, b) => ha.ComputeHash(a.Concat(b).ToArray());

                var algorithm = new SHA512Managed();
                var hash = computeHash(algorithm, Convert.FromBase64String(salt), Encoding.Unicode.GetBytes(password));
                var bytes = new byte[4];

                for (var i = 0; i < spinCount; i++)
                {
                    Array.Copy(BitConverter.GetBytes(i), bytes, bytes.Length);
                    hash = computeHash(algorithm, hash, bytes);
                }

                return Convert.ToBase64String(hash);
            };

            // https://msdn.microsoft.com/en-us/library/documentformat.openxml.wordprocessing.writeprotection.cryptographicalgorithmsid.aspx
            const int sha512 = 14;

            using (var wd = WordprocessingDocument.Open(path, true))
            {
                var salt = createSalt();

                var dp = new DocumentProtection
                {
                    Edit = DocumentProtectionValues.ReadOnly,
                    CryptographicAlgorithmSid = sha512,
                    CryptographicSpinCount = spinCount,
                    Salt = salt,
                    Hash = createHash(salt)
                };

                var dsp = wd.MainDocumentPart.DocumentSettingsPart;

                var oldDp = dsp.Settings.FirstOrDefault(s => s.GetType() == typeof(DocumentProtection));

                if (oldDp == null)
                {
                    dsp.Settings.AppendChild(dp);
                }
                else
                {
                    dsp.Settings.ReplaceChild(dp, oldDp);
                }

                dsp.Settings.Save();
            }
        }
예제 #17
0
        // Main implementation
        private static void ApplyDocumentProtection(WordprocessingDocument wdDocument, string strPassword)
        {
            // Generate the Salt
            byte[] arrSalt             = new byte[16];
            RandomNumberGenerator rand = new RNGCryptoServiceProvider();

            rand.GetNonZeroBytes(arrSalt);

            //Array to hold Key Values
            byte[] generatedKey = new byte[4];

            //Maximum length of the password is 15 chars.
            int intMaxPasswordLength = 15;


            if (!String.IsNullOrEmpty(strPassword))
            {
                // Truncate the password to 15 characters
                strPassword = strPassword.Substring(0, Math.Min(strPassword.Length, intMaxPasswordLength));

                // Construct a new NULL-terminated string consisting of single-byte characters:
                //  -- > Get the single-byte values by iterating through the Unicode characters of the truncated Password.
                //   --> For each character, if the low byte is not equal to 0, take it. Otherwise, take the high byte.

                byte[] arrByteChars = new byte[strPassword.Length];

                for (int intLoop = 0; intLoop < strPassword.Length; intLoop++)
                {
                    int intTemp = Convert.ToInt32(strPassword[intLoop]);
                    arrByteChars[intLoop] = Convert.ToByte(intTemp & 0x00FF);
                    if (arrByteChars[intLoop] == 0)
                    {
                        arrByteChars[intLoop] = Convert.ToByte((intTemp & 0xFF00) >> 8);
                    }
                }

                // Compute the high-order word of the new key:

                // --> Initialize from the initial code array (see below), depending on the strPassword’s length.
                int intHighOrderWord = InitialCodeArray[arrByteChars.Length - 1];

                // --> For each character in the strPassword:
                //      --> For every bit in the character, starting with the least significant and progressing to (but excluding)
                //          the most significant, if the bit is set, XOR the key’s high-order word with the corresponding word from
                //          the Encryption Matrix

                for (int intLoop = 0; intLoop < arrByteChars.Length; intLoop++)
                {
                    int tmp = intMaxPasswordLength - arrByteChars.Length + intLoop;
                    for (int intBit = 0; intBit < 7; intBit++)
                    {
                        if ((arrByteChars[intLoop] & (0x0001 << intBit)) != 0)
                        {
                            intHighOrderWord ^= EncryptionMatrix[tmp, intBit];
                        }
                    }
                }

                // Compute the low-order word of the new key:

                // Initialize with 0
                int intLowOrderWord = 0;

                // For each character in the strPassword, going backwards
                for (int intLoopChar = arrByteChars.Length - 1; intLoopChar >= 0; intLoopChar--)
                {
                    // low-order word = (((low-order word SHR 14) AND 0x0001) OR (low-order word SHL 1) AND 0x7FFF)) XOR character
                    intLowOrderWord = (((intLowOrderWord >> 14) & 0x0001) | ((intLowOrderWord << 1) & 0x7FFF)) ^ arrByteChars[intLoopChar];
                }

                // Lastly,low-order word = (((low-order word SHR 14) AND 0x0001) OR (low-order word SHL 1) AND 0x7FFF)) XOR strPassword length XOR 0xCE4B.
                intLowOrderWord = (((intLowOrderWord >> 14) & 0x0001) | ((intLowOrderWord << 1) & 0x7FFF)) ^ arrByteChars.Length ^ 0xCE4B;

                // Combine the Low and High Order Word
                int intCombinedkey = (intHighOrderWord << 16) + intLowOrderWord;

                // The byte order of the result shall be reversed [Example: 0x64CEED7E becomes 7EEDCE64. end example],
                // and that value shall be hashed as defined by the attribute values.

                for (int intTemp = 0; intTemp < 4; intTemp++)
                {
                    generatedKey[intTemp] = Convert.ToByte(((uint)(intCombinedkey & (0x000000FF << (intTemp * 8)))) >> (intTemp * 8));
                }
            }

            // Implementation Notes List:
            // --> In this third stage, the reversed byte order legacy hash from the second stage shall be converted to Unicode hex
            // --> string representation
            StringBuilder sb = new StringBuilder();

            for (int intTemp = 0; intTemp < 4; intTemp++)
            {
                sb.Append(Convert.ToString(generatedKey[intTemp], 16));
            }
            generatedKey = Encoding.Unicode.GetBytes(sb.ToString().ToUpper());

            // Implementation Notes List:
            //Word appends the binary form of the salt attribute and not the base64 string representation when hashing
            // Before calculating the initial hash, you are supposed to prepend (not append) the salt to the key
            byte[] tmpArray1 = generatedKey;
            byte[] tmpArray2 = arrSalt;
            byte[] tempKey   = new byte[tmpArray1.Length + tmpArray2.Length];
            Buffer.BlockCopy(tmpArray2, 0, tempKey, 0, tmpArray2.Length);
            Buffer.BlockCopy(tmpArray1, 0, tempKey, tmpArray2.Length, tmpArray1.Length);
            generatedKey = tempKey;


            // Iterations specifies the number of times the hashing function shall be iteratively run (using each
            // iteration's result as the input for the next iteration).
            int iterations = 50000;

            // Implementation Notes List:
            //Word requires that the initial hash of the password with the salt not be considered in the count.
            //    The initial hash of salt + key is not included in the iteration count.
            HashAlgorithm sha1 = new SHA1Managed();

            generatedKey = sha1.ComputeHash(generatedKey);
            byte[] iterator = new byte[4];
            for (int intTmp = 0; intTmp < iterations; intTmp++)
            {
                //When iterating on the hash, you are supposed to append the current iteration number.
                iterator[0] = Convert.ToByte((intTmp & 0x000000FF) >> 0);
                iterator[1] = Convert.ToByte((intTmp & 0x0000FF00) >> 8);
                iterator[2] = Convert.ToByte((intTmp & 0x00FF0000) >> 16);
                iterator[3] = Convert.ToByte((intTmp & 0xFF000000) >> 24);

                generatedKey = concatByteArrays(iterator, generatedKey);
                generatedKey = sha1.ComputeHash(generatedKey);
            }

            // Apply the element
            DocumentProtection documentProtection = new DocumentProtection();

            documentProtection.Edit = DocumentProtectionValues.ReadOnly;

            OnOffValue docProtection = new OnOffValue(true);

            documentProtection.Enforcement = docProtection;

            documentProtection.CryptographicAlgorithmClass = CryptAlgorithmClassValues.Hash;
            documentProtection.CryptographicProviderType   = CryptProviderValues.RsaFull;
            documentProtection.CryptographicAlgorithmType  = CryptAlgorithmValues.TypeAny;
            documentProtection.CryptographicAlgorithmSid   = 4; // SHA1
            //    The iteration count is unsigned
            UInt32Value uintVal = new UInt32Value();

            uintVal.Value = (uint)iterations;
            documentProtection.CryptographicSpinCount = uintVal;
            documentProtection.Hash = Convert.ToBase64String(generatedKey);
            documentProtection.Salt = Convert.ToBase64String(arrSalt);

            // if protection in current document is exist then delete it
            DocumentProtection existingDocumentProtection = wdDocument.MainDocumentPart.DocumentSettingsPart.Settings.Descendants <DocumentProtection>().FirstOrDefault();

            if (existingDocumentProtection != null)
            {
                existingDocumentProtection.Remove();
            }

            wdDocument.MainDocumentPart.DocumentSettingsPart.Settings.AppendChild(documentProtection);
            wdDocument.MainDocumentPart.DocumentSettingsPart.Settings.Save();

            // add permission to edit

            int  id   = 1;
            Body body = wdDocument.MainDocumentPart.Document.GetFirstChild <Body>();

            // for all text
            foreach (var text in body.Descendants <Text>())
            {
                PermStart newPermStart = new PermStart();
                newPermStart.Id          = id;
                newPermStart.EditorGroup = RangePermissionEditingGroupValues.Everyone;
                text.InsertBeforeSelf(newPermStart);
                PermEnd newPermEnd = new PermEnd();
                newPermEnd.Id = id++;
                text.InsertAfterSelf(newPermEnd);
            }
            // for all table
//            var paras = body.Elements<Table>();
//
//            foreach (var para in paras)
//            {
//                PermStart newPermStart = new PermStart();
//                newPermStart.Id = id;
//                newPermStart.EditorGroup = RangePermissionEditingGroupValues.Everyone;
//                para.InsertBeforeSelf(newPermStart);
//                PermEnd newPermEnd = new PermEnd();
//                newPermEnd.Id = id++;
//                para.InsertAfterSelf(newPermEnd);
//            }
            wdDocument.MainDocumentPart.Document.Save();

            // for headers
            foreach (HeaderPart headerPart in wdDocument.MainDocumentPart.HeaderParts)
            {
                foreach (var text in headerPart.RootElement.Descendants <Text>())
                {
                    PermStart newPermStart = new PermStart();
                    newPermStart.Id          = id;
                    newPermStart.EditorGroup = RangePermissionEditingGroupValues.Everyone;
                    text.InsertBeforeSelf(newPermStart);
                    PermEnd newPermEnd = new PermEnd();
                    newPermEnd.Id = id++;
                    text.InsertAfterSelf(newPermEnd);
                }
                headerPart.Header.Save();
            }

            foreach (FooterPart footerPart in wdDocument.MainDocumentPart.FooterParts)
            {
                foreach (var text in footerPart.RootElement.Descendants <Text>())
                {
                    PermStart newPermStart = new PermStart();
                    newPermStart.Id          = id;
                    newPermStart.EditorGroup = RangePermissionEditingGroupValues.Everyone;
                    text.InsertBeforeSelf(newPermStart);
                    PermEnd newPermEnd = new PermEnd();
                    newPermEnd.Id = id++;
                    text.InsertAfterSelf(newPermEnd);
                }
                footerPart.Footer.Save();
            }
        }
        /// <summary>
        /// Get all the CIW information, create temp csv file then load that and then filter it down to the different objects
        /// </summary>
        /// <param name="fileName"></param>

        public string GetCIWInformation(int uploaderID, string filePath, string fileName, out int errorCode)
        {
            List <CIWData> ciwInformation = new List <CIWData>();

            log.Info(String.Format("Getting information from file {0}", filePath));

            //Check for password protection
            try
            {
                using (WordprocessingDocument wd = WordprocessingDocument.Open(filePath, false))
                {
                    DocumentProtection dp = wd.MainDocumentPart.DocumentSettingsPart.Settings.GetFirstChild <DocumentProtection>();
                }
            }
            catch (FileFormatException e)
            {
                log.Warn(string.Format("Locked Document - {0} with inner exception:{1}", e.Message, e.InnerException));
                sendPasswordProtection(uploaderID, fileNameHelper(fileName));
                errorCode = (int)ErrorCodes.password_protected;
                log.Warn(string.Format("Inserting error code {0}:{1} into upload table", ErrorCodes.password_protected, (int)ErrorCodes.password_protected));
                return(null);
            }

            //Begin parsing XML from CIW document
            using (var document = WordprocessingDocument.Open(filePath, true))
            {
                XmlDocument      xml     = new XmlDocument();
                MainDocumentPart docPart = document.MainDocumentPart;
                xml.InnerXml = docPart.Document.FirstChild.OuterXml;
                XmlNamespaceManager nameSpaceManager = new XmlNamespaceManager(xml.NameTable);
                nameSpaceManager.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");

                //Get Version number node
                var node = xml.SelectSingleNode(string.Format("w:body/w:tbl/w:tr/w:tc/w:tbl/w:tr/w:tc/w:sdt/w:sdtContent/w:p/w:r/w:t"), nameSpaceManager);

                if (node != null)
                {
                    if (node.InnerText != "V1")
                    {
                        //Begin exiting if wrong version
                        sendWrongVersion(uploaderID, fileNameHelper(fileName));
                        errorCode = (int)ErrorCodes.wrong_version;
                        log.Warn(string.Format("Inserting error code {0}:{1} into upload table", ErrorCodes.wrong_version, (int)ErrorCodes.wrong_version));
                        return(null);
                    }
                }
                else
                {
                    //Begin exiting if no version on form
                    sendWrongVersion(uploaderID, fileNameHelper(fileName));
                    errorCode = (int)ErrorCodes.wrong_version;
                    log.Warn(string.Format("Inserting error code {0}:{1} into upload table", ErrorCodes.wrong_version, (int)ErrorCodes.wrong_version));
                    return(null);
                }

                try
                {
                    //Gets all data on the form via tags
                    log.Info(string.Format("Parsing XML."));
                    //docpart.document.firstchild is the entire xml document
                    //if we get the child elements we get a list of first children which should be 9
                    //we select the 3rd child which is the main table that gets filled out
                    var docTable = docPart.Document.FirstChild.ChildElements[2];
                    //the first 2 children of this table are grid settings and properties which we dont care about right now
                    docTable.RemoveAllChildren <TableGrid>();
                    docTable.RemoveAllChildren <TableProperties>();
                    //now we have 29 children of type w:tr which are the rows of the table
                    //select all the table cells inside the current table that arent a section header.
                    //currently headers start with a number so excluded those
                    var tableCells = docTable.Descendants <TableCell>().Except(docTable.Descendants <TableCell>().Where(x => "0123456789".Contains(x.InnerText.Trim().Substring(0, 1))));

                    //Grab the version cell and add it to ciwInformation
                    var versionNode = xml.SelectSingleNode(string.Format("w:body/w:tbl/w:tr/w:tc/w:tbl/w:tr/w:tc"), nameSpaceManager).NextSibling;
                    ciwInformation.Add(new CIWData {
                        InnerText = versionNode.InnerText, TagName = versionNode.ChildNodes[1].ChildNodes[0].ChildNodes[1].Attributes[0].Value
                    });

                    //get pob country name
                    var placeOfBirthCountryNode = xml.FirstChild.ChildNodes[2].ChildNodes[4].ChildNodes[4];
                    var pobTagname = placeOfBirthCountryNode.ChildNodes[2].FirstChild.ChildNodes[1].Attributes[0].Value;
                    ciwInformation.Add(new CIWData {
                        InnerText = placeOfBirthCountryNode.LastChild.InnerText, TagName = pobTagname + "2"
                    });

                    //get home country name
                    var homeCountry = xml.FirstChild.ChildNodes[2].ChildNodes[6].ChildNodes[2].LastChild.InnerText;
                    var homeTag     = xml.FirstChild.ChildNodes[2].ChildNodes[6].ChildNodes[2].ChildNodes[2].FirstChild.ChildNodes[1].Attributes[0].Value;
                    ciwInformation.Add(new CIWData {
                        InnerText = homeCountry, TagName = homeTag + "2"
                    });

                    //get citizenship country
                    var citizenCountry = xml.FirstChild.ChildNodes[2].ChildNodes[9].ChildNodes[4].ChildNodes[2].InnerText;
                    var citizenTag     = xml.FirstChild.ChildNodes[2].ChildNodes[9].ChildNodes[4].ChildNodes[2].FirstChild.ChildNodes[1].Attributes[0].Value;
                    ciwInformation.Add(new CIWData {
                        InnerText = citizenCountry, TagName = citizenTag + "2"
                    });

                    //get all table cells and add them after the version in ciwInformation
                    ciwInformation.AddRange(tableCells
                                            .Select
                                            (
                                                s =>
                                                new CIWData
                    {
                        TagName   = s.ChildElements.OfType <SdtBlock>().FirstOrDefault().GetFirstChild <SdtProperties>().GetFirstChild <Tag>().Val,
                        InnerText = ParseXML(s.ChildElements.OfType <SdtBlock>().FirstOrDefault().InnerText, s.OuterXml),
                    }
                                            ).ToList());
                }
                catch (Exception e)
                {
                    log.Warn(string.Format("XML Parsing Failed - {0} with inner exception: {1}", e.Message, e.InnerException));
                    sendWrongVersion(uploaderID, fileNameHelper(fileName));


                    errorCode = (int)ErrorCodes.wrong_version;
                    log.Warn(string.Format("Inserting error code {0}:{1} into upload table", ErrorCodes.wrong_version, (int)ErrorCodes.wrong_version));
                    return(null);
                }


                //used in log
                string lastFirst = (ciwInformation.FirstOrDefault(c => c.TagName == "Employee-LastName").InnerText ?? "null") + ", " + (ciwInformation.FirstOrDefault(c => c.TagName == "Employee-FirstName").InnerText ?? "null");

                log.Info(String.Format("CiwInformation obtained for {0}", lastFirst));

                log.Info(String.Format("Creating temp file for {0}", lastFirst));

                //Create a temp csv file of the information within the form
                string tempFile = CreateTempFile(ciwInformation);
                errorCode = (int)ErrorCodes.successfully_processed;

                return(tempFile);
            }
        }
예제 #19
0
        /// <summary>
        /// Protects the document.
        /// </summary>
        /// <param name="wordprocessingDocument">The word processing document.</param>
        public static void ProtectDocument(WordprocessingDocument wordprocessingDocument)
        {
            if (wordprocessingDocument == null)
            {
                throw new ArgumentNullException("wordprocessingDocument");
            }

            var documentSettingsPart = wordprocessingDocument.MainDocumentPart.GetPartsOfType<DocumentSettingsPart>().FirstOrDefault();

            if (documentSettingsPart != null)
            {
                var documentProtection = documentSettingsPart.Settings.Elements<DocumentProtection>().FirstOrDefault();

                if (documentProtection != null)
                {
                    documentProtection.Enforcement = true;
                }
                else
                {
                    documentProtection = new DocumentProtection()
                    {
                        Edit = DocumentProtectionValues.Comments,
                        Enforcement = false,
                        CryptographicProviderType = CryptProviderValues.RsaFull,
                        CryptographicAlgorithmClass = CryptAlgorithmClassValues.Hash,
                        CryptographicAlgorithmType = CryptAlgorithmValues.TypeAny,
                        CryptographicAlgorithmSid = 4,
                        CryptographicSpinCount = 100000U,
                        Hash = "2krUoz1qWd0WBeXqVrOq81l8xpk=",
                        Salt = "9kIgmDDYtt2r5U2idCOwMA=="
                    };
                    documentSettingsPart.Settings.Append(documentProtection);
                }
            }

            wordprocessingDocument.MainDocumentPart.Document.Save();
        }
예제 #20
0
        public static void PasswordProtect(WordprocessingDocument wordDocument)
        {
            try
            {
                // password protect document
                string password = Password;

                // generate the salt
                byte[] arrSalt             = new byte[16];
                RandomNumberGenerator rand = new RNGCryptoServiceProvider();
                rand.GetNonZeroBytes(arrSalt);

                // array to hold key values
                byte[] generatedKey = new byte[4];

                // maximum length of the password is 15 chars
                int intMaxPasswordLength = 15;

                if (!String.IsNullOrEmpty(password))
                {
                    // truncate the password to 15 characters
                    password = password.Substring(0, Math.Min(password.Length, intMaxPasswordLength));

                    // construct a new NULL-terminated string consisting of single-byte characters:
                    // --> Get the single-byte values by iterating through the Unicode characters of the truncated Password
                    // --> For each character, if the low byte is not equal to 0, take it. Otherwise, take the high byte
                    byte[] arrByteChars = new byte[password.Length];

                    for (int intLoop = 0; intLoop < password.Length; intLoop++)
                    {
                        int intTemp = Convert.ToInt32(password[intLoop]);
                        arrByteChars[intLoop] = Convert.ToByte(intTemp & 0x00FF);
                        if (arrByteChars[intLoop] == 0)
                        {
                            arrByteChars[intLoop] = Convert.ToByte((intTemp & 0xFF00) >> 8);
                        }
                    }

                    // compute the high-order word of the new key:
                    // --> Initialize from the initial code array (see below), depending on the strPassword’s length
                    int intHighOrderWord = InitialCodeArray[arrByteChars.Length - 1];

                    // --> for each character in the password:
                    //   --> For every bit in the character, starting with the least significant and progressing to (but excluding)
                    //       the most significant, if the bit is set, XOR the key’s high-order word with the corresponding word from
                    //       the Encryption Matrix
                    for (int intLoop = 0; intLoop < arrByteChars.Length; intLoop++)
                    {
                        int tmp = intMaxPasswordLength - arrByteChars.Length + intLoop;
                        for (int intBit = 0; intBit < 7; intBit++)
                        {
                            if ((arrByteChars[intLoop] & (0x0001 << intBit)) != 0)
                            {
                                intHighOrderWord ^= EncryptionMatrix[tmp, intBit];
                            }
                        }
                    }

                    // compute the low-order word of the new key:
                    int intLowOrderWord = 0;   // initialize with 0

                    // for each character in the strPassword, going backwards
                    for (int intLoopChar = arrByteChars.Length - 1; intLoopChar >= 0; intLoopChar--)
                    {
                        intLowOrderWord = (((intLowOrderWord >> 14) & 0x0001) | ((intLowOrderWord << 1) & 0x7FFF)) ^ arrByteChars[intLoopChar];
                    }

                    intLowOrderWord = (((intLowOrderWord >> 14) & 0x0001) | ((intLowOrderWord << 1) & 0x7FFF)) ^ arrByteChars.Length ^ 0xCE4B;

                    // combine the Low and High Order Word
                    int combinedKey = (intHighOrderWord << 16) + intLowOrderWord;

                    // the byte order of the result shall be reversed
                    // and that value shall be hashed as defined by the attribute values.
                    for (int intTemp = 0; intTemp < 4; intTemp++)
                    {
                        generatedKey[intTemp] = Convert.ToByte(((uint)(combinedKey & (0x000000FF << (intTemp * 8)))) >> (intTemp * 8));
                    }
                }

                // implementation Notes List:
                // --> In this third stage, the reversed byte order legacy hash from the second stage shall be converted to Unicode hex
                // --> string representation
                StringBuilder sb = new StringBuilder();

                for (int intTemp = 0; intTemp < 4; intTemp++)
                {
                    sb.Append(Convert.ToString(generatedKey[intTemp], 16));
                }

                generatedKey = Encoding.Unicode.GetBytes(sb.ToString().ToUpper());

                // implementation Notes List:
                // --> Word appends the binary form of the salt attribute and not the base64 string representation when hashing
                //     Before calculating the initial hash, you are supposed to prepend (not append) the salt to the key
                byte[] tmpArray1 = generatedKey;
                byte[] tmpArray2 = arrSalt;
                byte[] tempKey   = new byte[tmpArray1.Length + tmpArray2.Length];
                Buffer.BlockCopy(tmpArray2, 0, tempKey, 0, tmpArray2.Length);
                Buffer.BlockCopy(tmpArray1, 0, tempKey, tmpArray2.Length, tmpArray1.Length);
                generatedKey = tempKey;

                // iterations specifies the number of times the hashing function shall be iteratively run (using each
                // iteration's result as the input for the next iteration)
                int iterations = 50000;

                // implementation Notes List:
                // --> Word requires that the initial hash of the password with the salt not be considered in the count
                //     The initial hash of salt + key is not included in the iteration count
                HashAlgorithm sha1 = new SHA1Managed();
                generatedKey = sha1.ComputeHash(generatedKey);
                byte[] iterator = new byte[4];

                for (int intTmp = 0; intTmp < iterations; intTmp++)
                {
                    // when iterating on the hash, you are supposed to append the current iteration number.
                    iterator[0] = Convert.ToByte((intTmp & 0x000000FF) >> 0);
                    iterator[1] = Convert.ToByte((intTmp & 0x0000FF00) >> 8);
                    iterator[2] = Convert.ToByte((intTmp & 0x00FF0000) >> 16);
                    iterator[3] = Convert.ToByte((intTmp & 0xFF000000) >> 24);

                    generatedKey = ConcatByteArrays(iterator, generatedKey);
                    generatedKey = sha1.ComputeHash(generatedKey);
                }

                DocumentProtection documentProtection = new DocumentProtection {
                    Edit = DocumentProtectionValues.ReadOnly
                };

                OnOffValue docProtection = new OnOffValue(true);
                documentProtection.Enforcement = docProtection;

                documentProtection.CryptographicAlgorithmClass = CryptAlgorithmClassValues.Hash;
                documentProtection.CryptographicProviderType   = CryptProviderValues.RsaFull;
                documentProtection.CryptographicAlgorithmType  = CryptAlgorithmValues.TypeAny;
                documentProtection.CryptographicAlgorithmSid   = 4; // SHA1

                // the iteration count is unsigned
                UInt32Value uintVal = new UInt32Value {
                    Value = (uint)iterations
                };
                documentProtection.CryptographicSpinCount = uintVal;
                documentProtection.Hash = Convert.ToBase64String(generatedKey);
                documentProtection.Salt = Convert.ToBase64String(arrSalt);

                wordDocument.MainDocumentPart.DocumentSettingsPart.Settings.AppendChild(documentProtection);
                wordDocument.MainDocumentPart.DocumentSettingsPart.Settings.Save();

                wordDocument.ExtendedFilePropertiesPart.Properties.Save();

                DocumentProtection dp = new DocumentProtection {
                    Edit = DocumentProtectionValues.Comments, Enforcement = true
                };

                wordDocument.MainDocumentPart.DocumentSettingsPart.Settings.AppendChild(dp);

                wordDocument.MainDocumentPart.DocumentSettingsPart.Settings.Save();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }