git-svn-id: https://svn.apache.org/repos/asf/poi/branches/xml_signature@1628107 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_11_BETA3
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
} |
@@ -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."); | |||
} | |||
} | |||
} |
@@ -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); |
@@ -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, |
@@ -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); |
@@ -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); | |||
} |
@@ -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; | |||
@@ -207,6 +211,35 @@ public class TestSignatureInfo { | |||
pkg.close(); | |||
} | |||
@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"); | |||
@@ -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 { |