/// <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(); }
private void IndicateUnlocked() { _parent.PreLinkEnable(); IndicatedUnfavoredLink(); IsCheckedOutByMe = false; IsCheckedOut = false; DocumentProtection.Protect(_documentCustomProperties); }
/// <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(); } }
/// <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(); } }
/// <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(); }
/// <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); }
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); }
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; }
/// <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); }
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; }
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(); } } } }
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); }
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(); } }
/* * 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); } } } }
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); }
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(); } }
// 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); } }
/// <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(); }
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; } }