Java POI:设置密码以防止更改

Java POI:设置密码以防止更改,java,apache-poi,openxml,Java,Apache Poi,Openxml,我想使用ApachePOI对OOXML文件启用密码保护 通过Office程序,在保存文件时(pptx,xlsx,…),我可以选择Tools>Options,并在那里提示设置打开和/或更改文件的密码 现在,我通过谷歌搜索了几个小时,阅读了一些API页面,找到了POI方法,但什么也找不到 你知道这是实现了还是微软的专业,因为他们根本不在乎自己的标准化 编辑: 因为下面的第一条评论是针对Office2003文档的,所以我可能会明确地指出:我说的是XSS*特性。我想保护2007年的OOXML格式。我在不

我想使用ApachePOI对OOXML文件启用密码保护

通过Office程序,在保存文件时(
pptx
xlsx
,…),我可以选择
Tools>Options
,并在那里提示设置打开和/或更改文件的密码

现在,我通过谷歌搜索了几个小时,阅读了一些API页面,找到了POI方法,但什么也找不到

你知道这是实现了还是微软的专业,因为他们根本不在乎自己的标准化

编辑:
因为下面的第一条评论是针对Office2003文档的,所以我可能会明确地指出:我说的是XSS*特性。我想保护2007年的OOXML格式。我在不同的API上查找相似的函数,但找不到它们。HSS工作簿#写保护。。。是我所知道的。

虽然
Excel
在保存文件时一步完成,但这是两个步骤

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

<workbook>
 ...
 <fileSharing readOnlyRecommended="true" userName="user" reservationPassword="DC45"/>
 ...
对于所有
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();

 }
}
import java.io.*;
导入org.apache.poi.xssf.usermodel.xssf工作簿;
导入org.apache.poi.ss.usermodel.WorkbookFactory;
导入org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
导入java.nio.ByteBuffer;
导入org.apache.poi.xwpf.usermodel.*;
导入org.apache.poi.POIXMLDocumentPart;
导入org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
导入org.apache.poi.xslf.usermodel.xmlsideshow;
导入org.openxmlformats.schemas.presentationml.x2006.main.*;
导入org.apache.poi.poifs.crypt.CryptoFunctions;
导入org.apache.poi.poifs.crypt.HashAlgorithm;
导入java.security.SecureRandom;
导入java.math.biginger;
导入java.lang.reflect.Field;
公共类RORecommendedTest{
//使用ECMA-376§14.7.1中定义的低阶字算法散列密码
静态短getPasswordHash(字符串szPassword){
int-wPasswordHash;
字节[]pch=szPassword.getBytes();
int-cchPassword=pch.length;
wPasswordHash=0;
如果(cchPassword>0){
对于(int i=cchPassword;i>0;i--){

wPasswordHash=((wPasswordHash>>14)和0x01)|((wPasswordHash>14)和0x01)|((wPasswordHash检查并搜索关于writeProtectWorkbook@canillas该方法在XSSF类型上不可用。(您可以再次阅读该问题)您通读了吗?@Gagravarr是的。我可以设置密码打开它,但不能设置密码以防止意外修改。@Isfirs是的……对不起。您是否试图保护整个工作表?
sheet.protectSheet(password);
然后启用锁定选项?
sheet.enableLocking()
使用taht,您应该能够执行类似于
sheet.lock…(true)的操作
到目前为止还不错,这让我对如何为XSLX实现这一点有了一些见解。我也在研究如何访问XWPF和XSSF的这些部分。似乎CTDocument和CTPresentation没有任何文件共享,我也不知道OOXML规范(正如您所做的那样).有什么想法吗?我可以简单地回答这个问题,因为这个问题太广泛了,无法在这里回答。但我已经更新了我的答案,提供了为Excel、Word和PowerPoint设置只读推荐的示例。希望能有所帮助。真是令人印象深刻!我不知道你是如何知道这些东西的,我找不到任何关于非常感谢!它现在正在我的应用程序中运行,这正是我所期望的。@Isfirs:所有现代
Microsoft Office
文件格式的第一个源代码。这将有助于
apache poi
的底层对象。但最强大的知识库是一个ZIP工具,它使用您只需解压缩
*.xlsx
*.docx
*.pptx
文件,并查看这些归档文件中的
XML
文件。@AxelRichter:我想知道您为什么没有使用
(短)加密函数。createXorVerifier1(“baafoo”)
在您的示例中…我希望您还不知道,而不是其中的一些错误…顺便说一句。对于浏览ooxml/ole office文件,我最近开始使用一个…特别是对于嵌入式内容,我认为它比zip工具更有用
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();

 }
}