mirror of https://github.com/apache/poi.git
Follow-on for bugzilla 47652 - used more specific exception when password is incorrect
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@804381 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
f9d10a5631
commit
b23ac7a022
|
@ -23,6 +23,7 @@ import java.util.List;
|
|||
import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
|
||||
import org.apache.poi.hssf.eventusermodel.HSSFListener;
|
||||
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
|
||||
import org.apache.poi.EncryptedDocumentException;
|
||||
|
||||
/**
|
||||
* A stream based way to get at complete records, with
|
||||
|
@ -41,7 +42,7 @@ public final class RecordFactoryInputStream {
|
|||
* Needed for protected files because each byte is encrypted with respect to its absolute
|
||||
* position from the start of the stream.
|
||||
*/
|
||||
public static final class StreamEncryptionInfo {
|
||||
private static final class StreamEncryptionInfo {
|
||||
private final int _initialRecordsSize;
|
||||
private final FilePassRecord _filePassRec;
|
||||
private final Record _lastRecord;
|
||||
|
@ -97,7 +98,9 @@ public final class RecordFactoryInputStream {
|
|||
key = Biff8EncryptionKey.create(userPassword, fpr.getDocId());
|
||||
}
|
||||
if (!key.validate(fpr.getSaltData(), fpr.getSaltHash())) {
|
||||
throw new RecordFormatException("Password/docId do not correspond to saltData/saltHash");
|
||||
throw new EncryptedDocumentException(
|
||||
(userPassword == null ? "Default" : "Supplied")
|
||||
+ " password is invalid for docId/saltData/saltHash");
|
||||
}
|
||||
return new RecordInputStream(original, key, _initialRecordsSize);
|
||||
}
|
||||
|
|
|
@ -90,9 +90,21 @@ public final class Biff8EncryptionKey {
|
|||
md5.update(saltDataPrime);
|
||||
byte[] finalSaltResult = md5.digest();
|
||||
|
||||
if (false) { // set true to see a valid saltHash value
|
||||
byte[] saltHashThatWouldWork = xor(saltHash, xor(saltHashPrime, finalSaltResult));
|
||||
System.out.println(HexDump.toHex(saltHashThatWouldWork));
|
||||
}
|
||||
|
||||
return Arrays.equals(saltHashPrime, finalSaltResult);
|
||||
}
|
||||
|
||||
private static byte[] xor(byte[] a, byte[] b) {
|
||||
byte[] c = new byte[a.length];
|
||||
for (int i = 0; i < c.length; i++) {
|
||||
c[i] = (byte) (a[i] ^ b[i]);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
private static void check16Bytes(byte[] data, String argName) {
|
||||
if (data.length != 16) {
|
||||
throw new IllegalArgumentException("Expected 16 byte " + argName + ", but got " + HexDump.toHex(data));
|
||||
|
|
|
@ -47,9 +47,11 @@ public final class AllRecordTests {
|
|||
result.addTestSuite(TestBOFRecord.class);
|
||||
result.addTestSuite(TestBoolErrRecord.class);
|
||||
result.addTestSuite(TestBoundSheetRecord.class);
|
||||
result.addTestSuite(TestCellRange.class);
|
||||
result.addTestSuite(TestCFHeaderRecord.class);
|
||||
result.addTestSuite(TestCFRuleRecord.class);
|
||||
result.addTestSuite(TestCommonObjectDataSubRecord.class);
|
||||
result.addTestSuite(TestConstantValueParser.class);
|
||||
result.addTestSuite(TestDrawingGroupRecord.class);
|
||||
result.addTestSuite(TestEmbeddedObjectRefSubRecord.class);
|
||||
result.addTestSuite(TestEndSubRecord.class);
|
||||
|
@ -67,8 +69,9 @@ public final class AllRecordTests {
|
|||
result.addTestSuite(TestObjRecord.class);
|
||||
result.addTestSuite(TestPaletteRecord.class);
|
||||
result.addTestSuite(TestPaneRecord.class);
|
||||
result.addTestSuite(TestRecordInputStream.class);
|
||||
result.addTestSuite(TestRecordFactory.class);
|
||||
result.addTestSuite(TestRecordFactoryInputStream.class);
|
||||
result.addTestSuite(TestRecordInputStream.class);
|
||||
result.addTestSuite(TestSCLRecord.class);
|
||||
result.addTestSuite(TestSSTDeserializer.class);
|
||||
result.addTestSuite(TestSSTRecord.class);
|
||||
|
@ -84,8 +87,6 @@ public final class AllRecordTests {
|
|||
result.addTestSuite(TestUnicodeNameRecord.class);
|
||||
result.addTestSuite(TestUnicodeString.class);
|
||||
result.addTestSuite(TestWriteAccessRecord.class);
|
||||
result.addTestSuite(TestCellRange.class);
|
||||
result.addTestSuite(TestConstantValueParser.class);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.record;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.EncryptedDocumentException;
|
||||
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
|
||||
import org.apache.poi.util.HexRead;
|
||||
|
||||
/**
|
||||
* Tests for {@link RecordFactoryInputStream}
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class TestRecordFactoryInputStream extends TestCase {
|
||||
|
||||
/**
|
||||
* Hex dump of a BOF record and most of a FILEPASS record.
|
||||
* A 16 byte saltHash should be added to complete the second record
|
||||
*/
|
||||
private static final String COMMON_HEX_DATA = ""
|
||||
// BOF
|
||||
+ "09 08 10 00"
|
||||
+ "00 06 05 00 D3 10 CC 07 01 00 00 00 00 06 00 00"
|
||||
// FILEPASS
|
||||
+ "2F 00 36 00"
|
||||
+ "01 00 01 00 01 00"
|
||||
+ "BAADF00D BAADF00D BAADF00D BAADF00D" // docId
|
||||
+ "DEADBEEF DEADBEEF DEADBEEF DEADBEEF" // saltData
|
||||
;
|
||||
|
||||
/**
|
||||
* Hex dump of a sample WINDOW1 record
|
||||
*/
|
||||
private static final String SAMPLE_WINDOW1 = "3D 00 12 00"
|
||||
+ "00 00 00 00 40 38 55 23 38 00 00 00 00 00 01 00 58 02";
|
||||
|
||||
/**
|
||||
* Makes sure that a default password mismatch condition is represented with {@link EncryptedDocumentException}
|
||||
*/
|
||||
public void testDefaultPassword() {
|
||||
// This encodng depends on docId, password and stream position
|
||||
final String SAMPLE_WINDOW1_ENCR1 = "3D 00 12 00"
|
||||
+ "C4, 9B, 02, 50, 86, E0, DF, 34, FB, 57, 0E, 8C, CE, 25, 45, E3, 80, 01";
|
||||
|
||||
byte[] dataWrongDefault = HexRead.readFromString(""
|
||||
+ COMMON_HEX_DATA
|
||||
+ "00000000 00000000 00000000 00000001"
|
||||
+ SAMPLE_WINDOW1_ENCR1
|
||||
);
|
||||
|
||||
RecordFactoryInputStream rfis;
|
||||
try {
|
||||
rfis = createRFIS(dataWrongDefault);
|
||||
throw new AssertionFailedError("Expected password mismatch error");
|
||||
} catch (EncryptedDocumentException e) {
|
||||
// expected during successful test
|
||||
if (!e.getMessage().equals("Default password is invalid for docId/saltData/saltHash")) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] dataCorrectDefault = HexRead.readFromString(""
|
||||
+ COMMON_HEX_DATA
|
||||
+ "137BEF04 969A200B 306329DE 52254005" // correct saltHash for default password (and docId/saltHash)
|
||||
+ SAMPLE_WINDOW1_ENCR1
|
||||
);
|
||||
|
||||
rfis = createRFIS(dataCorrectDefault);
|
||||
|
||||
confirmReadInitialRecords(rfis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that an incorrect user supplied password condition is represented with {@link EncryptedDocumentException}
|
||||
*/
|
||||
public void testSuppliedPassword() {
|
||||
// This encodng depends on docId, password and stream position
|
||||
final String SAMPLE_WINDOW1_ENCR2 = "3D 00 12 00"
|
||||
+ "45, B9, 90, FE, B6, C6, EC, 73, EE, 3F, 52, 45, 97, DB, E3, C1, D6, FE";
|
||||
|
||||
byte[] dataWrongDefault = HexRead.readFromString(""
|
||||
+ COMMON_HEX_DATA
|
||||
+ "00000000 00000000 00000000 00000000"
|
||||
+ SAMPLE_WINDOW1_ENCR2
|
||||
);
|
||||
|
||||
|
||||
Biff8EncryptionKey.setCurrentUserPassword("passw0rd");
|
||||
|
||||
RecordFactoryInputStream rfis;
|
||||
try {
|
||||
rfis = createRFIS(dataWrongDefault);
|
||||
throw new AssertionFailedError("Expected password mismatch error");
|
||||
} catch (EncryptedDocumentException e) {
|
||||
// expected during successful test
|
||||
if (!e.getMessage().equals("Supplied password is invalid for docId/saltData/saltHash")) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] dataCorrectDefault = HexRead.readFromString(""
|
||||
+ COMMON_HEX_DATA
|
||||
+ "C728659A C38E35E0 568A338F C3FC9D70" // correct saltHash for supplied password (and docId/saltHash)
|
||||
+ SAMPLE_WINDOW1_ENCR2
|
||||
);
|
||||
|
||||
rfis = createRFIS(dataCorrectDefault);
|
||||
Biff8EncryptionKey.setCurrentUserPassword(null);
|
||||
|
||||
confirmReadInitialRecords(rfis);
|
||||
}
|
||||
|
||||
/**
|
||||
* makes sure the record stream starts with {@link BOFRecord} and then {@link WindowOneRecord}
|
||||
* The second record is gets decrypted so this method also checks its content.
|
||||
*/
|
||||
private void confirmReadInitialRecords(RecordFactoryInputStream rfis) {
|
||||
assertEquals(BOFRecord.class, rfis.nextRecord().getClass());
|
||||
WindowOneRecord rec1 = (WindowOneRecord) rfis.nextRecord();
|
||||
assertTrue(Arrays.equals(HexRead.readFromString(SAMPLE_WINDOW1),rec1.serialize()));
|
||||
}
|
||||
|
||||
private static RecordFactoryInputStream createRFIS(byte[] data) {
|
||||
return new RecordFactoryInputStream(new ByteArrayInputStream(data), true);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue