Some encryption fixes:

- don't rely on SecretKey object having the right algorithm set
- leave encryption-description parsing of string/stream to xmlbeans and refactor it to one location
- use namespaces of schema instead of hard-coded strings
- use CryptoFunctions.getMessageDigest() instead of code duplication

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1588874 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2014-04-21 12:16:54 +00:00
parent 740c46ef84
commit f566b57be0
7 changed files with 60 additions and 59 deletions

View File

@ -195,16 +195,18 @@ public class CryptoFunctions {
try { try {
// Ensure the JCE policies files allow for this sized key // Ensure the JCE policies files allow for this sized key
if (Cipher.getMaxAllowedKeyLength(key.getAlgorithm()) < keySizeInBytes*8) { if (Cipher.getMaxAllowedKeyLength(cipherAlgorithm.jceId) < keySizeInBytes*8) {
throw new EncryptedDocumentException("Export Restrictions in place - please install JCE Unlimited Strength Jurisdiction Policy files"); throw new EncryptedDocumentException("Export Restrictions in place - please install JCE Unlimited Strength Jurisdiction Policy files");
} }
Cipher cipher; Cipher cipher;
if (cipherAlgorithm.needsBouncyCastle) { if (cipherAlgorithm == CipherAlgorithm.rc4) {
cipher = Cipher.getInstance(cipherAlgorithm.jceId);
} else if (cipherAlgorithm.needsBouncyCastle) {
registerBouncyCastle(); registerBouncyCastle();
cipher = Cipher.getInstance(key.getAlgorithm() + "/" + chain.jceId + "/" + padding, "BC"); cipher = Cipher.getInstance(cipherAlgorithm.jceId + "/" + chain.jceId + "/" + padding, "BC");
} else { } else {
cipher = Cipher.getInstance(key.getAlgorithm() + "/" + chain.jceId + "/" + padding); cipher = Cipher.getInstance(cipherAlgorithm.jceId + "/" + chain.jceId + "/" + padding);
} }
if (vec == null) { if (vec == null) {
@ -282,7 +284,6 @@ public class CryptoFunctions {
} }
} }
private static final int InitialCodeArray[] = { private static final int InitialCodeArray[] = {
0xE1F0, 0x1D0F, 0xCC9C, 0x84C0, 0x110C, 0x0E10, 0xF1CE, 0xE1F0, 0x1D0F, 0xCC9C, 0x84C0, 0x110C, 0x0E10, 0xF1CE,
0x313E, 0x1872, 0xE139, 0xD40F, 0x84F9, 0x280C, 0xA96A, 0x313E, 0x1872, 0xE139, 0xD40F, 0x84F9, 0x280C, 0xA96A,

View File

@ -16,14 +16,11 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.poifs.crypt.agile; package org.apache.poi.poifs.crypt.agile;
import java.io.IOException;
import org.apache.poi.EncryptedDocumentException; import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.poifs.crypt.ChainingMode; import org.apache.poi.poifs.crypt.ChainingMode;
import org.apache.poi.poifs.crypt.CipherAlgorithm; import org.apache.poi.poifs.crypt.CipherAlgorithm;
import org.apache.poi.poifs.crypt.EncryptionHeader; import org.apache.poi.poifs.crypt.EncryptionHeader;
import org.apache.poi.poifs.crypt.HashAlgorithm; import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.xmlbeans.XmlException;
import com.microsoft.schemas.office.x2006.encryption.CTDataIntegrity; import com.microsoft.schemas.office.x2006.encryption.CTDataIntegrity;
import com.microsoft.schemas.office.x2006.encryption.CTKeyData; import com.microsoft.schemas.office.x2006.encryption.CTKeyData;
@ -33,14 +30,11 @@ import com.microsoft.schemas.office.x2006.encryption.STCipherChaining;
public class AgileEncryptionHeader extends EncryptionHeader { public class AgileEncryptionHeader extends EncryptionHeader {
private byte encryptedHmacKey[], encryptedHmacValue[]; private byte encryptedHmacKey[], encryptedHmacValue[];
public AgileEncryptionHeader(String descriptor) throws IOException { public AgileEncryptionHeader(String descriptor) {
EncryptionDocument ed; this(AgileEncryptionInfoBuilder.parseDescriptor(descriptor));
try { }
ed = EncryptionDocument.Factory.parse(descriptor);
} catch (XmlException e) { protected AgileEncryptionHeader(EncryptionDocument ed) {
throw new EncryptedDocumentException("Unable to parse encryption descriptor", e);
}
CTKeyData keyData; CTKeyData keyData;
try { try {
keyData = ed.getEncryption().getKeyData(); keyData = ed.getEncryption().getKeyData();

View File

@ -17,14 +17,19 @@
package org.apache.poi.poifs.crypt.agile; package org.apache.poi.poifs.crypt.agile;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import org.apache.poi.EncryptedDocumentException; import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.poifs.crypt.ChainingMode; import org.apache.poi.poifs.crypt.ChainingMode;
import org.apache.poi.poifs.crypt.CipherAlgorithm; import org.apache.poi.poifs.crypt.CipherAlgorithm;
import org.apache.poi.poifs.crypt.EncryptionInfo; import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.EncryptionInfoBuilder; import org.apache.poi.poifs.crypt.EncryptionInfoBuilder;
import org.apache.poi.poifs.crypt.EncryptionMode;
import org.apache.poi.poifs.crypt.HashAlgorithm; import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.poifs.filesystem.DocumentInputStream; import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.xmlbeans.XmlException;
import com.microsoft.schemas.office.x2006.encryption.EncryptionDocument;
public class AgileEncryptionInfoBuilder implements EncryptionInfoBuilder { public class AgileEncryptionInfoBuilder implements EncryptionInfoBuilder {
@ -37,15 +42,11 @@ public class AgileEncryptionInfoBuilder implements EncryptionInfoBuilder {
public void initialize(EncryptionInfo info, DocumentInputStream dis) throws IOException { public void initialize(EncryptionInfo info, DocumentInputStream dis) throws IOException {
this.info = info; this.info = info;
StringBuilder builder = new StringBuilder(); EncryptionDocument ed = parseDescriptor(dis);
byte[] xmlDescriptor = new byte[dis.available()]; header = new AgileEncryptionHeader(ed);
dis.read(xmlDescriptor); verifier = new AgileEncryptionVerifier(ed);
for (byte b : xmlDescriptor) if (info.getVersionMajor() == EncryptionMode.agile.versionMajor
builder.append((char)b); && info.getVersionMinor() == EncryptionMode.agile.versionMinor) {
String descriptor = builder.toString();
header = new AgileEncryptionHeader(descriptor);
verifier = new AgileEncryptionVerifier(descriptor);
if (info.getVersionMajor() == 4 && info.getVersionMinor() == 4) {
decryptor = new AgileDecryptor(this); decryptor = new AgileDecryptor(this);
} }
} }
@ -107,5 +108,19 @@ public class AgileEncryptionInfoBuilder implements EncryptionInfoBuilder {
return info; return info;
} }
protected static EncryptionDocument parseDescriptor(String descriptor) {
try {
return EncryptionDocument.Factory.parse(descriptor);
} catch (XmlException e) {
throw new EncryptedDocumentException("Unable to parse encryption descriptor", e);
}
}
protected static EncryptionDocument parseDescriptor(InputStream descriptor) {
try {
return EncryptionDocument.Factory.parse(descriptor);
} catch (Exception e) {
throw new EncryptedDocumentException("Unable to parse encryption descriptor", e);
}
}
} }

View File

@ -29,7 +29,6 @@ import org.apache.poi.poifs.crypt.ChainingMode;
import org.apache.poi.poifs.crypt.CipherAlgorithm; import org.apache.poi.poifs.crypt.CipherAlgorithm;
import org.apache.poi.poifs.crypt.EncryptionVerifier; import org.apache.poi.poifs.crypt.EncryptionVerifier;
import org.apache.poi.poifs.crypt.HashAlgorithm; import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.xmlbeans.XmlException;
import com.microsoft.schemas.office.x2006.encryption.CTKeyEncryptor; import com.microsoft.schemas.office.x2006.encryption.CTKeyEncryptor;
import com.microsoft.schemas.office.x2006.encryption.EncryptionDocument; import com.microsoft.schemas.office.x2006.encryption.EncryptionDocument;
@ -50,15 +49,11 @@ public class AgileEncryptionVerifier extends EncryptionVerifier {
private List<AgileCertificateEntry> certList = new ArrayList<AgileCertificateEntry>(); private List<AgileCertificateEntry> certList = new ArrayList<AgileCertificateEntry>();
public AgileEncryptionVerifier(String descriptor) { public AgileEncryptionVerifier(String descriptor) {
EncryptionDocument ed; this(AgileEncryptionInfoBuilder.parseDescriptor(descriptor));
try { }
ed = EncryptionDocument.Factory.parse(descriptor);
} catch (XmlException e) { protected AgileEncryptionVerifier(EncryptionDocument ed) {
throw new EncryptedDocumentException("Unable to parse encryption descriptor", e);
}
Iterator<CTKeyEncryptor> encList = ed.getEncryption().getKeyEncryptors().getKeyEncryptorList().iterator(); Iterator<CTKeyEncryptor> encList = ed.getEncryption().getKeyEncryptors().getKeyEncryptorList().iterator();
CTPasswordKeyEncryptor keyData; CTPasswordKeyEncryptor keyData;
try { try {

View File

@ -403,6 +403,11 @@ public class AgileEncryptor extends Encryptor {
} }
protected void createEncryptionInfoEntry(DirectoryNode dir) throws IOException { protected void createEncryptionInfoEntry(DirectoryNode dir) throws IOException {
final CTKeyEncryptor.Uri.Enum passwordUri =
CTKeyEncryptor.Uri.HTTP_SCHEMAS_MICROSOFT_COM_OFFICE_2006_KEY_ENCRYPTOR_PASSWORD;
final CTKeyEncryptor.Uri.Enum certificateUri =
CTKeyEncryptor.Uri.HTTP_SCHEMAS_MICROSOFT_COM_OFFICE_2006_KEY_ENCRYPTOR_CERTIFICATE;
AgileEncryptionVerifier ver = builder.getVerifier(); AgileEncryptionVerifier ver = builder.getVerifier();
AgileEncryptionHeader header = builder.getHeader(); AgileEncryptionHeader header = builder.getHeader();
@ -412,7 +417,7 @@ public class AgileEncryptor extends Encryptor {
CTKeyData keyData = edRoot.addNewKeyData(); CTKeyData keyData = edRoot.addNewKeyData();
CTKeyEncryptors keyEncList = edRoot.addNewKeyEncryptors(); CTKeyEncryptors keyEncList = edRoot.addNewKeyEncryptors();
CTKeyEncryptor keyEnc = keyEncList.addNewKeyEncryptor(); CTKeyEncryptor keyEnc = keyEncList.addNewKeyEncryptor();
keyEnc.setUri(CTKeyEncryptor.Uri.HTTP_SCHEMAS_MICROSOFT_COM_OFFICE_2006_KEY_ENCRYPTOR_PASSWORD); keyEnc.setUri(passwordUri);
CTPasswordKeyEncryptor keyPass = keyEnc.addNewEncryptedPasswordKey(); CTPasswordKeyEncryptor keyPass = keyEnc.addNewEncryptedPasswordKey();
keyPass.setSpinCount(ver.getSpinCount()); keyPass.setSpinCount(ver.getSpinCount());
@ -469,7 +474,7 @@ public class AgileEncryptor extends Encryptor {
for (AgileCertificateEntry ace : ver.getCertificates()) { for (AgileCertificateEntry ace : ver.getCertificates()) {
keyEnc = keyEncList.addNewKeyEncryptor(); keyEnc = keyEncList.addNewKeyEncryptor();
keyEnc.setUri(CTKeyEncryptor.Uri.HTTP_SCHEMAS_MICROSOFT_COM_OFFICE_2006_KEY_ENCRYPTOR_CERTIFICATE); keyEnc.setUri(certificateUri);
CTCertificateKeyEncryptor certData = keyEnc.addNewEncryptedCertificateKey(); CTCertificateKeyEncryptor certData = keyEnc.addNewEncryptedCertificateKey();
try { try {
certData.setX509Certificate(ace.x509.getEncoded()); certData.setX509Certificate(ace.x509.getEncoded());
@ -479,13 +484,13 @@ public class AgileEncryptor extends Encryptor {
certData.setEncryptedKeyValue(ace.encryptedKey); certData.setEncryptedKeyValue(ace.encryptedKey);
certData.setCertVerifier(ace.certVerifier); certData.setCertVerifier(ace.certVerifier);
} }
XmlOptions xo = new XmlOptions(); XmlOptions xo = new XmlOptions();
xo.setCharacterEncoding("UTF-8"); xo.setCharacterEncoding("UTF-8");
Map<String,String> nsMap = new HashMap<String,String>(); Map<String,String> nsMap = new HashMap<String,String>();
nsMap.put("http://schemas.microsoft.com/office/2006/keyEncryptor/password","p"); nsMap.put(passwordUri.toString(),"p");
nsMap.put("http://schemas.microsoft.com/office/2006/keyEncryptor/certificate", "c"); nsMap.put(certificateUri.toString(), "c");
nsMap.put("http://schemas.microsoft.com/office/2006/encryption",""); xo.setUseDefaultNamespace();
xo.setSaveSuggestedPrefixes(nsMap); xo.setSaveSuggestedPrefixes(nsMap);
xo.setSaveNamespacesFirst(); xo.setSaveNamespacesFirst();
xo.setSaveAggressiveNamespaces(); xo.setSaveAggressiveNamespaces();
@ -505,7 +510,7 @@ public class AgileEncryptor extends Encryptor {
leos.writeShort(info.getVersionMajor()); leos.writeShort(info.getVersionMajor());
leos.writeShort(info.getVersionMinor()); leos.writeShort(info.getVersionMinor());
// Reserved (4 bytes): A value that MUST be 0x00000040 // Reserved (4 bytes): A value that MUST be 0x00000040
leos.writeInt(0x40); leos.writeInt(info.getEncryptionFlags());
leos.write(bos.toByteArray()); leos.write(bos.toByteArray());
dir.createDocument("EncryptionInfo", leos.getWriteIndex(), new POIFSWriterListener() { dir.createDocument("EncryptionInfo", leos.getWriteIndex(), new POIFSWriterListener() {

View File

@ -21,7 +21,6 @@ import java.awt.Graphics2D;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.poi.hslf.blip.BitmapPainter; import org.apache.poi.hslf.blip.BitmapPainter;
import org.apache.poi.hslf.blip.DIB; import org.apache.poi.hslf.blip.DIB;
@ -31,8 +30,9 @@ import org.apache.poi.hslf.blip.JPEG;
import org.apache.poi.hslf.blip.PICT; import org.apache.poi.hslf.blip.PICT;
import org.apache.poi.hslf.blip.PNG; import org.apache.poi.hslf.blip.PNG;
import org.apache.poi.hslf.blip.WMF; import org.apache.poi.hslf.blip.WMF;
import org.apache.poi.hslf.exceptions.HSLFException;
import org.apache.poi.hslf.model.Picture; import org.apache.poi.hslf.model.Picture;
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
@ -138,14 +138,9 @@ public abstract class PictureData {
* Compute 16-byte checksum of this picture using MD5 algorithm. * Compute 16-byte checksum of this picture using MD5 algorithm.
*/ */
public static byte[] getChecksum(byte[] data) { public static byte[] getChecksum(byte[] data) {
MessageDigest sha; MessageDigest md5 = CryptoFunctions.getMessageDigest(HashAlgorithm.md5);
try { md5.update(data);
sha = MessageDigest.getInstance("MD5"); return md5.digest();
} catch (NoSuchAlgorithmException e){
throw new HSLFException(e.getMessage());
}
sha.update(data);
return sha.digest();
} }
/** /**

View File

@ -31,7 +31,6 @@ import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -43,6 +42,8 @@ import java.util.Stack;
import java.util.zip.ZipException; import java.util.zip.ZipException;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler; import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
@ -542,12 +543,7 @@ public final class ExcelFileFormatDocFunctionExtractor {
* Helps identify the source file * Helps identify the source file
*/ */
private static String getFileMD5(File f) { private static String getFileMD5(File f) {
MessageDigest m; MessageDigest m = CryptoFunctions.getMessageDigest(HashAlgorithm.md5);
try {
m = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
byte[]buf = new byte[2048]; byte[]buf = new byte[2048];
try { try {