簡體   English   中英

POI:設置密碼以防止更改

[英]POI: Set password to protect from changes

我想使用Apache POI對OOXML文件啟用密碼保護。

通過Office程序,在保存文件( pptxxlsx ,...)的同時,我可以選擇“ Tools > Options並且在此提示您設置用於打開和/或更改文件的密碼。

現在,我通過Google搜索了幾個小時,並閱讀了一些API頁面,以找到實現此目的的POI方法,但是卻找不到任何東西。

您是否知道這是實施還是Microsoft專業知識,因為他們對自己的標准化不滿意?

編輯:由於下面的第一條評論指向Office 2003文檔,所以我可能會明確指出:我在談論XSS *功能。 我想從2007年開始保護OOXML格式。我在不同的API上查找了類似的函數,但找不到這些。 HSSWorkBook#writeProtect ...據我所知。

盡管Excel在保存文件時一步一步完成此操作,但這是兩個步驟。

首先,在/xl/workbook.xml中設置ReadOnlyRecommended ,如下所示:

<workbook>
 ...
 <fileSharing readOnlyRecommended="true" userName="user" reservationPassword="DC45"/>
 ...

可以使用org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFileSharing來設置fileSharing元素,您可以在CTWorkbook獲取/設置它,可以從XSSFWorkbook.getCTWorkbook獲取。

對於密碼哈希reservationPassword是由一種特殊的算法計算。 不幸的是,大多數Office Open XML規范都沒有正確描述此算法。 我在Office Open XML Part 4-Transitional Migration Features.pdf(第229頁和第230頁)中找到了正確的描述。

完成此步驟后,您將獲得一個工作簿,其中建議只讀,並帶有寫訪問權限打開密碼。

完成后,您現在可以按照Apache POI-Encryption support中所示設置加密

例:

import java.io.*;
import org.apache.poi.openxml4j.opc.*;
import org.apache.poi.poifs.filesystem.*;
import org.apache.poi.poifs.crypt.*;

import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;

import java.nio.ByteBuffer;

import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;

public class OOXMLEncryptionTest {

 //password hashed using the low-order word algorithm defined in §14.7.1 of ECMA-376
 static short getPasswordHash(String szPassword) {
  int wPasswordHash;
  byte[] pch = szPassword.getBytes();
  int cchPassword = pch.length;
  wPasswordHash = 0;
  if (cchPassword > 0) {
   for (int i = cchPassword; i > 0; i--) {
    wPasswordHash = ((wPasswordHash >> 14) & 0x01) | ((wPasswordHash << 1) & 0x7FFF);
    wPasswordHash ^= pch[i-1];
   }
   wPasswordHash = ((wPasswordHash >> 14) & 0x01) | ((wPasswordHash << 1) & 0x7FFF);
   wPasswordHash ^= cchPassword;
   wPasswordHash ^= (0x8000 | ('N' << 8) | 'K');
  }
System.out.println(wPasswordHash); 
  return (short)(wPasswordHash);
 }

 public static void main(String[] args) throws Exception {

  // Open an Excel workbook and set ReadOnlyRecommended
  XSSFWorkbook workbook = (XSSFWorkbook)WorkbookFactory.create(new FileInputStream("ExcelTest.xlsx"));
  CTWorkbook ctWorkbook = workbook.getCTWorkbook();
  CTFileSharing ctfilesharing = ctWorkbook.getFileSharing();
  if (ctfilesharing == null) ctfilesharing = ctWorkbook.addNewFileSharing();
  ctfilesharing.setReadOnlyRecommended(true);
  ctfilesharing.setUserName("user");

  short passwordhash = getPasswordHash("baafoo");
System.out.println(passwordhash); 

  byte[] bpasswordhash = ByteBuffer.allocate(2).putShort(passwordhash).array();
  ctfilesharing.setReservationPassword(bpasswordhash);

  workbook.write(new FileOutputStream("ExcelTestRORecommended.xlsx"));
  workbook.close();

  // Now do the encryption
  POIFSFileSystem fs = new POIFSFileSystem();
  EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile);
  // EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile, CipherAlgorithm.aes192, HashAlgorithm.sha384, -1, -1, null);

  Encryptor enc = info.getEncryptor();
  enc.confirmPassword("foobaa");

  // Read in an existing OOXML file
  OPCPackage opc = OPCPackage.open(new File("ExcelTestRORecommended.xlsx"), PackageAccess.READ_WRITE);
  OutputStream os = enc.getDataStream(fs);
  opc.save(os);
  opc.close();

  // Write out the encrypted version
  FileOutputStream fos = new FileOutputStream("ExcelTestEncrypted.xlsx");
  fs.writeFilesystem(fos);
  fos.close();

 }
}

對於所有Microsoft Office文件類型,設置只讀建議似乎都是相同的,因為在所有情況下都將在文件保存時設置只讀建議,但在幕后並非如此。 Microsoft將其存儲到文件中的方式非常不同。

Excel它在工作簿的FileSharing元素中為ReadOnlyRecommended ,並且僅使用非常不安全的2字節密碼哈希。

Word它是設置部分中的WriteProtection元素。 它使用現代加密方法使用加鹽的密碼哈希。

PowerPoint中,演示文稿中是ModifyVerifier元素,該元素還使用現代加密方法使用加鹽的密碼哈希。

以下示例顯示了所有三種方法:

import java.io.*;

import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
import java.nio.ByteBuffer;

import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.POIXMLDocumentPart;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.openxmlformats.schemas.presentationml.x2006.main.*;

import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import java.security.SecureRandom;
import java.math.BigInteger;
import java.lang.reflect.Field;

public class RORecommendedTest {

 //password hashed using the low-order word algorithm defined in §14.7.1 of ECMA-376
 static short getPasswordHash(String szPassword) {
  int wPasswordHash;
  byte[] pch = szPassword.getBytes();
  int cchPassword = pch.length;
  wPasswordHash = 0;
  if (cchPassword > 0) {
   for (int i = cchPassword; i > 0; i--) {
    wPasswordHash = ((wPasswordHash >> 14) & 0x01) | ((wPasswordHash << 1) & 0x7FFF);
    wPasswordHash ^= pch[i-1];
   }
   wPasswordHash = ((wPasswordHash >> 14) & 0x01) | ((wPasswordHash << 1) & 0x7FFF);
   wPasswordHash ^= cchPassword;
   wPasswordHash ^= (0x8000 | ('N' << 8) | 'K');
  }
  return (short)(wPasswordHash);
 }

 public static void main(String[] args) throws Exception {

  // Open an Excel workbook and set ReadOnlyRecommended
  XSSFWorkbook workbook = (XSSFWorkbook)WorkbookFactory.create(new FileInputStream("ExcelTest.xlsx"));
  CTWorkbook ctWorkbook = workbook.getCTWorkbook();
  CTFileSharing ctfilesharing = ctWorkbook.getFileSharing();
  if (ctfilesharing == null) ctfilesharing = ctWorkbook.addNewFileSharing();
  ctfilesharing.setReadOnlyRecommended(true);
  ctfilesharing.setUserName("user");

  short passwordhash = getPasswordHash("baafoo");

  byte[] bpasswordhash = ByteBuffer.allocate(2).putShort(passwordhash).array();
  ctfilesharing.setReservationPassword(bpasswordhash);

  workbook.write(new FileOutputStream("ExcelTestRORecommended.xlsx"));
  workbook.close();


  // Open a Word document and set read only recommended aka WriteProtection
  XWPFDocument document = new XWPFDocument(new FileInputStream("WordTest.docx"));

  POIXMLDocumentPart part = null;
  for (int i = 0; i < document.getRelations().size(); i++) {
   part = document.getRelations().get(i);
   if (part instanceof XWPFSettings) break;
  }
  if (part instanceof XWPFSettings) {
   XWPFSettings settings = (XWPFSettings)part;

   Field _ctSettings = XWPFSettings.class.getDeclaredField("ctSettings"); 
   _ctSettings.setAccessible(true); 
   CTSettings ctSettings = (CTSettings)_ctSettings.get(settings);

   CTWriteProtection ctwriteprotection = ctSettings.getWriteProtection();
   if (ctwriteprotection == null) ctwriteprotection = ctSettings.addNewWriteProtection();
   ctwriteprotection.setRecommended(STOnOff.ON);

   ctwriteprotection.setCryptProviderType(org.openxmlformats.schemas.wordprocessingml.x2006.main.STCryptProv.RSA_FULL);
   ctwriteprotection.setCryptAlgorithmClass(org.openxmlformats.schemas.wordprocessingml.x2006.main.STAlgClass.HASH);
   ctwriteprotection.setCryptAlgorithmType(org.openxmlformats.schemas.wordprocessingml.x2006.main.STAlgType.TYPE_ANY);
   ctwriteprotection.setCryptAlgorithmSid(BigInteger.valueOf(4)); //SHA-1
   ctwriteprotection.setCryptSpinCount(BigInteger.valueOf(100000));

   SecureRandom random = new SecureRandom();
   byte[] salt = random.generateSeed(16);
   byte[] hash = CryptoFunctions.hashPassword("baafoo", HashAlgorithm.sha1, salt, 100000, false);

   ctwriteprotection.setHash(hash);
   ctwriteprotection.setSalt(salt);
  }

  document.write(new FileOutputStream("WordTestRORecommended.docx"));
  document.close();

  // Open a PowerPoint show and set read only recommended aka ModifyVerifier
  XMLSlideShow slideShow = new XMLSlideShow(new FileInputStream("PowerPntTest.pptx"));
  CTPresentation ctpresentation = slideShow.getCTPresentation();
  CTModifyVerifier ctmodifyverifier = ctpresentation.getModifyVerifier();
  if (ctmodifyverifier == null) ctmodifyverifier = ctpresentation.addNewModifyVerifier();

  ctmodifyverifier.setCryptProviderType(org.openxmlformats.schemas.presentationml.x2006.main.STCryptProv.RSA_FULL);
  ctmodifyverifier.setCryptAlgorithmClass(org.openxmlformats.schemas.presentationml.x2006.main.STAlgClass.HASH);
  ctmodifyverifier.setCryptAlgorithmType(org.openxmlformats.schemas.presentationml.x2006.main.STAlgType.TYPE_ANY);
  ctmodifyverifier.setCryptAlgorithmSid(4); //SHA-1
  ctmodifyverifier.setSpinCount(100000);

  SecureRandom random = new SecureRandom();
  byte[] salt = random.generateSeed(16);
  byte[] hash = CryptoFunctions.hashPassword("baafoo", HashAlgorithm.sha1, salt, 100000, false);

  ctmodifyverifier.setHashData(java.util.Base64.getEncoder().encodeToString(hash));
  ctmodifyverifier.setSaltData(java.util.Base64.getEncoder().encodeToString(salt));

  slideShow.write(new FileOutputStream("PowerPntTestRORecommended.pptx"));
  slideShow.close();

 }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM