我想使用Apache POI在OOXML文件上启用密码保护。
通过Office程序,在保存文件(pptx
,xlsx
,...)的同时,我可以选择Tools > Options
并在那里提示设置打开和/或更改文件。
现在我通过谷歌搜索了几个小时,并阅读了几个API页面来查找POI方法,但无法找到任何内容。
是否有任何想法是否已实施或微软专业,因为他们不会对自己的标准化做出错误的判断?
编辑: 由于下面的第一条评论指向Office 2003文档,我可能会明确指出:我在谈论XSS *功能。我希望从2007年开始保护OOXML格式。我在不同的API上查找类似的功能,但无法找到它们。 HSSWorkBook#writeProtect ...对我而言。
答案 0 :(得分:3)
虽然Excel
在保存文件的同时一步完成此操作,但这是两个步骤。
首先ReadOnlyRecommended
设置在/xl/workbook.xml
中,如下所示:
<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, page 229 and 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
中,ReadOnlyRecommended
位于工作簿的FileSharing
元素中,并且仅使用非常不安全的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();
}
}