git-svn-id: https://svn.apache.org/repos/asf/poi/branches/xml_signature@1627434 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_11_BETA3
@@ -44,8 +44,10 @@ import java.security.cert.X509Certificate; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.NoSuchElementException; | |||
import javax.crypto.Cipher; | |||
import javax.xml.crypto.MarshalException; | |||
@@ -142,6 +144,58 @@ public class SignatureInfo implements SignatureConfigurable { | |||
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; | |||
private SignatureConfig signatureConfig; | |||
public class SignaturePart { | |||
private final PackagePart signaturePart; | |||
private X509Certificate signer; | |||
private SignaturePart(PackagePart signaturePart) { | |||
this.signaturePart = signaturePart; | |||
} | |||
public PackagePart getPackagePart() { | |||
return signaturePart; | |||
} | |||
public X509Certificate getSigner() { | |||
return signer; | |||
} | |||
public SignatureDocument getSignatureDocument() throws IOException, XmlException { | |||
// TODO: check for XXE | |||
return SignatureDocument.Factory.parse(signaturePart.getInputStream()); | |||
} | |||
public boolean validate() { | |||
KeyInfoKeySelector keySelector = new KeyInfoKeySelector(); | |||
try { | |||
Document doc = DocumentHelper.readDocument(signaturePart.getInputStream()); | |||
registerIds(doc); | |||
DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc); | |||
domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE); | |||
domValidateContext.setURIDereferencer(signatureConfig.getUriDereferencer()); | |||
XMLSignatureFactory xmlSignatureFactory = getSignatureFactory(); | |||
XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext); | |||
boolean valid = xmlSignature.validate(domValidateContext); | |||
if (valid) { | |||
signer = keySelector.getCertificate(); | |||
} | |||
return valid; | |||
} catch (Exception e) { | |||
LOG.log(POILogger.ERROR, "error in marshalling and validating the signature", e); | |||
return false; | |||
} | |||
} | |||
} | |||
protected static class SignCreationListener implements EventListener, SignatureConfigurable { | |||
ThreadLocal<EventTarget> target = new ThreadLocal<EventTarget>(); | |||
@@ -168,11 +222,10 @@ public class SignatureInfo implements SignatureConfigurable { | |||
} | |||
private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class); | |||
private static boolean isInitialized = false; | |||
public SignatureInfo() { | |||
initXmlProvider(); | |||
} | |||
private SignatureConfig signatureConfig; | |||
public SignatureConfig getSignatureConfig() { | |||
return signatureConfig; | |||
} | |||
@@ -182,10 +235,12 @@ public class SignatureInfo implements SignatureConfigurable { | |||
} | |||
public boolean verifySignature() { | |||
initXmlProvider(); | |||
// http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html | |||
List<X509Certificate> signers = new ArrayList<X509Certificate>(); | |||
return getSignersAndValidate(signers, true); | |||
for (SignaturePart sp : getSignatureParts()){ | |||
// only validate first part | |||
return sp.validate(); | |||
} | |||
return false; | |||
} | |||
public void confirmSignature() | |||
@@ -218,77 +273,50 @@ public class SignatureInfo implements SignatureConfigurable { | |||
} | |||
} | |||
public List<X509Certificate> getSigners() { | |||
initXmlProvider(); | |||
List<X509Certificate> signers = new ArrayList<X509Certificate>(); | |||
getSignersAndValidate(signers, false); | |||
return signers; | |||
} | |||
protected boolean getSignersAndValidate(List<X509Certificate> signers, boolean onlyFirst) { | |||
signatureConfig.init(true); | |||
boolean allValid = true; | |||
List<PackagePart> signatureParts = getSignatureParts(onlyFirst); | |||
if (signatureParts.isEmpty()) { | |||
LOG.log(POILogger.DEBUG, "no signature resources"); | |||
allValid = false; | |||
} | |||
for (PackagePart signaturePart : signatureParts) { | |||
KeyInfoKeySelector keySelector = new KeyInfoKeySelector(); | |||
try { | |||
Document doc = DocumentHelper.readDocument(signaturePart.getInputStream()); | |||
registerIds(doc); | |||
DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc); | |||
domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE); | |||
domValidateContext.setURIDereferencer(signatureConfig.getUriDereferencer()); | |||
XMLSignatureFactory xmlSignatureFactory = getSignatureFactory(); | |||
XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext); | |||
boolean validity = xmlSignature.validate(domValidateContext); | |||
allValid &= validity; | |||
if (!validity) continue; | |||
// TODO: check what has been signed. | |||
} catch (Exception e) { | |||
LOG.log(POILogger.ERROR, "error in marshalling and validating the signature", e); | |||
continue; | |||
} | |||
X509Certificate signer = keySelector.getCertificate(); | |||
signers.add(signer); | |||
} | |||
return allValid; | |||
} | |||
protected List<PackagePart> getSignatureParts(boolean onlyFirst) { | |||
List<PackagePart> packageParts = new ArrayList<PackagePart>(); | |||
OPCPackage pkg = signatureConfig.getOpcPackage(); | |||
PackageRelationshipCollection sigOrigRels = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN); | |||
for (PackageRelationship rel : sigOrigRels) { | |||
PackagePart sigPart = pkg.getPart(rel); | |||
LOG.log(POILogger.DEBUG, "Digital Signature Origin part", sigPart); | |||
try { | |||
PackageRelationshipCollection sigRels = sigPart.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE); | |||
for (PackageRelationship sigRel : sigRels) { | |||
PackagePart sigRelPart = sigPart.getRelatedPart(sigRel); | |||
LOG.log(POILogger.DEBUG, "XML Signature part", sigRelPart); | |||
packageParts.add(sigRelPart); | |||
if (onlyFirst) break; | |||
} | |||
} catch (InvalidFormatException e) { | |||
LOG.log(POILogger.WARN, "Reference to signature is invalid.", e); | |||
public Iterable<SignaturePart> getSignatureParts() { | |||
return new Iterable<SignaturePart>() { | |||
public Iterator<SignaturePart> iterator() { | |||
return new Iterator<SignaturePart>() { | |||
OPCPackage pkg = signatureConfig.getOpcPackage(); | |||
Iterator<PackageRelationship> sigOrigRels = | |||
pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN).iterator(); | |||
Iterator<PackageRelationship> sigRels = null; | |||
PackagePart sigPart = null; | |||
public boolean hasNext() { | |||
while (sigRels == null || !sigRels.hasNext()) { | |||
if (!sigOrigRels.hasNext()) return false; | |||
sigPart = pkg.getPart(sigOrigRels.next()); | |||
LOG.log(POILogger.DEBUG, "Digital Signature Origin part", sigPart); | |||
try { | |||
sigRels = sigPart.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE).iterator(); | |||
} catch (InvalidFormatException e) { | |||
LOG.log(POILogger.WARN, "Reference to signature is invalid.", e); | |||
} | |||
} | |||
return true; | |||
} | |||
public SignaturePart next() { | |||
PackagePart sigRelPart = null; | |||
do { | |||
try { | |||
if (!hasNext()) throw new NoSuchElementException(); | |||
sigRelPart = sigPart.getRelatedPart(sigRels.next()); | |||
LOG.log(POILogger.DEBUG, "XML Signature part", sigRelPart); | |||
} catch (InvalidFormatException e) { | |||
LOG.log(POILogger.WARN, "Reference to signature is invalid.", e); | |||
} | |||
} while (sigPart == null); | |||
return new SignaturePart(sigRelPart); | |||
} | |||
public void remove() { | |||
throw new UnsupportedOperationException(); | |||
} | |||
}; | |||
} | |||
if (onlyFirst && !packageParts.isEmpty()) break; | |||
} | |||
return packageParts; | |||
}; | |||
} | |||
public static XMLSignatureFactory getSignatureFactory() { |
@@ -45,19 +45,16 @@ import java.util.ArrayList; | |||
import java.util.Calendar; | |||
import java.util.Collections; | |||
import java.util.Date; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.TimeZone; | |||
import javax.xml.crypto.KeySelector; | |||
import javax.xml.crypto.dsig.XMLSignature; | |||
import javax.xml.crypto.dsig.XMLSignatureFactory; | |||
import javax.xml.crypto.dsig.dom.DOMValidateContext; | |||
import org.apache.poi.POIDataSamples; | |||
import org.apache.poi.openxml4j.opc.OPCPackage; | |||
import org.apache.poi.openxml4j.opc.PackageAccess; | |||
import org.apache.poi.poifs.crypt.dsig.SignatureConfig; | |||
import org.apache.poi.poifs.crypt.dsig.SignatureInfo; | |||
import org.apache.poi.poifs.crypt.dsig.SignatureInfo.SignaturePart; | |||
import org.apache.poi.poifs.crypt.dsig.facets.EnvelopedSignatureFacet; | |||
import org.apache.poi.poifs.crypt.dsig.facets.KeyInfoSignatureFacet; | |||
import org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet; | |||
@@ -78,6 +75,7 @@ import org.etsi.uri.x01903.v13.DigestAlgAndValueType; | |||
import org.etsi.uri.x01903.v13.QualifyingPropertiesType; | |||
import org.junit.BeforeClass; | |||
import org.junit.Test; | |||
import org.w3.x2000.x09.xmldsig.ReferenceType; | |||
import org.w3.x2000.x09.xmldsig.SignatureDocument; | |||
import org.w3c.dom.Document; | |||
@@ -122,7 +120,12 @@ public class TestSignatureInfo { | |||
sic.setOpcPackage(pkg); | |||
SignatureInfo si = new SignatureInfo(); | |||
si.setSignatureConfig(sic); | |||
List<X509Certificate> result = si.getSigners(); | |||
List<X509Certificate> result = new ArrayList<X509Certificate>(); | |||
for (SignaturePart sp : si.getSignatureParts()) { | |||
if (sp.validate()) { | |||
result.add(sp.getSigner()); | |||
} | |||
} | |||
pkg.revert(); | |||
pkg.close(); | |||
assertNotNull(result); | |||
@@ -151,7 +154,12 @@ public class TestSignatureInfo { | |||
sic.setOpcPackage(pkg); | |||
SignatureInfo si = new SignatureInfo(); | |||
si.setSignatureConfig(sic); | |||
List<X509Certificate> result = si.getSigners(); | |||
List<X509Certificate> result = new ArrayList<X509Certificate>(); | |||
for (SignaturePart sp : si.getSignatureParts()) { | |||
if (sp.validate()) { | |||
result.add(sp.getSigner()); | |||
} | |||
} | |||
assertNotNull(result); | |||
assertEquals("test-file: "+testFile, 1, result.size()); | |||
@@ -172,7 +180,12 @@ public class TestSignatureInfo { | |||
sic.setOpcPackage(pkg); | |||
SignatureInfo si = new SignatureInfo(); | |||
si.setSignatureConfig(sic); | |||
List<X509Certificate> result = si.getSigners(); | |||
List<X509Certificate> result = new ArrayList<X509Certificate>(); | |||
for (SignaturePart sp : si.getSignatureParts()) { | |||
if (sp.validate()) { | |||
result.add(sp.getSigner()); | |||
} | |||
} | |||
assertNotNull(result); | |||
assertEquals("test-file: "+testFile, 2, result.size()); | |||
@@ -207,12 +220,16 @@ public class TestSignatureInfo { | |||
si.setSignatureConfig(sic); | |||
// hash > sha1 doesn't work in excel viewer ... | |||
si.confirmSignature(); | |||
List<X509Certificate> signer = si.getSigners(); | |||
assertEquals(1, signer.size()); | |||
List<X509Certificate> result = new ArrayList<X509Certificate>(); | |||
for (SignaturePart sp : si.getSignatureParts()) { | |||
if (sp.validate()) { | |||
result.add(sp.getSigner()); | |||
} | |||
} | |||
assertEquals(1, result.size()); | |||
pkg.close(); | |||
} | |||
@SuppressWarnings("unused") | |||
@Test | |||
public void testSignEnvelopingDocument() throws Exception { | |||
String testFile = "hello-world-unsigned.xlsx"; | |||
@@ -283,60 +300,45 @@ public class TestSignatureInfo { | |||
}; | |||
signatureConfig.setRevocationDataService(revocationDataService); | |||
// operate | |||
SignatureInfo si = new SignatureInfo(); | |||
si.setSignatureConfig(signatureConfig); | |||
si.confirmSignature(); | |||
Document document = DocumentHelper.createDocument(); | |||
// operate | |||
DigestInfo digestInfo = si.preSign(document, null); | |||
// verify | |||
assertNotNull(digestInfo); | |||
assertEquals(HashAlgorithm.sha1, digestInfo.hashAlgo); | |||
assertNotNull(digestInfo.digestValue); | |||
Iterator<SignaturePart> spIter = si.getSignatureParts().iterator(); | |||
assertTrue(spIter.hasNext()); | |||
SignaturePart sp = spIter.next(); | |||
boolean valid = sp.validate(); | |||
assertTrue(valid); | |||
SignatureDocument sigDoc = SignatureDocument.Factory.parse(document); | |||
String certDigestXQuery = | |||
"declare namespace xades='http://uri.etsi.org/01903/v1.3.2#'; " | |||
+ "declare namespace ds='http://www.w3.org/2000/09/xmldsig#'; " | |||
+ "$this/ds:Signature/ds:Object/xades:QualifyingProperties/xades:SignedProperties/xades:SignedSignatureProperties/xades:SigningCertificate/xades:Cert/xades:CertDigest"; | |||
SignatureDocument sigDoc = sp.getSignatureDocument(); | |||
String declareNS = | |||
"declare namespace xades='http://uri.etsi.org/01903/v1.3.2#'; " | |||
+ "declare namespace ds='http://www.w3.org/2000/09/xmldsig#'; "; | |||
String digestValXQuery = declareNS + | |||
"$this/ds:Signature/ds:SignedInfo/ds:Reference"; | |||
for (ReferenceType rt : (ReferenceType[])sigDoc.selectPath(digestValXQuery)) { | |||
assertNotNull(rt.getDigestValue()); | |||
assertEquals(HashAlgorithm.sha1.xmlSignUri, rt.getDigestMethod().getAlgorithm()); | |||
} | |||
String certDigestXQuery = declareNS + | |||
"$this//xades:SigningCertificate/xades:Cert/xades:CertDigest"; | |||
XmlObject xoList[] = sigDoc.selectPath(certDigestXQuery); | |||
assertEquals(xoList.length, 1); | |||
DigestAlgAndValueType certDigest = (DigestAlgAndValueType)xoList[0]; | |||
assertNotNull(certDigest.getDigestValue()); | |||
// Sign the received XML signature digest value. | |||
byte[] signatureValue = si.signDigest(digestInfo.digestValue); | |||
// Operate: postSign | |||
si.postSign(document, signatureValue); | |||
DOMValidateContext domValidateContext = new DOMValidateContext( | |||
KeySelector.singletonKeySelector(keyPair.getPublic()), | |||
document); | |||
XMLSignatureFactory xmlSignatureFactory = SignatureInfo.getSignatureFactory(); | |||
XMLSignature xmlSignature = xmlSignatureFactory | |||
.unmarshalXMLSignature(domValidateContext); | |||
boolean validity = xmlSignature.validate(domValidateContext); | |||
assertTrue(validity); | |||
sigDoc = SignatureDocument.Factory.parse(document); | |||
xoList = sigDoc.selectPath(certDigestXQuery); | |||
assertEquals(xoList.length, 1); | |||
certDigest = (DigestAlgAndValueType)xoList[0]; | |||
assertNotNull(certDigest.getDigestValue()); | |||
String qualPropXQuery = | |||
"declare namespace xades='http://uri.etsi.org/01903/v1.3.2#'; " | |||
+ "declare namespace ds='http://www.w3.org/2000/09/xmldsig#'; " | |||
+ "$this/ds:Signature/ds:Object/xades:QualifyingProperties"; | |||
String qualPropXQuery = declareNS + | |||
"$this/ds:Signature/ds:Object/xades:QualifyingProperties"; | |||
xoList = sigDoc.selectPath(qualPropXQuery); | |||
assertEquals(xoList.length, 1); | |||
QualifyingPropertiesType qualProp = (QualifyingPropertiesType)xoList[0]; | |||
boolean qualPropXsdOk = qualProp.validate(); | |||
assertTrue(qualPropXsdOk); | |||
pkg.close(); | |||
} | |||
@@ -374,8 +376,13 @@ public class TestSignatureInfo { | |||
// verify: signature | |||
si.getSignatureConfig().setOpcPackage(pkgCopy); | |||
List<X509Certificate> signers = si.getSigners(); | |||
assertEquals(signerCount, signers.size()); | |||
List<X509Certificate> result = new ArrayList<X509Certificate>(); | |||
for (SignaturePart sp : si.getSignatureParts()) { | |||
if (sp.validate()) { | |||
result.add(sp.getSigner()); | |||
} | |||
} | |||
assertEquals(signerCount, result.size()); | |||
return pkgCopy; | |||
} |