From f566b57be081e6d426ef149009b808e8d06fd879 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Mon, 21 Apr 2014 12:16:54 +0000 Subject: [PATCH] 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 --- .../poi/poifs/crypt/CryptoFunctions.java | 11 +++--- .../crypt/agile/AgileEncryptionHeader.java | 16 +++------ .../agile/AgileEncryptionInfoBuilder.java | 35 +++++++++++++------ .../crypt/agile/AgileEncryptionVerifier.java | 13 +++---- .../poi/poifs/crypt/agile/AgileEncryptor.java | 19 ++++++---- .../poi/hslf/usermodel/PictureData.java | 15 +++----- .../ExcelFileFormatDocFunctionExtractor.java | 10 ++---- 7 files changed, 60 insertions(+), 59 deletions(-) diff --git a/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java b/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java index 0349c5c67a..f5f52b93f4 100644 --- a/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java +++ b/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java @@ -195,16 +195,18 @@ public class CryptoFunctions { try { // 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"); } Cipher cipher; - if (cipherAlgorithm.needsBouncyCastle) { + if (cipherAlgorithm == CipherAlgorithm.rc4) { + cipher = Cipher.getInstance(cipherAlgorithm.jceId); + } else if (cipherAlgorithm.needsBouncyCastle) { registerBouncyCastle(); - cipher = Cipher.getInstance(key.getAlgorithm() + "/" + chain.jceId + "/" + padding, "BC"); + cipher = Cipher.getInstance(cipherAlgorithm.jceId + "/" + chain.jceId + "/" + padding, "BC"); } else { - cipher = Cipher.getInstance(key.getAlgorithm() + "/" + chain.jceId + "/" + padding); + cipher = Cipher.getInstance(cipherAlgorithm.jceId + "/" + chain.jceId + "/" + padding); } if (vec == null) { @@ -282,7 +284,6 @@ public class CryptoFunctions { } } - private static final int InitialCodeArray[] = { 0xE1F0, 0x1D0F, 0xCC9C, 0x84C0, 0x110C, 0x0E10, 0xF1CE, 0x313E, 0x1872, 0xE139, 0xD40F, 0x84F9, 0x280C, 0xA96A, diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java b/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java index 965207ea47..de953b8db1 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java @@ -16,14 +16,11 @@ ==================================================================== */ package org.apache.poi.poifs.crypt.agile; -import java.io.IOException; - import org.apache.poi.EncryptedDocumentException; import org.apache.poi.poifs.crypt.ChainingMode; import org.apache.poi.poifs.crypt.CipherAlgorithm; import org.apache.poi.poifs.crypt.EncryptionHeader; 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.CTKeyData; @@ -33,14 +30,11 @@ import com.microsoft.schemas.office.x2006.encryption.STCipherChaining; public class AgileEncryptionHeader extends EncryptionHeader { private byte encryptedHmacKey[], encryptedHmacValue[]; - public AgileEncryptionHeader(String descriptor) throws IOException { - EncryptionDocument ed; - try { - ed = EncryptionDocument.Factory.parse(descriptor); - } catch (XmlException e) { - throw new EncryptedDocumentException("Unable to parse encryption descriptor", e); - } - + public AgileEncryptionHeader(String descriptor) { + this(AgileEncryptionInfoBuilder.parseDescriptor(descriptor)); + } + + protected AgileEncryptionHeader(EncryptionDocument ed) { CTKeyData keyData; try { keyData = ed.getEncryption().getKeyData(); diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionInfoBuilder.java b/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionInfoBuilder.java index 12a74620bc..ef19cb85db 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionInfoBuilder.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionInfoBuilder.java @@ -17,14 +17,19 @@ package org.apache.poi.poifs.crypt.agile; import java.io.IOException; +import java.io.InputStream; import org.apache.poi.EncryptedDocumentException; import org.apache.poi.poifs.crypt.ChainingMode; import org.apache.poi.poifs.crypt.CipherAlgorithm; import org.apache.poi.poifs.crypt.EncryptionInfo; 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.filesystem.DocumentInputStream; +import org.apache.xmlbeans.XmlException; + +import com.microsoft.schemas.office.x2006.encryption.EncryptionDocument; public class AgileEncryptionInfoBuilder implements EncryptionInfoBuilder { @@ -37,15 +42,11 @@ public class AgileEncryptionInfoBuilder implements EncryptionInfoBuilder { public void initialize(EncryptionInfo info, DocumentInputStream dis) throws IOException { this.info = info; - StringBuilder builder = new StringBuilder(); - byte[] xmlDescriptor = new byte[dis.available()]; - dis.read(xmlDescriptor); - for (byte b : xmlDescriptor) - builder.append((char)b); - String descriptor = builder.toString(); - header = new AgileEncryptionHeader(descriptor); - verifier = new AgileEncryptionVerifier(descriptor); - if (info.getVersionMajor() == 4 && info.getVersionMinor() == 4) { + EncryptionDocument ed = parseDescriptor(dis); + header = new AgileEncryptionHeader(ed); + verifier = new AgileEncryptionVerifier(ed); + if (info.getVersionMajor() == EncryptionMode.agile.versionMajor + && info.getVersionMinor() == EncryptionMode.agile.versionMinor) { decryptor = new AgileDecryptor(this); } } @@ -107,5 +108,19 @@ public class AgileEncryptionInfoBuilder implements EncryptionInfoBuilder { 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); + } + } } diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionVerifier.java b/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionVerifier.java index b3d2494c20..e2910431aa 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionVerifier.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionVerifier.java @@ -29,7 +29,6 @@ import org.apache.poi.poifs.crypt.ChainingMode; import org.apache.poi.poifs.crypt.CipherAlgorithm; import org.apache.poi.poifs.crypt.EncryptionVerifier; 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.EncryptionDocument; @@ -50,15 +49,11 @@ public class AgileEncryptionVerifier extends EncryptionVerifier { private List certList = new ArrayList(); - public AgileEncryptionVerifier(String descriptor) { - EncryptionDocument ed; - try { - ed = EncryptionDocument.Factory.parse(descriptor); - } catch (XmlException e) { - throw new EncryptedDocumentException("Unable to parse encryption descriptor", e); - } - + this(AgileEncryptionInfoBuilder.parseDescriptor(descriptor)); + } + + protected AgileEncryptionVerifier(EncryptionDocument ed) { Iterator encList = ed.getEncryption().getKeyEncryptors().getKeyEncryptorList().iterator(); CTPasswordKeyEncryptor keyData; try { diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java b/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java index aa538a9908..ee3b71036f 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java @@ -403,6 +403,11 @@ public class AgileEncryptor extends Encryptor { } 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(); AgileEncryptionHeader header = builder.getHeader(); @@ -412,7 +417,7 @@ public class AgileEncryptor extends Encryptor { CTKeyData keyData = edRoot.addNewKeyData(); CTKeyEncryptors keyEncList = edRoot.addNewKeyEncryptors(); CTKeyEncryptor keyEnc = keyEncList.addNewKeyEncryptor(); - keyEnc.setUri(CTKeyEncryptor.Uri.HTTP_SCHEMAS_MICROSOFT_COM_OFFICE_2006_KEY_ENCRYPTOR_PASSWORD); + keyEnc.setUri(passwordUri); CTPasswordKeyEncryptor keyPass = keyEnc.addNewEncryptedPasswordKey(); keyPass.setSpinCount(ver.getSpinCount()); @@ -469,7 +474,7 @@ public class AgileEncryptor extends Encryptor { for (AgileCertificateEntry ace : ver.getCertificates()) { keyEnc = keyEncList.addNewKeyEncryptor(); - keyEnc.setUri(CTKeyEncryptor.Uri.HTTP_SCHEMAS_MICROSOFT_COM_OFFICE_2006_KEY_ENCRYPTOR_CERTIFICATE); + keyEnc.setUri(certificateUri); CTCertificateKeyEncryptor certData = keyEnc.addNewEncryptedCertificateKey(); try { certData.setX509Certificate(ace.x509.getEncoded()); @@ -479,13 +484,13 @@ public class AgileEncryptor extends Encryptor { certData.setEncryptedKeyValue(ace.encryptedKey); certData.setCertVerifier(ace.certVerifier); } - + XmlOptions xo = new XmlOptions(); xo.setCharacterEncoding("UTF-8"); Map nsMap = new HashMap(); - nsMap.put("http://schemas.microsoft.com/office/2006/keyEncryptor/password","p"); - nsMap.put("http://schemas.microsoft.com/office/2006/keyEncryptor/certificate", "c"); - nsMap.put("http://schemas.microsoft.com/office/2006/encryption",""); + nsMap.put(passwordUri.toString(),"p"); + nsMap.put(certificateUri.toString(), "c"); + xo.setUseDefaultNamespace(); xo.setSaveSuggestedPrefixes(nsMap); xo.setSaveNamespacesFirst(); xo.setSaveAggressiveNamespaces(); @@ -505,7 +510,7 @@ public class AgileEncryptor extends Encryptor { leos.writeShort(info.getVersionMajor()); leos.writeShort(info.getVersionMinor()); // Reserved (4 bytes): A value that MUST be 0x00000040 - leos.writeInt(0x40); + leos.writeInt(info.getEncryptionFlags()); leos.write(bos.toByteArray()); dir.createDocument("EncryptionInfo", leos.getWriteIndex(), new POIFSWriterListener() { diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/PictureData.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/PictureData.java index 82e3c2060a..857ad5451e 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/PictureData.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/PictureData.java @@ -21,7 +21,6 @@ import java.awt.Graphics2D; import java.io.IOException; import java.io.OutputStream; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import org.apache.poi.hslf.blip.BitmapPainter; 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.PNG; 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.poifs.crypt.CryptoFunctions; +import org.apache.poi.poifs.crypt.HashAlgorithm; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; @@ -138,14 +138,9 @@ public abstract class PictureData { * Compute 16-byte checksum of this picture using MD5 algorithm. */ public static byte[] getChecksum(byte[] data) { - MessageDigest sha; - try { - sha = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e){ - throw new HSLFException(e.getMessage()); - } - sha.update(data); - return sha.digest(); + MessageDigest md5 = CryptoFunctions.getMessageDigest(HashAlgorithm.md5); + md5.update(data); + return md5.digest(); } /** diff --git a/src/testcases/org/apache/poi/ss/formula/function/ExcelFileFormatDocFunctionExtractor.java b/src/testcases/org/apache/poi/ss/formula/function/ExcelFileFormatDocFunctionExtractor.java index 4d6877f0f9..e6646ae826 100644 --- a/src/testcases/org/apache/poi/ss/formula/function/ExcelFileFormatDocFunctionExtractor.java +++ b/src/testcases/org/apache/poi/ss/formula/function/ExcelFileFormatDocFunctionExtractor.java @@ -31,7 +31,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -43,6 +42,8 @@ import java.util.Stack; import java.util.zip.ZipException; 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.ContentHandler; import org.xml.sax.InputSource; @@ -542,12 +543,7 @@ public final class ExcelFileFormatDocFunctionExtractor { * Helps identify the source file */ private static String getFileMD5(File f) { - MessageDigest m; - try { - m = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } + MessageDigest m = CryptoFunctions.getMessageDigest(HashAlgorithm.md5); byte[]buf = new byte[2048]; try { -- 2.39.5