aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Beeker <kiwiwings@apache.org>2014-09-28 22:30:02 +0000
committerAndreas Beeker <kiwiwings@apache.org>2014-09-28 22:30:02 +0000
commit9acd59dd9c771cd0081e0c68ff857a5bb55e67bf (patch)
treef5a4b3829b58e2e397fb2c6496d257a98c39eee8
parent0ec3cdc75c9b29e8ca1c096adcbf53892df4edce (diff)
downloadpoi-9acd59dd9c771cd0081e0c68ff857a5bb55e67bf.tar.gz
poi-9acd59dd9c771cd0081e0c68ff857a5bb55e67bf.zip
more tests, some refactoring
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/xml_signature@1628107 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--src/java/org/apache/poi/poifs/crypt/HashAlgorithm.java30
-rw-r--r--src/ooxml/java/org/apache/poi/poifs/crypt/dsig/KeyInfoKeySelector.java36
-rw-r--r--src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java96
-rw-r--r--src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java77
-rw-r--r--src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/EnvelopedSignatureFacet.java13
-rw-r--r--src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java11
-rw-r--r--src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESSignatureFacet.java8
-rw-r--r--src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java114
-rw-r--r--test-data/xmldsign/chaintest.pfxbin0 -> 10416 bytes
9 files changed, 261 insertions, 124 deletions
diff --git a/src/java/org/apache/poi/poifs/crypt/HashAlgorithm.java b/src/java/org/apache/poi/poifs/crypt/HashAlgorithm.java
index 8f2efc2f71..6a490b0148 100644
--- a/src/java/org/apache/poi/poifs/crypt/HashAlgorithm.java
+++ b/src/java/org/apache/poi/poifs/crypt/HashAlgorithm.java
@@ -17,24 +17,24 @@
package org.apache.poi.poifs.crypt;
-import javax.xml.crypto.dsig.DigestMethod;
-
import org.apache.poi.EncryptedDocumentException;
public enum HashAlgorithm {
- none ( "", 0x0000, "", 0, "", null, false),
- sha1 ( "SHA-1", 0x8004, "SHA1", 20, "HmacSHA1", DigestMethod.SHA1, false),
- sha256 ( "SHA-256", 0x800C, "SHA256", 32, "HmacSHA256", DigestMethod.SHA256, false),
- sha384 ( "SHA-384", 0x800D, "SHA384", 48, "HmacSHA384", null, false),
- sha512 ( "SHA-512", 0x800E, "SHA512", 64, "HmacSHA512", DigestMethod.SHA512, false),
+ none ( "", 0x0000, "", 0, "", false),
+ sha1 ( "SHA-1", 0x8004, "SHA1", 20, "HmacSHA1", false),
+ sha256 ( "SHA-256", 0x800C, "SHA256", 32, "HmacSHA256", false),
+ sha384 ( "SHA-384", 0x800D, "SHA384", 48, "HmacSHA384", false),
+ sha512 ( "SHA-512", 0x800E, "SHA512", 64, "HmacSHA512", false),
/* only for agile encryption */
- md5 ( "MD5", -1, "MD5", 16, "HmacMD5", null, false),
+ md5 ( "MD5", -1, "MD5", 16, "HmacMD5", false),
// although sunjc2 supports md2, hmac-md2 is only supported by bouncycastle
- md2 ( "MD2", -1, "MD2", 16, "Hmac-MD2", null, true),
- md4 ( "MD4", -1, "MD4", 16, "Hmac-MD4", null, true),
- ripemd128("RipeMD128", -1, "RIPEMD-128", 16, "HMac-RipeMD128", null, true),
- ripemd160("RipeMD160", -1, "RIPEMD-160", 20, "HMac-RipeMD160", DigestMethod.RIPEMD160, true),
- whirlpool("Whirlpool", -1, "WHIRLPOOL", 64, "HMac-Whirlpool", null, true),
+ md2 ( "MD2", -1, "MD2", 16, "Hmac-MD2", true),
+ md4 ( "MD4", -1, "MD4", 16, "Hmac-MD4", true),
+ ripemd128("RipeMD128", -1, "RIPEMD-128", 16, "HMac-RipeMD128", true),
+ ripemd160("RipeMD160", -1, "RIPEMD-160", 20, "HMac-RipeMD160", true),
+ whirlpool("Whirlpool", -1, "WHIRLPOOL", 64, "HMac-Whirlpool", true),
+ // only for xml signing
+ sha224 ( "SHA-224", -1, "SHA224", 28, "HmacSHA224", true);
;
public final String jceId;
@@ -42,16 +42,14 @@ public enum HashAlgorithm {
public final String ecmaString;
public final int hashSize;
public final String jceHmacId;
- public final String xmlSignUri;
public final boolean needsBouncyCastle;
- HashAlgorithm(String jceId, int ecmaId, String ecmaString, int hashSize, String jceHmacId, String xmlSignUri, boolean needsBouncyCastle) {
+ HashAlgorithm(String jceId, int ecmaId, String ecmaString, int hashSize, String jceHmacId, boolean needsBouncyCastle) {
this.jceId = jceId;
this.ecmaId = ecmaId;
this.ecmaString = ecmaString;
this.hashSize = hashSize;
this.jceHmacId = jceHmacId;
- this.xmlSignUri = xmlSignUri;
this.needsBouncyCastle = needsBouncyCastle;
}
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/KeyInfoKeySelector.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/KeyInfoKeySelector.java
index c24c36fc49..61fedcb9ec 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/KeyInfoKeySelector.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/KeyInfoKeySelector.java
@@ -26,6 +26,7 @@ package org.apache.poi.poifs.crypt.dsig;
import java.security.Key;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.List;
import javax.xml.crypto.AlgorithmMethod;
@@ -48,7 +49,7 @@ public class KeyInfoKeySelector extends KeySelector implements KeySelectorResult
private static final POILogger LOG = POILogFactory.getLogger(KeyInfoKeySelector.class);
- private X509Certificate certificate;
+ private List<X509Certificate> certChain = new ArrayList<X509Certificate>();
@SuppressWarnings("unchecked")
@Override
@@ -58,35 +59,31 @@ public class KeyInfoKeySelector extends KeySelector implements KeySelectorResult
throw new KeySelectorException("no ds:KeyInfo present");
}
List<XMLStructure> keyInfoContent = keyInfo.getContent();
- this.certificate = null;
+ certChain.clear();
for (XMLStructure keyInfoStructure : keyInfoContent) {
- if (false == (keyInfoStructure instanceof X509Data)) {
+ if (!(keyInfoStructure instanceof X509Data)) {
continue;
}
X509Data x509Data = (X509Data) keyInfoStructure;
List<Object> x509DataList = x509Data.getContent();
for (Object x509DataObject : x509DataList) {
- if (false == (x509DataObject instanceof X509Certificate)) {
+ if (!(x509DataObject instanceof X509Certificate)) {
continue;
}
X509Certificate certificate = (X509Certificate) x509DataObject;
LOG.log(POILogger.DEBUG, "certificate", certificate.getSubjectX500Principal());
- if (null == this.certificate) {
- /*
- * The first certificate is presumably the signer.
- */
- this.certificate = certificate;
- }
- }
- if (null != this.certificate) {
- return this;
+ certChain.add(certificate);
}
}
- throw new KeySelectorException("No key found!");
+ if (certChain.isEmpty()) {
+ throw new KeySelectorException("No key found!");
+ }
+ return this;
}
public Key getKey() {
- return this.certificate.getPublicKey();
+ // The first certificate is presumably the signer.
+ return certChain.isEmpty() ? null : certChain.get(0).getPublicKey();
}
/**
@@ -95,7 +92,12 @@ public class KeyInfoKeySelector extends KeySelector implements KeySelectorResult
*
* @return
*/
- public X509Certificate getCertificate() {
- return this.certificate;
+ public X509Certificate getSigner() {
+ // The first certificate is presumably the signer.
+ return certChain.isEmpty() ? null : certChain.get(0);
+ }
+
+ public List<X509Certificate> getCertChain() {
+ return certChain;
}
}
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java
index d60753e479..7c59fbcae0 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java
@@ -31,6 +31,7 @@ import java.util.UUID;
import javax.xml.crypto.URIDereferencer;
import javax.xml.crypto.dsig.CanonicalizationMethod;
+import javax.xml.crypto.dsig.DigestMethod;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.openxml4j.opc.OPCPackage;
@@ -87,7 +88,10 @@ public class SignatureConfig {
// timestamp service provider URL
private String tspUrl;
private boolean tspOldProtocol = false;
- private HashAlgorithm tspDigestAlgo = HashAlgorithm.sha1;
+ /**
+ * if not defined, it's the same as the main digest
+ */
+ private HashAlgorithm tspDigestAlgo = null;
private String tspUser;
private String tspPass;
private TimeStampServiceValidator tspValidator;
@@ -103,7 +107,10 @@ public class SignatureConfig {
* When <code>null</code> the signature will be limited to XAdES-T only.
*/
private RevocationDataService revocationDataService;
- private HashAlgorithm xadesDigestAlgo = HashAlgorithm.sha1;
+ /**
+ * if not defined, it's the same as the main digest
+ */
+ private HashAlgorithm xadesDigestAlgo = null;
private String xadesRole = null;
private String xadesSignatureId = null;
private boolean xadesSignaturePolicyImplied = true;
@@ -290,9 +297,7 @@ public class SignatureConfig {
return packageSignatureId;
}
public void setPackageSignatureId(String packageSignatureId) {
- this.packageSignatureId = (packageSignatureId != null)
- ? packageSignatureId
- : "xmldsig-" + UUID.randomUUID();
+ this.packageSignatureId = nvl(packageSignatureId,"xmldsig-"+UUID.randomUUID());
}
public String getTspUrl() {
return tspUrl;
@@ -307,7 +312,7 @@ public class SignatureConfig {
this.tspOldProtocol = tspOldProtocol;
}
public HashAlgorithm getTspDigestAlgo() {
- return tspDigestAlgo;
+ return nvl(tspDigestAlgo,digestAlgo);
}
public void setTspDigestAlgo(HashAlgorithm tspDigestAlgo) {
this.tspDigestAlgo = tspDigestAlgo;
@@ -349,7 +354,7 @@ public class SignatureConfig {
this.revocationDataService = revocationDataService;
}
public HashAlgorithm getXadesDigestAlgo() {
- return xadesDigestAlgo;
+ return nvl(xadesDigestAlgo,digestAlgo);
}
public void setXadesDigestAlgo(HashAlgorithm xadesDigestAlgo) {
this.xadesDigestAlgo = xadesDigestAlgo;
@@ -420,4 +425,81 @@ public class SignatureConfig {
public void setNamespacePrefixes(Map<String, String> namespacePrefixes) {
this.namespacePrefixes = namespacePrefixes;
}
+ protected static <T> T nvl(T value, T defaultValue) {
+ return value == null ? defaultValue : value;
+ }
+ public byte[] getHashMagic() {
+ // see https://www.ietf.org/rfc/rfc3110.txt
+ // RSA/SHA1 SIG Resource Records
+ byte result[];
+ switch (getDigestAlgo()) {
+ case sha1: result = new byte[]
+ { 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e
+ , 0x03, 0x02, 0x1a, 0x04, 0x14 };
+ break;
+ case sha224: result = new byte[]
+ { 0x30, 0x2b, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
+ , 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x04, 0x1c };
+ break;
+ case sha256: result = new byte[]
+ { 0x30, 0x2f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
+ , 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x04, 0x20 };
+ break;
+ case sha384: result = new byte[]
+ { 0x30, 0x3f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
+ , 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x04, 0x30 };
+ break;
+ case sha512: result = new byte[]
+ { 0x30, 0x4f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
+ , 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x04, 0x40 };
+ break;
+ case ripemd128: result = new byte[]
+ { 0x30, 0x1b, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24
+ , 0x03, 0x02, 0x02, 0x04, 0x10 };
+ break;
+ case ripemd160: result = new byte[]
+ { 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24
+ , 0x03, 0x02, 0x01, 0x04, 0x14 };
+ break;
+ // case ripemd256: result = new byte[]
+ // { 0x30, 0x2b, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24
+ // , 0x03, 0x02, 0x03, 0x04, 0x20 };
+ // break;
+ default: throw new EncryptedDocumentException("Hash algorithm "
+ +getDigestAlgo()+" not supported for signing.");
+ }
+
+ return result;
+ }
+
+ public String getSignatureMethod() {
+ switch (getDigestAlgo()) {
+ case sha1: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
+ case sha224: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA224;
+ case sha256: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256;
+ case sha384: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA384;
+ case sha512: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512;
+ case ripemd160: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_RIPEMD160;
+ default: throw new EncryptedDocumentException("Hash algorithm "
+ +getDigestAlgo()+" not supported for signing.");
+ }
+ }
+
+ public String getDigestMethodUri() {
+ return getDigestMethodUri(getDigestAlgo());
+ }
+
+ public static String getDigestMethodUri(HashAlgorithm digestAlgo) {
+ switch (digestAlgo) {
+ case sha1: return DigestMethod.SHA1;
+ case sha224: return "http://www.w3.org/2001/04/xmldsig-more#sha224";
+ case sha256: return DigestMethod.SHA256;
+ case sha384: return "http://www.w3.org/2001/04/xmldsig-more#sha384";
+ case sha512: return DigestMethod.SHA512;
+ case ripemd160: return DigestMethod.RIPEMD160;
+ default: throw new EncryptedDocumentException("Hash algorithm "
+ +digestAlgo+" not supported for signing.");
+ }
+ }
+
}
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java
index a4006af3ac..69a771b40f 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java
@@ -25,11 +25,6 @@
package org.apache.poi.poifs.crypt.dsig;
import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.XML_DIGSIG_NS;
-import static org.apache.xml.security.signature.XMLSignature.ALGO_ID_MAC_HMAC_RIPEMD160;
-import static org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
-import static org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256;
-import static org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA384;
-import static org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -113,36 +108,6 @@ import org.xml.sax.SAXException;
public class SignatureInfo implements SignatureConfigurable {
- // see https://www.ietf.org/rfc/rfc3110.txt
- // RSA/SHA1 SIG Resource Records
- public static final byte[] SHA1_DIGEST_INFO_PREFIX = new byte[]
- { 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14 };
-
- public static final byte[] SHA224_DIGEST_INFO_PREFIX = new byte[]
- { 0x30, 0x2b, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
- , 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x04, 0x1c };
-
- public static final byte[] SHA256_DIGEST_INFO_PREFIX = new byte[]
- { 0x30, 0x2f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
- , 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x04, 0x20 };
-
- public static final byte[] SHA384_DIGEST_INFO_PREFIX = new byte[]
- { 0x30, 0x3f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
- , 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x04, 0x30 };
-
- public static final byte[] SHA512_DIGEST_INFO_PREFIX = new byte[]
- { 0x30, 0x4f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
- , 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x04, 0x40 };
-
- public static final byte[] RIPEMD128_DIGEST_INFO_PREFIX = new byte[]
- { 0x30, 0x1b, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x02, 0x04, 0x10 };
-
- public static final byte[] RIPEMD160_DIGEST_INFO_PREFIX = new byte[]
- { 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x01, 0x04, 0x14 };
-
- public static final byte[] RIPEMD256_DIGEST_INFO_PREFIX = new byte[]
- { 0x30, 0x2b, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x03, 0x04, 0x20 };
-
private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class);
private static boolean isInitialized = false;
@@ -151,6 +116,7 @@ public class SignatureInfo implements SignatureConfigurable {
public class SignaturePart {
private final PackagePart signaturePart;
private X509Certificate signer;
+ private List<X509Certificate> certChain;
private SignaturePart(PackagePart signaturePart) {
this.signaturePart = signaturePart;
@@ -164,6 +130,10 @@ public class SignatureInfo implements SignatureConfigurable {
return signer;
}
+ public List<X509Certificate> getCertChain() {
+ return certChain;
+ }
+
public SignatureDocument getSignatureDocument() throws IOException, XmlException {
// TODO: check for XXE
return SignatureDocument.Factory.parse(signaturePart.getInputStream());
@@ -188,7 +158,8 @@ public class SignatureInfo implements SignatureConfigurable {
boolean valid = xmlSignature.validate(domValidateContext);
if (valid) {
- signer = keySelector.getCertificate();
+ signer = keySelector.getSigner();
+ certChain = keySelector.getCertChain();
}
return valid;
@@ -240,7 +211,7 @@ public class SignatureInfo implements SignatureConfigurable {
try {
ByteArrayOutputStream digestInfoValueBuf = new ByteArrayOutputStream();
- digestInfoValueBuf.write(getHashMagic());
+ digestInfoValueBuf.write(signatureConfig.getHashMagic());
digestInfoValueBuf.write(digest);
byte[] digestInfoValue = digestInfoValueBuf.toByteArray();
byte[] signatureValue = cipher.doFinal(digestInfoValue);
@@ -324,31 +295,6 @@ public class SignatureInfo implements SignatureConfigurable {
throw new RuntimeException("JRE doesn't support default xml signature provider - set jsr105Provider system property!");
}
- protected byte[] getHashMagic() {
- switch (signatureConfig.getDigestAlgo()) {
- case sha1: return SHA1_DIGEST_INFO_PREFIX;
- // sha224: return SHA224_DIGEST_INFO_PREFIX;
- case sha256: return SHA256_DIGEST_INFO_PREFIX;
- case sha384: return SHA384_DIGEST_INFO_PREFIX;
- case sha512: return SHA512_DIGEST_INFO_PREFIX;
- case ripemd128: return RIPEMD128_DIGEST_INFO_PREFIX;
- case ripemd160: return RIPEMD160_DIGEST_INFO_PREFIX;
- // case ripemd256: return RIPEMD256_DIGEST_INFO_PREFIX;
- default: throw new EncryptedDocumentException("Hash algorithm "+signatureConfig.getDigestAlgo()+" not supported for signing.");
- }
- }
-
- protected String getSignatureMethod() {
- switch (signatureConfig.getDigestAlgo()) {
- case sha1: return ALGO_ID_SIGNATURE_RSA_SHA1;
- case sha256: return ALGO_ID_SIGNATURE_RSA_SHA256;
- case sha384: return ALGO_ID_SIGNATURE_RSA_SHA384;
- case sha512: return ALGO_ID_SIGNATURE_RSA_SHA512;
- case ripemd160: return ALGO_ID_MAC_HMAC_RIPEMD160;
- default: throw new EncryptedDocumentException("Hash algorithm "+signatureConfig.getDigestAlgo()+" not supported for signing.");
- }
- }
-
protected static synchronized void initXmlProvider() {
if (isInitialized) return;
isInitialized = true;
@@ -409,8 +355,8 @@ public class SignatureInfo implements SignatureConfigurable {
for (DigestInfo digestInfo : safe(digestInfos)) {
byte[] documentDigestValue = digestInfo.digestValue;
- DigestMethod digestMethod = signatureFactory.newDigestMethod(
- digestInfo.hashAlgo.xmlSignUri, null);
+ DigestMethod digestMethod = signatureFactory.newDigestMethod
+ (signatureConfig.getDigestMethodUri(), null);
String uri = new File(digestInfo.description).getName();
@@ -431,7 +377,8 @@ public class SignatureInfo implements SignatureConfigurable {
/*
* ds:SignedInfo
*/
- SignatureMethod signatureMethod = signatureFactory.newSignatureMethod(getSignatureMethod(), null);
+ SignatureMethod signatureMethod = signatureFactory.newSignatureMethod
+ (signatureConfig.getSignatureMethod(), null);
CanonicalizationMethod canonicalizationMethod = signatureFactory
.newCanonicalizationMethod(signatureConfig.getCanonicalizationMethod(),
(C14NMethodParameterSpec) null);
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/EnvelopedSignatureFacet.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/EnvelopedSignatureFacet.java
index c96e932a5d..b9c743548f 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/EnvelopedSignatureFacet.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/EnvelopedSignatureFacet.java
@@ -42,16 +42,15 @@ public class EnvelopedSignatureFacet implements SignatureFacet {
, List<Reference> references
, List<XMLObject> objects)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
- DigestMethod digestMethod = signatureFactory.newDigestMethod(signatureConfig.getDigestAlgo().xmlSignUri, null);
+ DigestMethod digestMethod = signatureFactory.newDigestMethod
+ (signatureConfig.getDigestMethodUri(), null);
List<Transform> transforms = new ArrayList<Transform>();
- Transform envelopedTransform = signatureFactory
- .newTransform(CanonicalizationMethod.ENVELOPED,
- (TransformParameterSpec) null);
+ Transform envelopedTransform = signatureFactory.newTransform
+ (CanonicalizationMethod.ENVELOPED, (TransformParameterSpec) null);
transforms.add(envelopedTransform);
- Transform exclusiveTransform = signatureFactory
- .newTransform(CanonicalizationMethod.EXCLUSIVE,
- (TransformParameterSpec) null);
+ Transform exclusiveTransform = signatureFactory.newTransform
+ (CanonicalizationMethod.EXCLUSIVE, (TransformParameterSpec) null);
transforms.add(exclusiveTransform);
Reference reference = signatureFactory.newReference("", digestMethod,
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java
index b30c077641..28626e8270 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java
@@ -123,7 +123,8 @@ public class OOXMLSignatureFacet implements SignatureFacet {
XMLObject xo = signatureFactory.newXMLObject(objectContent, objectId, null, null);
objects.add(xo);
- DigestMethod digestMethod = signatureFactory.newDigestMethod(signatureConfig.getDigestAlgo().xmlSignUri, null);
+ DigestMethod digestMethod = signatureFactory.newDigestMethod
+ (signatureConfig.getDigestMethodUri(), null);
Reference reference = signatureFactory.newReference
("#" + objectId, digestMethod, null, XML_DIGSIG_NS+"Object", null);
references.add(reference);
@@ -136,7 +137,8 @@ public class OOXMLSignatureFacet implements SignatureFacet {
OPCPackage ooxml = signatureConfig.getOpcPackage();
List<PackagePart> relsEntryNames = ooxml.getPartsByContentType(ContentTypes.RELATIONSHIPS_PART);
- DigestMethod digestMethod = signatureFactory.newDigestMethod(signatureConfig.getDigestAlgo().xmlSignUri, null);
+ DigestMethod digestMethod = signatureFactory.newDigestMethod
+ (signatureConfig.getDigestMethodUri(), null);
Set<String> digestedPartNames = new HashSet<String>();
for (PackagePart pp : relsEntryNames) {
String baseUri = pp.getPartName().getName().replaceFirst("(.*)/_rels/.*", "$1");
@@ -252,7 +254,7 @@ public class OOXMLSignatureFacet implements SignatureFacet {
SignatureInfoV1Document sigV1 = SignatureInfoV1Document.Factory.newInstance();
CTSignatureInfoV1 ctSigV1 = sigV1.addNewSignatureInfoV1();
- ctSigV1.setManifestHashAlgorithm(signatureConfig.getDigestAlgo().xmlSignUri);
+ ctSigV1.setManifestHashAlgorithm(signatureConfig.getDigestMethodUri());
Element n = (Element)document.importNode(ctSigV1.getDomNode(), true);
n.setAttributeNS(XML_NS, XMLConstants.XMLNS_ATTRIBUTE, MS_DIGSIG_NS);
@@ -271,7 +273,8 @@ public class OOXMLSignatureFacet implements SignatureFacet {
String objectId = "idOfficeObject";
objects.add(signatureFactory.newXMLObject(objectContent, objectId, null, null));
- DigestMethod digestMethod = signatureFactory.newDigestMethod(signatureConfig.getDigestAlgo().xmlSignUri, null);
+ DigestMethod digestMethod = signatureFactory.newDigestMethod
+ (signatureConfig.getDigestMethodUri(), null);
Reference reference = signatureFactory.newReference
("#" + objectId, digestMethod, null, XML_DIGSIG_NS+"Object", null);
references.add(reference);
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESSignatureFacet.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESSignatureFacet.java
index 576fa9f514..d34b367dda 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESSignatureFacet.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESSignatureFacet.java
@@ -213,7 +213,7 @@ public class XAdESSignatureFacet implements SignatureFacet {
objects.add(xadesObject);
// add XAdES ds:Reference
- DigestMethod digestMethod = signatureFactory.newDigestMethod(signatureConfig.getDigestAlgo().xmlSignUri, null);
+ DigestMethod digestMethod = signatureFactory.newDigestMethod(signatureConfig.getDigestMethodUri(), null);
List<Transform> transforms = new ArrayList<Transform>();
Transform exclusiveTransform = signatureFactory
.newTransform(CanonicalizationMethod.INCLUSIVE,
@@ -236,11 +236,11 @@ public class XAdESSignatureFacet implements SignatureFacet {
protected static void setDigestAlgAndValue(
DigestAlgAndValueType digestAlgAndValue,
byte[] data,
- HashAlgorithm hashAlgo) {
+ HashAlgorithm digestAlgo) {
DigestMethodType digestMethod = digestAlgAndValue.addNewDigestMethod();
- digestMethod.setAlgorithm(hashAlgo.xmlSignUri);
+ digestMethod.setAlgorithm(SignatureConfig.getDigestMethodUri(digestAlgo));
- MessageDigest messageDigest = CryptoFunctions.getMessageDigest(hashAlgo);
+ MessageDigest messageDigest = CryptoFunctions.getMessageDigest(digestAlgo);
byte[] digestValue = messageDigest.digest(data);
digestAlgAndValue.setDigestValue(digestValue);
}
diff --git a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
index 77299c937d..4444abe89d 100644
--- a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
+++ b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
@@ -24,6 +24,7 @@
package org.apache.poi.poifs.crypt;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -31,6 +32,8 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
@@ -68,6 +71,7 @@ import org.apache.poi.util.DocumentHelper;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.xmlbeans.XmlObject;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.ocsp.OCSPResp;
@@ -208,6 +212,35 @@ public class TestSignatureInfo {
}
@Test
+ public void testManipulation() throws Exception {
+ // sign & validate
+ String testFile = "hello-world-unsigned.xlsx";
+ OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
+ sign(pkg, "Test", "CN=Test", 1);
+
+ // manipulate
+ XSSFWorkbook wb = new XSSFWorkbook(pkg);
+ wb.setSheetName(0, "manipulated");
+ // ... I don't know, why commit is protected ...
+ Method m = XSSFWorkbook.class.getDeclaredMethod("commit");
+ m.setAccessible(true);
+ m.invoke(wb);
+
+ // todo: test a manipulation on a package part, which is not signed
+ // ... maybe in combination with #56164
+
+ // validate
+ SignatureConfig sic = new SignatureConfig();
+ sic.setOpcPackage(pkg);
+ SignatureInfo si = new SignatureInfo();
+ si.setSignatureConfig(sic);
+ boolean b = si.verifySignature();
+ assertFalse("signature should be broken", b);
+
+ wb.close();
+ }
+
+ @Test
public void testSignSpreadsheetWithSignatureInfo() throws Exception {
initKeyPair("Test", "CN=Test");
String testFile = "hello-world-unsigned.xlsx";
@@ -321,7 +354,7 @@ public class TestSignatureInfo {
"$this/ds:Signature/ds:SignedInfo/ds:Reference";
for (ReferenceType rt : (ReferenceType[])sigDoc.selectPath(digestValXQuery)) {
assertNotNull(rt.getDigestValue());
- assertEquals(HashAlgorithm.sha1.xmlSignUri, rt.getDigestMethod().getAlgorithm());
+ assertEquals(signatureConfig.getDigestMethodUri(), rt.getDigestMethod().getAlgorithm());
}
String certDigestXQuery = declareNS +
@@ -341,8 +374,83 @@ public class TestSignatureInfo {
pkg.close();
}
+
+ @Test
+ public void testCertChain() throws Exception {
+ KeyStore keystore = KeyStore.getInstance("PKCS12");
+ String password = "test";
+ InputStream is = testdata.openResourceAsStream("chaintest.pfx");
+ keystore.load(is, password.toCharArray());
+ is.close();
+
+ Key key = keystore.getKey("poitest", password.toCharArray());
+ Certificate chainList[] = keystore.getCertificateChain("poitest");
+ List<X509Certificate> certChain = new ArrayList<X509Certificate>();
+ for (Certificate c : chainList) {
+ certChain.add((X509Certificate)c);
+ }
+ x509 = certChain.get(0);
+ keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key);
+
+ String testFile = "hello-world-unsigned.xlsx";
+ OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
+
+ SignatureConfig signatureConfig = new SignatureConfig();
+ signatureConfig.setKey(keyPair.getPrivate());
+ signatureConfig.setSigningCertificateChain(certChain);
+ Calendar cal = Calendar.getInstance();
+ cal.set(2007, 7, 1);
+ signatureConfig.setExecutionTime(cal.getTime());
+ signatureConfig.setDigestAlgo(HashAlgorithm.sha1);
+ signatureConfig.setOpcPackage(pkg);
+
+ SignatureInfo si = new SignatureInfo();
+ si.setSignatureConfig(signatureConfig);
+
+ si.confirmSignature();
+
+ for (SignaturePart sp : si.getSignatureParts()){
+ boolean b = sp.validate();
+ assertTrue(b);
+ X509Certificate signer = sp.getSigner();
+ assertNotNull("signer undefined?!", signer);
+ List<X509Certificate> certChainRes = sp.getCertChain();
+ assertEquals(3, certChainRes.size());
+ }
+
+ pkg.close();
+ }
+
+ @Test
+ public void testNonSha1() throws Exception {
+ String testFile = "hello-world-unsigned.xlsx";
+ initKeyPair("Test", "CN=Test");
+
+ SignatureConfig signatureConfig = new SignatureConfig();
+ signatureConfig.setKey(keyPair.getPrivate());
+ signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
+
+ HashAlgorithm testAlgo[] = { HashAlgorithm.sha224, HashAlgorithm.sha256
+ , HashAlgorithm.sha384, HashAlgorithm.sha512, HashAlgorithm.ripemd160 };
+
+ for (HashAlgorithm ha : testAlgo) {
+ signatureConfig.setDigestAlgo(ha);
+ OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
+ signatureConfig.setOpcPackage(pkg);
+
+ SignatureInfo si = new SignatureInfo();
+ si.setSignatureConfig(signatureConfig);
+
+ si.confirmSignature();
+ boolean b = si.verifySignature();
+ pkg.close();
- private OPCPackage sign(OPCPackage pkgCopy, String alias, String signerDn, int signerCount) throws Exception {
+ assertTrue(b);
+ }
+ }
+
+
+ private void sign(OPCPackage pkgCopy, String alias, String signerDn, int signerCount) throws Exception {
initKeyPair(alias, signerDn);
SignatureConfig signatureConfig = new SignatureConfig();
@@ -383,8 +491,6 @@ public class TestSignatureInfo {
}
}
assertEquals(signerCount, result.size());
-
- return pkgCopy;
}
private void initKeyPair(String alias, String subjectDN) throws Exception {
diff --git a/test-data/xmldsign/chaintest.pfx b/test-data/xmldsign/chaintest.pfx
new file mode 100644
index 0000000000..e92106d2bb
--- /dev/null
+++ b/test-data/xmldsign/chaintest.pfx
Binary files differ