[英]POI: Set password to protect from changes
我想使用Apache POI對OOXML文件啟用密碼保護。
通過Office程序,在保存文件( pptx
, xlsx
,...)的同時,我可以選擇“ 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.