예제 #1
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();
        }
예제 #2
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);
                    }
                }
            }
        }
예제 #3
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();
            }
        }