git-svn-id: https://svn.apache.org/repos/asf/poi/branches/hemf@1849036 13f79535-47bb-0310-9956-ffa450edef68pull/133/head
@@ -91,7 +91,7 @@ subprojects { | |||
// See https://github.com/melix/japicmp-gradle-plugin | |||
apply plugin: 'me.champeau.gradle.japicmp' | |||
version = '4.0.1-SNAPSHOT' | |||
version = '4.0.2-SNAPSHOT' | |||
ext { | |||
japicmpversion = '4.0.0' | |||
} | |||
@@ -233,6 +233,11 @@ project('ooxml') { | |||
compile 'org.bouncycastle:bcpkix-jdk15on:1.60' | |||
compile 'com.github.virtuald:curvesapi:1.05' | |||
// compile only, don't add it to our dist as it blows up the size | |||
compile 'org.apache.xmlgraphics:batik-all:1.10' | |||
compile 'xml-apis:xml-apis-ext:1.3.04' | |||
compile 'org.apache.xmlgraphics:xmlgraphics-commons:2.3' | |||
// for ooxml-lite, should we move this somewhere else? | |||
compile 'junit:junit:4.12' | |||
@@ -217,6 +217,14 @@ under the License. | |||
<property name="dsig.sl4j-api.jar" location="${compile.lib}/slf4j-api-1.7.25.jar"/> | |||
<property name="dsig.sl4j-api.url" value="${repository.m2}/maven2/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar"/> | |||
<!-- svg/batik libs - not part of the distribution --> | |||
<property name="svg.batik-all.url" value="${repository.m2}/maven2/org/apache/xmlgraphics/batik-all/1.10/batik-all-1.10.jar"/> | |||
<property name="svg.batik-all.jar" value="${compile.lib}/batik-all-1.10.jar"/> | |||
<property name="svg.xml-apis-ext.url" value="${repository.m2}/maven2/xml-apis/xml-apis-ext/1.3.04/xml-apis-ext-1.3.04.jar"/> | |||
<property name="svg.xml-apis-ext.jar" value="${compile.lib}/xml-apis-ext-1.3.04.jar"/> | |||
<property name="svg.xmlgraphics-commons.url" value="${repository.m2}/maven2/org/apache/xmlgraphics/xmlgraphics-commons/2.3/xmlgraphics-commons-2.3.jar"/> | |||
<property name="svg.xmlgraphics-commons.jar" value="${compile.lib}/xmlgraphics-commons-2.3.jar"/> | |||
<!-- jars in the ooxml-lib directory, see the fetch-ooxml-jars target--> | |||
<property name="ooxml.curvesapi.jar" location="${ooxml.lib}/curvesapi-1.05.jar"/> | |||
<property name="ooxml.curvesapi.url" | |||
@@ -395,6 +403,12 @@ under the License. | |||
<pathelement location="${scratchpad.output.dir}" unless:true="${scratchpad.ignore}"/> | |||
</path> | |||
<path id="batik.classpath"> | |||
<pathelement location="${svg.batik-all.jar}"/> | |||
<pathelement location="${svg.xml-apis-ext.jar}"/> | |||
<pathelement location="${svg.xmlgraphics-commons.jar}"/> | |||
</path> | |||
<path id="ooxml-lite.classpath"> | |||
<path refid="ooxml.base.classpath"/> | |||
<!-- instead of ooxml-xsds.jar use the filtered classes--> | |||
@@ -402,11 +416,13 @@ under the License. | |||
<pathelement location="${ooxml.output.dir}"/> | |||
<pathelement location="${ooxml.output.test.dir}"/> | |||
<pathelement location="${main.output.test.dir}"/> | |||
<path refid="batik.classpath"/> | |||
</path> | |||
<path id="ooxml.classpath"> | |||
<pathelement location="${ooxml.xsds.jar}"/> | |||
<path refid="ooxml.base.classpath"/> | |||
<path refid="batik.classpath"/> | |||
</path> | |||
<path id="ooxml.lite.verify.classpath"> | |||
@@ -777,6 +793,9 @@ under the License. | |||
<available file="${ooxml.test.reflections.jar}"/> | |||
<available file="${ooxml.test.guava.jar}"/> | |||
<available file="${ooxml.test.javassist.jar}"/> | |||
<available file="${svg.xml-apis-ext.jar}"/> | |||
<available file="${svg.batik-all.jar}"/> | |||
<available file="${svg.xmlgraphics-commons.jar}"/> | |||
</and> | |||
<isset property="disconnected"/> | |||
</or> | |||
@@ -791,6 +810,9 @@ under the License. | |||
<downloadfile src="${ooxml.test.reflections.url}" dest="${ooxml.test.reflections.jar}"/> | |||
<downloadfile src="${ooxml.test.guava.url}" dest="${ooxml.test.guava.jar}"/> | |||
<downloadfile src="${ooxml.test.javassist.url}" dest="${ooxml.test.javassist.jar}"/> | |||
<downloadfile src="${svg.batik-all.url}" dest="${svg.batik-all.jar}"/> | |||
<downloadfile src="${svg.xml-apis-ext.url}" dest="${svg.xml-apis-ext.jar}"/> | |||
<downloadfile src="${svg.xmlgraphics-commons.url}" dest="${svg.xmlgraphics-commons.jar}"/> | |||
</target> | |||
<target name="check-svn-jars"> | |||
<condition property="svn.jars.present"> | |||
@@ -932,6 +954,7 @@ under the License. | |||
</xmlbean> | |||
<local name="loaderMethod"/> | |||
<!-- the space between "public static" is on purpose to prevent double execution --> | |||
<property name="loaderMethod"><![CDATA[ | |||
private static java.lang.ref.SoftReference<org.apache.xmlbeans.SchemaTypeLoader> typeLoader; | |||
@@ -944,7 +967,7 @@ under the License. | |||
return stl; | |||
} | |||
public static \2 newInstance\(\) \{]]></property> | |||
public static \2 newInstance\(\) \{]]></property> | |||
<replaceregexp byline="true" match="(\s*)public static ([^ ]+) newInstance\(\) \{" replace="${loaderMethod}"> | |||
<fileset dir="${xmlbean.sources.dir}" includes="**/*.java" excludes="**/impl/**"/> |
@@ -70,9 +70,7 @@ def poijobs = [ | |||
'-Djava.locale.providers=JRE,CLDR'], | |||
skipcigame: true | |||
], | |||
[ name: 'POI-DSL-IBM-JDK', jdk: 'IBMJDK', trigger: triggerSundays, | |||
// some OOXML tests fail with strange XML parsing errors and missing JCE unlimited strength requirements | |||
disabled: true, skipcigame: true | |||
[ name: 'POI-DSL-IBM-JDK', jdk: 'IBMJDK', trigger: triggerSundays, skipcigame: true | |||
], | |||
[ name: 'POI-DSL-old-Xerces', trigger: triggerSundays, | |||
shell: "test -f ${xercesLib} || wget -O ${xercesLib} ${xercesUrl}\n", |
@@ -185,5 +185,12 @@ | |||
<version>1.19</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<!-- don't add it to the distribution --> | |||
<groupId>org.apache.xmlgraphics</groupId> | |||
<artifactId>batik-all</artifactId> | |||
<version>1.10</version> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -268,9 +268,7 @@ | |||
</goals> | |||
<configuration> | |||
<target> | |||
<property name="xmlbean.sources.dir" | |||
location="${basedir}/target/generated-sources/xmlbeans"/> | |||
<!-- the space between "public static" is on purpose to prevent double execution --> | |||
<property name="loaderMethod"><![CDATA[ | |||
private static java.lang.ref.SoftReference<org.apache.xmlbeans.SchemaTypeLoader> typeLoader; | |||
@@ -283,25 +281,26 @@ | |||
return stl; | |||
} | |||
public static \2 newInstance\(\) \{]]></property> | |||
public static \2 newInstance\(\) \{]]></property> | |||
<fileset id="xsrc" dir="${basedir}/target/generated-sources/xmlbeans" includes="**/*.java" excludes="**/impl/**"/> | |||
<replaceregexp byline="true" | |||
match="(\s*)public static ([^ ]+) newInstance\(\) \{" | |||
replace="${loaderMethod}"> | |||
<fileset dir="${xmlbean.sources.dir}" includes="**/*.java" | |||
excludes="**/impl/**"/> | |||
<fileset refid="xsrc"/> | |||
</replaceregexp> | |||
<replace dir="${xmlbean.sources.dir}" includes="**/*.java" | |||
excludes="**/impl/**"> | |||
<replace> | |||
<fileset refid="xsrc"/> | |||
<replacetoken>org.apache.xmlbeans.XmlBeans.getContextTypeLoader | |||
</replacetoken> | |||
<replacevalue>getTypeLoader</replacevalue> | |||
</replace> | |||
<!-- remove deprecated warnings, as we prefer the array methods - see #56854 --> | |||
<replace dir="${xmlbean.sources.dir}" includes="**/*.java" | |||
excludes="**/impl/**"> | |||
<replace> | |||
<fileset refid="xsrc"/> | |||
<replacetoken><![CDATA[ * @deprecated | |||
]]></replacetoken> | |||
</replace> |
@@ -211,7 +211,13 @@ public class TestAllFiles { | |||
HANDLERS.put("spreadsheet/BigSSTRecord2CR7", new NullFileHandler()); | |||
HANDLERS.put("spreadsheet/BigSSTRecordCR", new NullFileHandler()); | |||
HANDLERS.put("spreadsheet/test_properties1", new NullFileHandler()); | |||
// keystore files | |||
HANDLERS.put(".pfx", new NullFileHandler()); | |||
HANDLERS.put(".pem", new NullFileHandler()); | |||
HANDLERS.put(".jks", new NullFileHandler()); | |||
HANDLERS.put(".pkcs12", new NullFileHandler()); | |||
Map<String,String> passmap = new HashMap<>(); | |||
passmap.put("slideshow/Password_Protected-hello.ppt", "hello"); | |||
passmap.put("slideshow/Password_Protected-56-hello.ppt", "hello"); |
@@ -46,7 +46,10 @@ public interface PictureData { | |||
/** WordPerfect graphics (.wpg) */ | |||
WPG(-1,12,"image/x-wpg",".wpg"), | |||
/** Microsoft Windows Media Photo image (.wdp) */ | |||
WDP(-1,13,"image/vnd.ms-photo",".wdp"); | |||
WDP(-1,13,"image/vnd.ms-photo",".wdp"), | |||
/** Scalable vector graphics (.svg) - supported by Office 2016 and higher */ | |||
SVG(-1, -1, "image/svg+xml", ".svg") | |||
; | |||
public final int nativeId, ooxmlId; | |||
public final String contentType,extension; |
@@ -0,0 +1,62 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
this work for additional information regarding copyright ownership. | |||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||
(the "License"); you may not use this file except in compliance with | |||
the License. You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.poifs.crypt.dsig; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import org.apache.poi.ooxml.POIXMLDocumentPart; | |||
import org.apache.poi.ooxml.POIXMLRelation; | |||
import org.apache.poi.openxml4j.opc.ContentTypes; | |||
import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; | |||
public class DSigRelation extends POIXMLRelation { | |||
/** | |||
* A map to lookup POIXMLRelation by its relation type | |||
*/ | |||
private static final Map<String, DSigRelation> _table = new HashMap<>(); | |||
public static final DSigRelation ORIGIN_SIGS = new DSigRelation( | |||
ContentTypes.DIGITAL_SIGNATURE_ORIGIN_PART, | |||
PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN, | |||
"/_xmlsignatures/origin.sigs", null | |||
); | |||
public static final DSigRelation SIG = new DSigRelation( | |||
ContentTypes.DIGITAL_SIGNATURE_XML_SIGNATURE_PART, | |||
PackageRelationshipTypes.DIGITAL_SIGNATURE, | |||
"/_xmlsignatures/sig#.xml", null | |||
); | |||
private DSigRelation(String type, String rel, String defaultName, Class<? extends POIXMLDocumentPart> cls) { | |||
super(type, rel, defaultName, cls); | |||
_table.put(rel, this); | |||
} | |||
/** | |||
* Get POIXMLRelation by relation type | |||
* | |||
* @param rel relation type, for example, | |||
* <code>http://schemas.openxmlformats.org/officeDocument/2006/relationships/image</code> | |||
* @return registered POIXMLRelation or null if not found | |||
*/ | |||
public static DSigRelation getInstance(String rel) { | |||
return _table.get(rel); | |||
} | |||
} |
@@ -174,6 +174,13 @@ public class SignatureConfig { | |||
*/ | |||
private boolean updateConfigOnValidate = false; | |||
/** | |||
* if true, the signature is added to the existing signatures | |||
* | |||
* @since POI 4.0.2 | |||
*/ | |||
private boolean allowMultipleSignatures = false; | |||
/** | |||
* Inits and checks the config object. | |||
@@ -1008,4 +1015,25 @@ public class SignatureConfig { | |||
public void setUpdateConfigOnValidate(boolean updateConfigOnValidate) { | |||
this.updateConfigOnValidate = updateConfigOnValidate; | |||
} | |||
/** | |||
* @return true, if multiple signatures can be attached | |||
* | |||
* @since POI 4.0.2 | |||
*/ | |||
public boolean isAllowMultipleSignatures() { | |||
return allowMultipleSignatures; | |||
} | |||
/** | |||
* Activate multiple signatures | |||
* | |||
* @param allowMultipleSignatures if true, the signature will be added, | |||
* otherwise all existing signatures will be replaced by the current | |||
* | |||
* @since POI 4.0.2 | |||
*/ | |||
public void setAllowMultipleSignatures(boolean allowMultipleSignatures) { | |||
this.allowMultipleSignatures = allowMultipleSignatures; | |||
} | |||
} |
@@ -59,7 +59,6 @@ import org.apache.jcp.xml.dsig.internal.dom.DOMSignedInfo; | |||
import org.apache.jcp.xml.dsig.internal.dom.DOMSubTreeData; | |||
import org.apache.poi.EncryptedDocumentException; | |||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; | |||
import org.apache.poi.openxml4j.opc.ContentTypes; | |||
import org.apache.poi.openxml4j.opc.OPCPackage; | |||
import org.apache.poi.openxml4j.opc.PackagePart; | |||
import org.apache.poi.openxml4j.opc.PackagePartName; | |||
@@ -377,9 +376,8 @@ public class SignatureInfo implements SignatureConfigurable { | |||
xmlSignContext.setURIDereferencer(uriDereferencer); | |||
} | |||
for (Map.Entry<String,String> me : signatureConfig.getNamespacePrefixes().entrySet()) { | |||
xmlSignContext.putNamespacePrefix(me.getKey(), me.getValue()); | |||
} | |||
signatureConfig.getNamespacePrefixes().forEach(xmlSignContext::putNamespacePrefix); | |||
xmlSignContext.setDefaultNamespacePrefix(""); | |||
// signatureConfig.getNamespacePrefixes().get(XML_DIGSIG_NS)); | |||
@@ -516,9 +514,7 @@ public class SignatureInfo implements SignatureConfigurable { | |||
protected void writeDocument(Document document) throws MarshalException { | |||
XmlOptions xo = new XmlOptions(); | |||
Map<String,String> namespaceMap = new HashMap<>(); | |||
for(Map.Entry<String,String> entry : signatureConfig.getNamespacePrefixes().entrySet()){ | |||
namespaceMap.put(entry.getValue(), entry.getKey()); | |||
} | |||
signatureConfig.getNamespacePrefixes().forEach((k,v) -> namespaceMap.put(v,k)); | |||
xo.setSaveSuggestedPrefixes(namespaceMap); | |||
xo.setUseDefaultNamespace(); | |||
@@ -530,43 +526,58 @@ public class SignatureInfo implements SignatureConfigurable { | |||
*/ | |||
OPCPackage pkg = signatureConfig.getOpcPackage(); | |||
PackagePartName sigPartName, sigsPartName; | |||
try { | |||
// <Override PartName="/_xmlsignatures/sig1.xml" ContentType="application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"/> | |||
sigPartName = PackagingURIHelper.createPartName("/_xmlsignatures/sig1.xml"); | |||
// <Default Extension="sigs" ContentType="application/vnd.openxmlformats-package.digital-signature-origin"/> | |||
sigsPartName = PackagingURIHelper.createPartName("/_xmlsignatures/origin.sigs"); | |||
} catch (InvalidFormatException e) { | |||
throw new MarshalException(e); | |||
} | |||
final DSigRelation originDesc = DSigRelation.ORIGIN_SIGS; | |||
PackagePartName originPartName = PackagingURIHelper.createPartName(originDesc.getFileName(0)); | |||
PackagePart originPart = pkg.getPart(originPartName); | |||
if (originPart == null) { | |||
// touch empty marker file | |||
originPart = pkg.createPart(originPartName, originDesc.getContentType()); | |||
pkg.addRelationship(originPartName, TargetMode.INTERNAL, originDesc.getRelation()); | |||
} | |||
PackagePart sigPart = pkg.getPart(sigPartName); | |||
if (sigPart == null) { | |||
sigPart = pkg.createPart(sigPartName, ContentTypes.DIGITAL_SIGNATURE_XML_SIGNATURE_PART); | |||
} | |||
// <Override PartName="/_xmlsignatures/sig1.xml" ContentType="application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"/> | |||
final DSigRelation sigDesc = DSigRelation.SIG; | |||
int nextSigIdx = pkg.getUnusedPartIndex(sigDesc.getDefaultFileName()); | |||
if (!signatureConfig.isAllowMultipleSignatures()) { | |||
PackageRelationshipCollection prc = originPart.getRelationshipsByType(sigDesc.getRelation()); | |||
for (int i=2; i<nextSigIdx; i++) { | |||
PackagePartName pn = PackagingURIHelper.createPartName(sigDesc.getFileName(i)); | |||
for (PackageRelationship rel : prc) { | |||
PackagePart pp = originPart.getRelatedPart(rel); | |||
if (pp.getPartName().equals(pn)) { | |||
originPart.removeRelationship(rel.getId()); | |||
prc.removeRelationship(rel.getId()); | |||
break; | |||
} | |||
} | |||
try { | |||
OutputStream os = sigPart.getOutputStream(); | |||
SignatureDocument sigDoc = SignatureDocument.Factory.parse(document, DEFAULT_XML_OPTIONS); | |||
sigDoc.save(os, xo); | |||
os.close(); | |||
} catch (Exception e) { | |||
throw new MarshalException("Unable to write signature document", e); | |||
} | |||
pkg.removePart(pkg.getPart(pn)); | |||
} | |||
nextSigIdx = 1; | |||
} | |||
PackagePart sigsPart = pkg.getPart(sigsPartName); | |||
if (sigsPart == null) { | |||
// touch empty marker file | |||
sigsPart = pkg.createPart(sigsPartName, ContentTypes.DIGITAL_SIGNATURE_ORIGIN_PART); | |||
} | |||
PackageRelationshipCollection relCol = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN); | |||
for (PackageRelationship pr : relCol) { | |||
pkg.removeRelationship(pr.getId()); | |||
} | |||
pkg.addRelationship(sigsPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN); | |||
PackagePartName sigPartName = PackagingURIHelper.createPartName(sigDesc.getFileName(nextSigIdx)); | |||
PackagePart sigPart = pkg.getPart(sigPartName); | |||
if (sigPart == null) { | |||
sigPart = pkg.createPart(sigPartName, sigDesc.getContentType()); | |||
originPart.addRelationship(sigPartName, TargetMode.INTERNAL, sigDesc.getRelation()); | |||
} else { | |||
sigPart.clear(); | |||
} | |||
try (OutputStream os = sigPart.getOutputStream()) { | |||
SignatureDocument sigDoc = SignatureDocument.Factory.parse(document, DEFAULT_XML_OPTIONS); | |||
sigDoc.save(os, xo); | |||
} | |||
sigsPart.addRelationship(sigPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE); | |||
} catch (Exception e) { | |||
throw new MarshalException("Unable to write signature document", e); | |||
} | |||
} | |||
private Element getDsigElement(final Document document, final String localName) { |
@@ -185,9 +185,7 @@ public class SignaturePart { | |||
final Map<String,String> nsMap = new HashMap<>(); | |||
{ | |||
for (Map.Entry<String,String> me : signatureConfig.getNamespacePrefixes().entrySet()) { | |||
nsMap.put(me.getValue(), me.getKey()); | |||
} | |||
signatureConfig.getNamespacePrefixes().forEach((k,v) -> nsMap.put(v,k)); | |||
nsMap.put("dsss", MS_DIGSIG_NS); | |||
nsMap.put("ds", XML_DIGSIG_NS); | |||
} |
@@ -129,10 +129,8 @@ public class KeyInfoSignatureFacet extends SignatureFacet { | |||
DOMSignContext domSignContext = (nextSibling == null) | |||
? new DOMSignContext(key, n) | |||
: new DOMSignContext(key, n, nextSibling); | |||
for (Map.Entry<String,String> me : signatureConfig.getNamespacePrefixes().entrySet()) { | |||
domSignContext.putNamespacePrefix(me.getKey(), me.getValue()); | |||
} | |||
signatureConfig.getNamespacePrefixes().forEach(domSignContext::putNamespacePrefix); | |||
DOMStructure domStructure = new DOMStructure(n); | |||
domKeyInfo.marshal(domStructure, domSignContext); | |||
@@ -0,0 +1,136 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
this work for additional information regarding copyright ownership. | |||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||
(the "License"); you may not use this file except in compliance with | |||
the License. You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.xslf.draw; | |||
import java.awt.Dimension; | |||
import java.awt.Graphics2D; | |||
import java.awt.Insets; | |||
import java.awt.RenderingHints; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.image.BufferedImage; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.lang.ref.WeakReference; | |||
import org.apache.batik.anim.dom.SAXSVGDocumentFactory; | |||
import org.apache.batik.bridge.BridgeContext; | |||
import org.apache.batik.bridge.DocumentLoader; | |||
import org.apache.batik.bridge.GVTBuilder; | |||
import org.apache.batik.bridge.UserAgent; | |||
import org.apache.batik.bridge.UserAgentAdapter; | |||
import org.apache.batik.ext.awt.RenderingHintsKeyExt; | |||
import org.apache.batik.ext.awt.image.renderable.ClipRable8Bit; | |||
import org.apache.batik.gvt.GraphicsNode; | |||
import org.apache.batik.util.XMLResourceDescriptor; | |||
import org.apache.poi.sl.draw.ImageRenderer; | |||
import org.w3c.dom.Document; | |||
public class SVGImageRenderer implements ImageRenderer { | |||
private final GVTBuilder builder = new GVTBuilder(); | |||
private final BridgeContext context; | |||
private final SAXSVGDocumentFactory svgFact; | |||
private GraphicsNode svgRoot; | |||
private double alpha = 1.0; | |||
public SVGImageRenderer() { | |||
String parser = XMLResourceDescriptor.getXMLParserClassName(); | |||
// TOOO: tell the batik guys to use secure parsing feature | |||
svgFact = new SAXSVGDocumentFactory(parser); | |||
UserAgent agent = new UserAgentAdapter(); | |||
DocumentLoader loader = new DocumentLoader(agent); | |||
context = new BridgeContext(agent, loader); | |||
context.setDynamic(true); | |||
} | |||
@Override | |||
public void loadImage(InputStream data, String contentType) throws IOException { | |||
Document document = svgFact.createDocument("", data); | |||
svgRoot = builder.build(context, document); | |||
} | |||
@Override | |||
public void loadImage(byte[] data, String contentType) throws IOException { | |||
loadImage(new ByteArrayInputStream(data), contentType); | |||
} | |||
@Override | |||
public Dimension getDimension() { | |||
Rectangle2D r = svgRoot.getPrimitiveBounds(); | |||
return new Dimension((int)Math.ceil(r.getWidth()), (int)Math.ceil(r.getHeight())); | |||
} | |||
@Override | |||
public void setAlpha(double alpha) { | |||
this.alpha = alpha; | |||
} | |||
@Override | |||
public BufferedImage getImage() { | |||
return getImage(getDimension()); | |||
} | |||
@Override | |||
public BufferedImage getImage(Dimension dim) { | |||
BufferedImage bi = new BufferedImage((int)dim.getWidth(), (int)dim.getHeight(), BufferedImage.TYPE_INT_ARGB); | |||
Graphics2D g2d = (Graphics2D) bi.getGraphics(); | |||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | |||
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); | |||
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); | |||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); | |||
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); | |||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); | |||
g2d.setRenderingHint(RenderingHintsKeyExt.KEY_BUFFERED_IMAGE, new WeakReference(bi)); | |||
Dimension dimSVG = getDimension(); | |||
double scaleX = dim.getWidth() / dimSVG.getWidth(); | |||
double scaleY = dim.getHeight() / dimSVG.getHeight(); | |||
g2d.scale(scaleX, scaleY); | |||
svgRoot.paint(g2d); | |||
g2d.dispose(); | |||
return bi; | |||
} | |||
@Override | |||
public boolean drawImage(Graphics2D graphics, Rectangle2D anchor) { | |||
return drawImage(graphics, anchor, null); | |||
} | |||
@Override | |||
public boolean drawImage(Graphics2D graphics, Rectangle2D anchor, Insets clip) { | |||
if (clip == null) { | |||
svgRoot.setClip(null); | |||
} else { | |||
Rectangle2D clippedRect = new Rectangle2D.Double( | |||
anchor.getX()+clip.left, | |||
anchor.getY()+clip.top, | |||
anchor.getWidth()-(clip.left+clip.right), | |||
anchor.getHeight()-(clip.top+clip.bottom) | |||
); | |||
svgRoot.setClip(new ClipRable8Bit(null, clippedRect)); | |||
} | |||
svgRoot.paint(graphics); | |||
return true; | |||
} | |||
} |
@@ -218,6 +218,8 @@ public final class XSLFPictureData extends POIXMLDocumentPart implements Picture | |||
return PictureType.WDP; | |||
} else if (XSLFRelation.IMAGE_TIFF.getContentType().equals(ct)) { | |||
return PictureType.TIFF; | |||
} else if (XSLFRelation.IMAGE_SVG.getContentType().equals(ct)) { | |||
return PictureType.SVG; | |||
} else { | |||
return null; | |||
} | |||
@@ -237,6 +239,7 @@ public final class XSLFPictureData extends POIXMLDocumentPart implements Picture | |||
case WPG: return XSLFRelation.IMAGE_WPG; | |||
case WDP: return XSLFRelation.IMAGE_WDP; | |||
case TIFF: return XSLFRelation.IMAGE_TIFF; | |||
case SVG: return XSLFRelation.IMAGE_SVG; | |||
default: return null; | |||
} | |||
} |
@@ -19,18 +19,31 @@ | |||
package org.apache.poi.xslf.usermodel; | |||
import static org.apache.poi.openxml4j.opc.PackageRelationshipTypes.CORE_PROPERTIES_ECMA376_NS; | |||
import java.awt.Dimension; | |||
import java.awt.Insets; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.image.BufferedImage; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.net.URI; | |||
import javax.imageio.ImageIO; | |||
import javax.xml.namespace.QName; | |||
import org.apache.poi.openxml4j.opc.PackagePart; | |||
import org.apache.poi.openxml4j.opc.PackageRelationship; | |||
import org.apache.poi.sl.usermodel.PictureData.PictureType; | |||
import org.apache.poi.sl.usermodel.PictureShape; | |||
import org.apache.poi.sl.usermodel.Placeholder; | |||
import org.apache.poi.util.Beta; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
import org.apache.poi.util.Units; | |||
import org.apache.poi.xslf.draw.SVGImageRenderer; | |||
import org.apache.xmlbeans.XmlCursor; | |||
import org.apache.xmlbeans.XmlException; | |||
import org.apache.xmlbeans.XmlObject; | |||
@@ -55,6 +68,11 @@ public class XSLFPictureShape extends XSLFSimpleShape | |||
implements PictureShape<XSLFShape,XSLFTextParagraph> { | |||
private static final POILogger LOG = POILogFactory.getLogger(XSLFPictureShape.class); | |||
private static final String DML_NS = "http://schemas.microsoft.com/office/drawing/2010/main"; | |||
private static final String SVG_NS = "http://schemas.microsoft.com/office/drawing/2016/SVG/main"; | |||
private static final String BITMAP_URI = "{28A0092B-C50C-407E-A947-70E740481C1C}"; | |||
private static final String SVG_URI = "{96DAC541-7B7A-43D3-8B79-37D633B846F1}"; | |||
private XSLFPictureData _data; | |||
/*package*/ XSLFPictureShape(CTPicture shape, XSLFSheet sheet) { | |||
@@ -196,6 +214,97 @@ public class XSLFPictureShape extends XSLFSimpleShape | |||
return (r == null) ? null : new Insets(r.getT(), r.getL(), r.getB(), r.getR()); | |||
} | |||
/** | |||
* Add a SVG image reference | |||
* @param svgPic a previously imported svg image | |||
*/ | |||
public void setSvgImage(XSLFPictureData svgPic) { | |||
CTBlip blip = getBlip(); | |||
CTOfficeArtExtensionList extLst = blip.isSetExtLst() ? blip.getExtLst() : blip.addNewExtLst(); | |||
final int bitmapId = getExt(extLst, BITMAP_URI); | |||
CTOfficeArtExtension extBitmap; | |||
if (bitmapId == -1) { | |||
extBitmap = extLst.addNewExt(); | |||
extBitmap.setUri(BITMAP_URI); | |||
XmlCursor cur = extBitmap.newCursor(); | |||
cur.toEndToken(); | |||
cur.beginElement(new QName(DML_NS, "useLocalDpi", "a14")); | |||
cur.insertNamespace("a14", DML_NS); | |||
cur.insertAttributeWithValue("val", "0"); | |||
cur.dispose(); | |||
} | |||
final int svgId = getExt(extLst, SVG_URI);; | |||
if (svgId != -1) { | |||
extLst.removeExt(svgId); | |||
} | |||
String svgRelId = getSheet().getRelationId(svgPic); | |||
if (svgRelId == null) { | |||
svgRelId = getSheet().addRelation(null, XSLFRelation.IMAGE_SVG, svgPic).getRelationship().getId(); | |||
} | |||
CTOfficeArtExtension svgBitmap = extLst.addNewExt(); | |||
svgBitmap.setUri(SVG_URI); | |||
XmlCursor cur = svgBitmap.newCursor(); | |||
cur.toEndToken(); | |||
cur.beginElement(new QName(SVG_NS, "svgBlip", "asvg")); | |||
cur.insertNamespace("asvg", SVG_NS); | |||
cur.insertAttributeWithValue(new QName(CORE_PROPERTIES_ECMA376_NS, "embed", "rel"), svgRelId); | |||
cur.dispose(); | |||
} | |||
/** | |||
* Convienence method for adding SVG images, which generates the preview image | |||
* @param sheet the sheet to add | |||
* @param svgPic the svg picture to add | |||
* @param previewType the preview picture type or null (defaults to PNG) - currently only JPEG,GIF,PNG are allowed | |||
* @param anchor the image anchor (for calculating the preview image size) or | |||
* null (the preview size is taken from the svg picture bounds) | |||
*/ | |||
public static XSLFPictureShape addSvgImage(XSLFSheet sheet, XSLFPictureData svgPic, PictureType previewType, Rectangle2D anchor) throws IOException { | |||
SVGImageRenderer renderer = new SVGImageRenderer(); | |||
try (InputStream is = svgPic.getInputStream()) { | |||
renderer.loadImage(is, svgPic.getType().contentType); | |||
} | |||
Dimension dim = renderer.getDimension(); | |||
Rectangle2D anc = (anchor != null) ? anchor | |||
: new Rectangle2D.Double(0,0, Units.pixelToPoints((int)dim.getWidth()), Units.pixelToPoints((int)dim.getHeight())); | |||
PictureType pt = (previewType != null) ? previewType : PictureType.PNG; | |||
if (pt != PictureType.JPEG || pt != PictureType.GIF || pt != PictureType.PNG) { | |||
pt = PictureType.PNG; | |||
} | |||
BufferedImage thmBI = renderer.getImage(dim); | |||
ByteArrayOutputStream bos = new ByteArrayOutputStream(100000); | |||
// use extension instead of enum name, because of "jpeg" | |||
ImageIO.write(thmBI, pt.extension.substring(1), bos); | |||
XSLFPictureData pngPic = sheet.getSlideShow().addPicture(new ByteArrayInputStream(bos.toByteArray()), pt); | |||
XSLFPictureShape shape = sheet.createPicture(pngPic); | |||
shape.setAnchor(anc); | |||
shape.setSvgImage(svgPic); | |||
return shape; | |||
} | |||
private int getExt(CTOfficeArtExtensionList extLst, String uri) { | |||
final int size = extLst.sizeOfExtArray(); | |||
for (int i=0; i<size; i++) { | |||
CTOfficeArtExtension ext = extLst.getExtArray(i); | |||
if (uri.equals(ext.getUri())) { | |||
return i; | |||
} | |||
} | |||
return -1; | |||
} | |||
@Override | |||
void copy(XSLFShape sh){ | |||
super.copy(sh); | |||
@@ -219,11 +328,11 @@ public class XSLFPictureShape extends XSLFSimpleShape | |||
nvPr.unsetCustDataLst(); | |||
} | |||
if(blip.isSetExtLst()) { | |||
// TODO: check for SVG copying | |||
CTOfficeArtExtensionList extLst = blip.getExtLst(); | |||
//noinspection deprecation | |||
for(CTOfficeArtExtension ext : extLst.getExtArray()){ | |||
String xpath = "declare namespace a14='http://schemas.microsoft.com/office/drawing/2010/main' $this//a14:imgProps/a14:imgLayer"; | |||
String xpath = "declare namespace a14='"+ DML_NS +"' $this//a14:imgProps/a14:imgLayer"; | |||
XmlObject[] obj = ext.selectPath(xpath); | |||
if(obj != null && obj.length == 1){ | |||
XmlCursor c = obj[0].newCursor(); | |||
@@ -234,6 +343,5 @@ public class XSLFPictureShape extends XSLFSimpleShape | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -16,6 +16,8 @@ | |||
==================================================================== */ | |||
package org.apache.poi.xslf.usermodel; | |||
import static org.apache.poi.openxml4j.opc.PackageRelationshipTypes.IMAGE_PART; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
@@ -159,80 +161,87 @@ public final class XSLFRelation extends POIXMLRelation { | |||
public static final XSLFRelation IMAGE_EMF = new XSLFRelation( | |||
PictureType.EMF.contentType, | |||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", | |||
IMAGE_PART, | |||
"/ppt/media/image#.emf", | |||
XSLFPictureData.class | |||
); | |||
public static final XSLFRelation IMAGE_WMF = new XSLFRelation( | |||
PictureType.WMF.contentType, | |||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", | |||
IMAGE_PART, | |||
"/ppt/media/image#.wmf", | |||
XSLFPictureData.class | |||
); | |||
public static final XSLFRelation IMAGE_PICT = new XSLFRelation( | |||
PictureType.PICT.contentType, | |||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", | |||
IMAGE_PART, | |||
"/ppt/media/image#.pict", | |||
XSLFPictureData.class | |||
); | |||
public static final XSLFRelation IMAGE_JPEG = new XSLFRelation( | |||
PictureType.JPEG.contentType, | |||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", | |||
IMAGE_PART, | |||
"/ppt/media/image#.jpeg", | |||
XSLFPictureData.class | |||
); | |||
public static final XSLFRelation IMAGE_PNG = new XSLFRelation( | |||
PictureType.PNG.contentType, | |||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", | |||
IMAGE_PART, | |||
"/ppt/media/image#.png", | |||
XSLFPictureData.class | |||
); | |||
public static final XSLFRelation IMAGE_DIB = new XSLFRelation( | |||
PictureType.DIB.contentType, | |||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", | |||
IMAGE_PART, | |||
"/ppt/media/image#.dib", | |||
XSLFPictureData.class | |||
); | |||
public static final XSLFRelation IMAGE_GIF = new XSLFRelation( | |||
PictureType.GIF.contentType, | |||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", | |||
IMAGE_PART, | |||
"/ppt/media/image#.gif", | |||
XSLFPictureData.class | |||
); | |||
public static final XSLFRelation IMAGE_TIFF = new XSLFRelation( | |||
PictureType.TIFF.contentType, | |||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", | |||
IMAGE_PART, | |||
"/ppt/media/image#.tiff", | |||
XSLFPictureData.class | |||
); | |||
public static final XSLFRelation IMAGE_EPS = new XSLFRelation( | |||
PictureType.EPS.contentType, | |||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", | |||
IMAGE_PART, | |||
"/ppt/media/image#.eps", | |||
XSLFPictureData.class | |||
); | |||
public static final XSLFRelation IMAGE_BMP = new XSLFRelation( | |||
PictureType.BMP.contentType, | |||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", | |||
IMAGE_PART, | |||
"/ppt/media/image#.bmp", | |||
XSLFPictureData.class | |||
); | |||
public static final XSLFRelation IMAGE_WPG = new XSLFRelation( | |||
PictureType.WPG.contentType, | |||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", | |||
IMAGE_PART, | |||
"/ppt/media/image#.wpg", | |||
XSLFPictureData.class | |||
); | |||
public static final XSLFRelation IMAGE_WDP = new XSLFRelation( | |||
PictureType.WDP.contentType, | |||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", | |||
IMAGE_PART, | |||
"/ppt/media/image#.wdp", | |||
XSLFPictureData.class | |||
); | |||
public static final XSLFRelation IMAGE_SVG = new XSLFRelation( | |||
PictureType.SVG.contentType, | |||
IMAGE_PART, | |||
"/ppt/media/image#.svg", | |||
XSLFPictureData.class | |||
); | |||
public static final XSLFRelation IMAGES = new XSLFRelation( | |||
null, | |||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", | |||
IMAGE_PART, | |||
null, | |||
XSLFPictureData.class | |||
); |
@@ -1,314 +0,0 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
this work for additional information regarding copyright ownership. | |||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||
(the "License"); you may not use this file except in compliance with | |||
the License. You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.poifs.crypt; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.StringWriter; | |||
import java.math.BigInteger; | |||
import java.security.KeyPair; | |||
import java.security.KeyPairGenerator; | |||
import java.security.PrivateKey; | |||
import java.security.PublicKey; | |||
import java.security.SecureRandom; | |||
import java.security.cert.CRLException; | |||
import java.security.cert.CertificateEncodingException; | |||
import java.security.cert.CertificateException; | |||
import java.security.cert.X509CRL; | |||
import java.security.cert.X509Certificate; | |||
import java.security.interfaces.RSAPublicKey; | |||
import java.security.spec.RSAKeyGenParameterSpec; | |||
import java.util.Date; | |||
import javax.xml.parsers.DocumentBuilder; | |||
import javax.xml.parsers.DocumentBuilderFactory; | |||
import javax.xml.parsers.ParserConfigurationException; | |||
import javax.xml.transform.OutputKeys; | |||
import javax.xml.transform.Result; | |||
import javax.xml.transform.Source; | |||
import javax.xml.transform.Transformer; | |||
import javax.xml.transform.TransformerException; | |||
import javax.xml.transform.TransformerFactory; | |||
import javax.xml.transform.dom.DOMSource; | |||
import javax.xml.transform.stream.StreamResult; | |||
import org.bouncycastle.asn1.DERIA5String; | |||
import org.bouncycastle.asn1.DEROctetString; | |||
import org.bouncycastle.asn1.DERSequence; | |||
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers; | |||
import org.bouncycastle.asn1.x500.X500Name; | |||
import org.bouncycastle.asn1.x509.AuthorityInformationAccess; | |||
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; | |||
import org.bouncycastle.asn1.x509.BasicConstraints; | |||
import org.bouncycastle.asn1.x509.CRLNumber; | |||
import org.bouncycastle.asn1.x509.CRLReason; | |||
import org.bouncycastle.asn1.x509.DistributionPoint; | |||
import org.bouncycastle.asn1.x509.DistributionPointName; | |||
import org.bouncycastle.asn1.x509.Extension; | |||
import org.bouncycastle.asn1.x509.Extensions; | |||
import org.bouncycastle.asn1.x509.GeneralName; | |||
import org.bouncycastle.asn1.x509.GeneralNames; | |||
import org.bouncycastle.asn1.x509.KeyUsage; | |||
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; | |||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; | |||
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; | |||
import org.bouncycastle.cert.X509CRLHolder; | |||
import org.bouncycastle.cert.X509CertificateHolder; | |||
import org.bouncycastle.cert.X509ExtensionUtils; | |||
import org.bouncycastle.cert.X509v2CRLBuilder; | |||
import org.bouncycastle.cert.X509v3CertificateBuilder; | |||
import org.bouncycastle.cert.jcajce.JcaX509CRLConverter; | |||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; | |||
import org.bouncycastle.cert.ocsp.BasicOCSPResp; | |||
import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder; | |||
import org.bouncycastle.cert.ocsp.CertificateID; | |||
import org.bouncycastle.cert.ocsp.CertificateStatus; | |||
import org.bouncycastle.cert.ocsp.OCSPReq; | |||
import org.bouncycastle.cert.ocsp.OCSPReqBuilder; | |||
import org.bouncycastle.cert.ocsp.OCSPResp; | |||
import org.bouncycastle.cert.ocsp.OCSPRespBuilder; | |||
import org.bouncycastle.cert.ocsp.Req; | |||
import org.bouncycastle.cert.ocsp.RevokedStatus; | |||
import org.bouncycastle.crypto.params.RSAKeyParameters; | |||
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; | |||
import org.bouncycastle.operator.ContentSigner; | |||
import org.bouncycastle.operator.DigestCalculator; | |||
import org.bouncycastle.operator.OperatorCreationException; | |||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; | |||
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Node; | |||
import org.xml.sax.InputSource; | |||
import org.xml.sax.SAXException; | |||
public class PkiTestUtils { | |||
private PkiTestUtils() { | |||
super(); | |||
} | |||
static KeyPair generateKeyPair() throws Exception { | |||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); | |||
SecureRandom random = new SecureRandom(); | |||
keyPairGenerator.initialize(new RSAKeyGenParameterSpec(1024, | |||
RSAKeyGenParameterSpec.F4), random); | |||
return keyPairGenerator.generateKeyPair(); | |||
} | |||
static X509Certificate generateCertificate(PublicKey subjectPublicKey, | |||
String subjectDn, Date notBefore, Date notAfter, | |||
X509Certificate issuerCertificate, PrivateKey issuerPrivateKey, | |||
boolean caFlag, int pathLength, String crlUri, String ocspUri, | |||
KeyUsage keyUsage) | |||
throws IOException, OperatorCreationException, CertificateException | |||
{ | |||
String signatureAlgorithm = "SHA1withRSA"; | |||
X500Name issuerName; | |||
if (issuerCertificate != null) { | |||
issuerName = new X509CertificateHolder(issuerCertificate.getEncoded()).getIssuer(); | |||
} else { | |||
issuerName = new X500Name(subjectDn); | |||
} | |||
RSAPublicKey rsaPubKey = (RSAPublicKey)subjectPublicKey; | |||
RSAKeyParameters rsaSpec = new RSAKeyParameters(false, rsaPubKey.getModulus(), rsaPubKey.getPublicExponent()); | |||
SubjectPublicKeyInfo subjectPublicKeyInfo = | |||
SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(rsaSpec); | |||
DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder() | |||
.setProvider("BC").build().get(CertificateID.HASH_SHA1); | |||
X509v3CertificateBuilder certificateGenerator = new X509v3CertificateBuilder( | |||
issuerName | |||
, new BigInteger(128, new SecureRandom()) | |||
, notBefore | |||
, notAfter | |||
, new X500Name(subjectDn) | |||
, subjectPublicKeyInfo | |||
); | |||
X509ExtensionUtils exUtils = new X509ExtensionUtils(digestCalc); | |||
SubjectKeyIdentifier subKeyId = exUtils.createSubjectKeyIdentifier(subjectPublicKeyInfo); | |||
AuthorityKeyIdentifier autKeyId = (issuerCertificate != null) | |||
? exUtils.createAuthorityKeyIdentifier(new X509CertificateHolder(issuerCertificate.getEncoded())) | |||
: exUtils.createAuthorityKeyIdentifier(subjectPublicKeyInfo); | |||
certificateGenerator.addExtension(Extension.subjectKeyIdentifier, false, subKeyId); | |||
certificateGenerator.addExtension(Extension.authorityKeyIdentifier, false, autKeyId); | |||
if (caFlag) { | |||
BasicConstraints bc; | |||
if (-1 == pathLength) { | |||
bc = new BasicConstraints(true); | |||
} else { | |||
bc = new BasicConstraints(pathLength); | |||
} | |||
certificateGenerator.addExtension(Extension.basicConstraints, false, bc); | |||
} | |||
if (null != crlUri) { | |||
int uri = GeneralName.uniformResourceIdentifier; | |||
DERIA5String crlUriDer = new DERIA5String(crlUri); | |||
GeneralName gn = new GeneralName(uri, crlUriDer); | |||
DERSequence gnDer = new DERSequence(gn); | |||
GeneralNames gns = GeneralNames.getInstance(gnDer); | |||
DistributionPointName dpn = new DistributionPointName(0, gns); | |||
DistributionPoint distp = new DistributionPoint(dpn, null, null); | |||
DERSequence distpDer = new DERSequence(distp); | |||
certificateGenerator.addExtension(Extension.cRLDistributionPoints, false, distpDer); | |||
} | |||
if (null != ocspUri) { | |||
int uri = GeneralName.uniformResourceIdentifier; | |||
GeneralName ocspName = new GeneralName(uri, ocspUri); | |||
AuthorityInformationAccess authorityInformationAccess = | |||
new AuthorityInformationAccess(X509ObjectIdentifiers.ocspAccessMethod, ocspName); | |||
certificateGenerator.addExtension(Extension.authorityInfoAccess, false, authorityInformationAccess); | |||
} | |||
if (null != keyUsage) { | |||
certificateGenerator.addExtension(Extension.keyUsage, true, keyUsage); | |||
} | |||
JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(signatureAlgorithm); | |||
signerBuilder.setProvider("BC"); | |||
X509CertificateHolder certHolder = | |||
certificateGenerator.build(signerBuilder.build(issuerPrivateKey)); | |||
/* | |||
* Next certificate factory trick is needed to make sure that the | |||
* certificate delivered to the caller is provided by the default | |||
* security provider instead of BouncyCastle. If we don't do this trick | |||
* we might run into trouble when trying to use the CertPath validator. | |||
*/ | |||
// CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); | |||
// certificate = (X509Certificate) certificateFactory | |||
// .generateCertificate(new ByteArrayInputStream(certificate | |||
// .getEncoded())); | |||
return new JcaX509CertificateConverter().getCertificate(certHolder); | |||
} | |||
static Document loadDocument(InputStream documentInputStream) | |||
throws ParserConfigurationException, SAXException, IOException { | |||
InputSource inputSource = new InputSource(documentInputStream); | |||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory | |||
.newInstance(); | |||
documentBuilderFactory.setNamespaceAware(true); | |||
DocumentBuilder documentBuilder = documentBuilderFactory | |||
.newDocumentBuilder(); | |||
return documentBuilder.parse(inputSource); | |||
} | |||
static String toString(Node dom) throws TransformerException { | |||
Source source = new DOMSource(dom); | |||
StringWriter stringWriter = new StringWriter(); | |||
Result result = new StreamResult(stringWriter); | |||
TransformerFactory transformerFactory = TransformerFactory | |||
.newInstance(); | |||
Transformer transformer = transformerFactory.newTransformer(); | |||
/* | |||
* We have to omit the ?xml declaration if we want to embed the | |||
* document. | |||
*/ | |||
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); | |||
transformer.transform(source, result); | |||
return stringWriter.getBuffer().toString(); | |||
} | |||
public static X509CRL generateCrl(X509Certificate issuer, PrivateKey issuerPrivateKey) | |||
throws CertificateEncodingException, IOException, CRLException, OperatorCreationException { | |||
X509CertificateHolder holder = new X509CertificateHolder(issuer.getEncoded()); | |||
X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(holder.getIssuer(), new Date()); | |||
crlBuilder.setNextUpdate(new Date(new Date().getTime() + 100000)); | |||
JcaContentSignerBuilder contentBuilder = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC"); | |||
CRLNumber crlNumber = new CRLNumber(new BigInteger("1234")); | |||
crlBuilder.addExtension(Extension.cRLNumber, false, crlNumber); | |||
X509CRLHolder x509Crl = crlBuilder.build(contentBuilder.build(issuerPrivateKey)); | |||
return new JcaX509CRLConverter().setProvider("BC").getCRL(x509Crl); | |||
} | |||
public static OCSPResp createOcspResp(X509Certificate certificate, | |||
boolean revoked, X509Certificate issuerCertificate, | |||
X509Certificate ocspResponderCertificate, | |||
PrivateKey ocspResponderPrivateKey, String signatureAlgorithm, | |||
long nonceTimeinMillis) | |||
throws Exception { | |||
DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder() | |||
.setProvider("BC").build().get(CertificateID.HASH_SHA1); | |||
X509CertificateHolder issuerHolder = new X509CertificateHolder(issuerCertificate.getEncoded()); | |||
CertificateID certId = new CertificateID(digestCalc, issuerHolder, certificate.getSerialNumber()); | |||
// request | |||
//create a nonce to avoid replay attack | |||
BigInteger nonce = BigInteger.valueOf(nonceTimeinMillis); | |||
DEROctetString nonceDer = new DEROctetString(nonce.toByteArray()); | |||
Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, true, nonceDer); | |||
Extensions exts = new Extensions(ext); | |||
OCSPReqBuilder ocspReqBuilder = new OCSPReqBuilder(); | |||
ocspReqBuilder.addRequest(certId); | |||
ocspReqBuilder.setRequestExtensions(exts); | |||
OCSPReq ocspReq = ocspReqBuilder.build(); | |||
SubjectPublicKeyInfo keyInfo = new SubjectPublicKeyInfo | |||
(CertificateID.HASH_SHA1, ocspResponderCertificate.getPublicKey().getEncoded()); | |||
BasicOCSPRespBuilder basicOCSPRespBuilder = new BasicOCSPRespBuilder(keyInfo, digestCalc); | |||
basicOCSPRespBuilder.setResponseExtensions(exts); | |||
// request processing | |||
Req[] requestList = ocspReq.getRequestList(); | |||
for (Req ocspRequest : requestList) { | |||
CertificateID certificateID = ocspRequest.getCertID(); | |||
CertificateStatus certificateStatus = CertificateStatus.GOOD; | |||
if (revoked) { | |||
certificateStatus = new RevokedStatus(new Date(), CRLReason.privilegeWithdrawn); | |||
} | |||
basicOCSPRespBuilder.addResponse(certificateID, certificateStatus); | |||
} | |||
// basic response generation | |||
X509CertificateHolder[] chain = null; | |||
if (!ocspResponderCertificate.equals(issuerCertificate)) { | |||
// TODO: HorribleProxy can't convert array input params yet | |||
chain = new X509CertificateHolder[] { | |||
new X509CertificateHolder(ocspResponderCertificate.getEncoded()), | |||
issuerHolder | |||
}; | |||
} | |||
ContentSigner contentSigner = new JcaContentSignerBuilder("SHA1withRSA") | |||
.setProvider("BC").build(ocspResponderPrivateKey); | |||
BasicOCSPResp basicOCSPResp = basicOCSPRespBuilder.build(contentSigner, chain, new Date(nonceTimeinMillis)); | |||
OCSPRespBuilder ocspRespBuilder = new OCSPRespBuilder(); | |||
return ocspRespBuilder.build(OCSPRespBuilder.SUCCESSFUL, basicOCSPResp); | |||
} | |||
} |
@@ -29,6 +29,7 @@ import static org.junit.Assert.assertNotNull; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.junit.Assume.assumeTrue; | |||
import java.io.BufferedReader; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
@@ -36,19 +37,30 @@ import java.io.FileInputStream; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.InputStreamReader; | |||
import java.io.OutputStream; | |||
import java.math.BigInteger; | |||
import java.net.ConnectException; | |||
import java.net.HttpURLConnection; | |||
import java.net.MalformedURLException; | |||
import java.net.SocketTimeoutException; | |||
import java.net.URL; | |||
import java.nio.charset.StandardCharsets; | |||
import java.security.Key; | |||
import java.security.KeyPair; | |||
import java.security.KeyPairGenerator; | |||
import java.security.KeyStore; | |||
import java.security.PrivateKey; | |||
import java.security.PublicKey; | |||
import java.security.SecureRandom; | |||
import java.security.cert.CRLException; | |||
import java.security.cert.Certificate; | |||
import java.security.cert.CertificateEncodingException; | |||
import java.security.cert.CertificateException; | |||
import java.security.cert.X509CRL; | |||
import java.security.cert.X509Certificate; | |||
import java.security.interfaces.RSAPublicKey; | |||
import java.security.spec.RSAKeyGenParameterSpec; | |||
import java.util.ArrayList; | |||
import java.util.Calendar; | |||
import java.util.Collections; | |||
@@ -56,7 +68,9 @@ import java.util.Date; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import javax.xml.crypto.MarshalException; | |||
import javax.xml.crypto.dsig.CanonicalizationMethod; | |||
import javax.xml.crypto.dsig.XMLSignatureException; | |||
import javax.xml.crypto.dsig.dom.DOMSignContext; | |||
import org.apache.jcp.xml.dsig.internal.dom.DOMSignedInfo; | |||
@@ -90,8 +104,53 @@ import org.apache.poi.xssf.streaming.SXSSFWorkbook; | |||
import org.apache.poi.xssf.usermodel.XSSFWorkbook; | |||
import org.apache.xmlbeans.SystemProperties; | |||
import org.apache.xmlbeans.XmlObject; | |||
import org.bouncycastle.asn1.DERIA5String; | |||
import org.bouncycastle.asn1.DEROctetString; | |||
import org.bouncycastle.asn1.DERSequence; | |||
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers; | |||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; | |||
import org.bouncycastle.asn1.x500.X500Name; | |||
import org.bouncycastle.asn1.x509.AuthorityInformationAccess; | |||
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; | |||
import org.bouncycastle.asn1.x509.BasicConstraints; | |||
import org.bouncycastle.asn1.x509.CRLNumber; | |||
import org.bouncycastle.asn1.x509.CRLReason; | |||
import org.bouncycastle.asn1.x509.DistributionPoint; | |||
import org.bouncycastle.asn1.x509.DistributionPointName; | |||
import org.bouncycastle.asn1.x509.Extension; | |||
import org.bouncycastle.asn1.x509.Extensions; | |||
import org.bouncycastle.asn1.x509.GeneralName; | |||
import org.bouncycastle.asn1.x509.GeneralNames; | |||
import org.bouncycastle.asn1.x509.KeyUsage; | |||
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; | |||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; | |||
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; | |||
import org.bouncycastle.cert.X509CRLHolder; | |||
import org.bouncycastle.cert.X509CertificateHolder; | |||
import org.bouncycastle.cert.X509ExtensionUtils; | |||
import org.bouncycastle.cert.X509v2CRLBuilder; | |||
import org.bouncycastle.cert.X509v3CertificateBuilder; | |||
import org.bouncycastle.cert.jcajce.JcaX509CRLConverter; | |||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; | |||
import org.bouncycastle.cert.ocsp.BasicOCSPResp; | |||
import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder; | |||
import org.bouncycastle.cert.ocsp.CertificateID; | |||
import org.bouncycastle.cert.ocsp.CertificateStatus; | |||
import org.bouncycastle.cert.ocsp.OCSPReq; | |||
import org.bouncycastle.cert.ocsp.OCSPReqBuilder; | |||
import org.bouncycastle.cert.ocsp.OCSPResp; | |||
import org.bouncycastle.cert.ocsp.OCSPRespBuilder; | |||
import org.bouncycastle.cert.ocsp.Req; | |||
import org.bouncycastle.cert.ocsp.RevokedStatus; | |||
import org.bouncycastle.crypto.params.RSAKeyParameters; | |||
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; | |||
import org.bouncycastle.openssl.PEMParser; | |||
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; | |||
import org.bouncycastle.operator.ContentSigner; | |||
import org.bouncycastle.operator.DigestCalculator; | |||
import org.bouncycastle.operator.OperatorCreationException; | |||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; | |||
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; | |||
import org.etsi.uri.x01903.v13.DigestAlgAndValueType; | |||
import org.etsi.uri.x01903.v13.QualifyingPropertiesType; | |||
import org.junit.AfterClass; | |||
@@ -408,7 +467,7 @@ public class TestSignatureInfo { | |||
try (OPCPackage pkg = OPCPackage.open(copy(sigCopy), PackageAccess.READ_WRITE)) { | |||
initKeyPair("Test", "CN=Test"); | |||
final X509CRL crl = PkiTestUtils.generateCrl(x509, keyPair.getPrivate()); | |||
final X509CRL crl = generateCrl(x509, keyPair.getPrivate()); | |||
// setup | |||
SignatureConfig signatureConfig = new SignatureConfig(); | |||
@@ -481,7 +540,7 @@ public class TestSignatureInfo { | |||
final RevocationData revocationData = new RevocationData(); | |||
revocationData.addCRL(crl); | |||
OCSPResp ocspResp = PkiTestUtils.createOcspResp(x509, false, | |||
OCSPResp ocspResp = createOcspResp(x509, false, | |||
x509, x509, keyPair.getPrivate(), "SHA1withRSA", cal.getTimeInMillis()); | |||
revocationData.addOCSP(ocspResp.getEncoded()); | |||
@@ -721,18 +780,84 @@ public class TestSignatureInfo { | |||
@Test | |||
public void testMultiSign() throws Exception { | |||
initKeyPair("KeyA", "CN=KeyA"); | |||
//KeyPair keyPairA = keyPair; | |||
//X509Certificate x509A = x509; | |||
initKeyPair("KeyB", "CN=KeyB"); | |||
//KeyPair keyPairB = keyPair; | |||
//X509Certificate x509B = x509; | |||
File tpl = copy(testdata.getFile("bug58630.xlsx")); | |||
cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC); | |||
cal.clear(); | |||
cal.setTimeZone(LocaleUtil.TIMEZONE_UTC); | |||
cal.set(2018, Calendar.DECEMBER, 14); | |||
// test signing with separate opened packages | |||
File tpl = copy(testdata.getFile("hello-world-unsigned.xlsx")); | |||
try (OPCPackage pkg = OPCPackage.open(tpl)) { | |||
signPkg63011(pkg, "bug63011_key1.pem", true); | |||
} | |||
try (OPCPackage pkg = OPCPackage.open(tpl)) { | |||
signPkg63011(pkg, "bug63011_key2.pem", true); | |||
} | |||
verifyPkg63011(tpl, true); | |||
// test signing with single opened package | |||
tpl = copy(testdata.getFile("hello-world-unsigned.xlsx")); | |||
try (OPCPackage pkg = OPCPackage.open(tpl)) { | |||
signPkg63011(pkg, "bug63011_key1.pem", true); | |||
signPkg63011(pkg, "bug63011_key2.pem", true); | |||
} | |||
verifyPkg63011(tpl, true); | |||
try (OPCPackage pkg = OPCPackage.open(tpl)) { | |||
//SignatureConfig signatureConfig = new SignatureConfig(); | |||
assertNotNull(pkg); | |||
signPkg63011(pkg, "bug63011_key1.pem", true); | |||
signPkg63011(pkg, "bug63011_key2.pem", false); | |||
} | |||
verifyPkg63011(tpl, false); | |||
} | |||
private void verifyPkg63011(File tpl, boolean multi) throws InvalidFormatException, IOException { | |||
try (OPCPackage pkg = OPCPackage.open(tpl, PackageAccess.READ)) { | |||
SignatureConfig sic = new SignatureConfig(); | |||
sic.setOpcPackage(pkg); | |||
SignatureInfo si = new SignatureInfo(); | |||
si.setSignatureConfig(sic); | |||
List<X509Certificate> result = new ArrayList<>(); | |||
for (SignaturePart sp : si.getSignatureParts()) { | |||
if (sp.validate()) { | |||
result.add(sp.getSigner()); | |||
} | |||
} | |||
assertNotNull(result); | |||
if (multi) { | |||
assertEquals(2, result.size()); | |||
assertEquals("CN=Muj Klic", result.get(0).getSubjectDN().toString()); | |||
assertEquals("CN=My Second key", result.get(1).getSubjectDN().toString()); | |||
} else { | |||
assertEquals(1, result.size()); | |||
assertEquals("CN=My Second key", result.get(0).getSubjectDN().toString()); | |||
} | |||
assertTrue(si.verifySignature()); | |||
pkg.revert(); | |||
} | |||
} | |||
private void signPkg63011(OPCPackage pkg, String pemFile, boolean multi) | |||
throws IOException, CertificateException, XMLSignatureException, MarshalException { | |||
assertNotNull(pkg); | |||
initKeyFromPEM(testdata.getFile(pemFile)); | |||
SignatureConfig config = new SignatureConfig(); | |||
config.setKey(keyPair.getPrivate()); | |||
config.setSigningCertificateChain(Collections.singletonList(x509)); | |||
config.setExecutionTime(cal.getTime()); | |||
config.setAllowMultipleSignatures(multi); | |||
config.setOpcPackage(pkg); | |||
SignatureInfo si = new SignatureInfo(); | |||
si.setSignatureConfig(config); | |||
si.confirmSignature(); | |||
} | |||
@Test | |||
@@ -829,14 +954,14 @@ public class TestSignatureInfo { | |||
x509 = (X509Certificate)keystore.getCertificate(alias); | |||
keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key); | |||
} else { | |||
keyPair = PkiTestUtils.generateKeyPair(); | |||
keyPair = generateKeyPair(); | |||
Date notBefore = cal.getTime(); | |||
Calendar cal2 = (Calendar)cal.clone(); | |||
cal2.add(Calendar.YEAR, 1); | |||
Date notAfter = cal2.getTime(); | |||
KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature); | |||
x509 = PkiTestUtils.generateCertificate(keyPair.getPublic(), subjectDN | |||
x509 = generateCertificate(keyPair.getPublic(), subjectDN | |||
, notBefore, notAfter, null, keyPair.getPrivate(), true, 0, null, null, keyUsage); | |||
keystore.setKeyEntry(alias, keyPair.getPrivate(), password, new Certificate[]{x509}); | |||
@@ -849,6 +974,27 @@ public class TestSignatureInfo { | |||
} | |||
} | |||
private void initKeyFromPEM(File pemFile) throws IOException, CertificateException { | |||
// see https://stackoverflow.com/questions/11787571/how-to-read-pem-file-to-get-private-and-public-key | |||
PrivateKey key = null; | |||
x509 = null; | |||
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(pemFile), StandardCharsets.ISO_8859_1))) { | |||
PEMParser parser = new PEMParser(br); | |||
for (Object obj; (obj = parser.readObject()) != null; ) { | |||
if (obj instanceof PrivateKeyInfo) { | |||
key = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey((PrivateKeyInfo)obj); | |||
} else if (obj instanceof X509CertificateHolder) { | |||
x509 = new JcaX509CertificateConverter().setProvider("BC").getCertificate((X509CertificateHolder)obj); | |||
} | |||
} | |||
} | |||
if (key != null && x509 != null) { | |||
keyPair = new KeyPair(x509.getPublicKey(), key); | |||
} | |||
} | |||
private static File copy(File input) throws IOException { | |||
String extension = input.getName().replaceAll(".*?(\\.[^.]+)?$", "$1"); | |||
if (extension == null || extension.isEmpty()) { | |||
@@ -872,4 +1018,187 @@ public class TestSignatureInfo { | |||
return tmpFile; | |||
} | |||
private static KeyPair generateKeyPair() throws Exception { | |||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); | |||
SecureRandom random = new SecureRandom(); | |||
keyPairGenerator.initialize(new RSAKeyGenParameterSpec(1024, | |||
RSAKeyGenParameterSpec.F4), random); | |||
return keyPairGenerator.generateKeyPair(); | |||
} | |||
private static X509Certificate generateCertificate(PublicKey subjectPublicKey, | |||
String subjectDn, Date notBefore, Date notAfter, | |||
X509Certificate issuerCertificate, PrivateKey issuerPrivateKey, | |||
boolean caFlag, int pathLength, String crlUri, String ocspUri, | |||
KeyUsage keyUsage) | |||
throws IOException, OperatorCreationException, CertificateException | |||
{ | |||
String signatureAlgorithm = "SHA1withRSA"; | |||
X500Name issuerName; | |||
if (issuerCertificate != null) { | |||
issuerName = new X509CertificateHolder(issuerCertificate.getEncoded()).getIssuer(); | |||
} else { | |||
issuerName = new X500Name(subjectDn); | |||
} | |||
RSAPublicKey rsaPubKey = (RSAPublicKey)subjectPublicKey; | |||
RSAKeyParameters rsaSpec = new RSAKeyParameters(false, rsaPubKey.getModulus(), rsaPubKey.getPublicExponent()); | |||
SubjectPublicKeyInfo subjectPublicKeyInfo = | |||
SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(rsaSpec); | |||
DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder() | |||
.setProvider("BC").build().get(CertificateID.HASH_SHA1); | |||
X509v3CertificateBuilder certificateGenerator = new X509v3CertificateBuilder( | |||
issuerName | |||
, new BigInteger(128, new SecureRandom()) | |||
, notBefore | |||
, notAfter | |||
, new X500Name(subjectDn) | |||
, subjectPublicKeyInfo | |||
); | |||
X509ExtensionUtils exUtils = new X509ExtensionUtils(digestCalc); | |||
SubjectKeyIdentifier subKeyId = exUtils.createSubjectKeyIdentifier(subjectPublicKeyInfo); | |||
AuthorityKeyIdentifier autKeyId = (issuerCertificate != null) | |||
? exUtils.createAuthorityKeyIdentifier(new X509CertificateHolder(issuerCertificate.getEncoded())) | |||
: exUtils.createAuthorityKeyIdentifier(subjectPublicKeyInfo); | |||
certificateGenerator.addExtension(Extension.subjectKeyIdentifier, false, subKeyId); | |||
certificateGenerator.addExtension(Extension.authorityKeyIdentifier, false, autKeyId); | |||
if (caFlag) { | |||
BasicConstraints bc; | |||
if (-1 == pathLength) { | |||
bc = new BasicConstraints(true); | |||
} else { | |||
bc = new BasicConstraints(pathLength); | |||
} | |||
certificateGenerator.addExtension(Extension.basicConstraints, false, bc); | |||
} | |||
if (null != crlUri) { | |||
int uri = GeneralName.uniformResourceIdentifier; | |||
DERIA5String crlUriDer = new DERIA5String(crlUri); | |||
GeneralName gn = new GeneralName(uri, crlUriDer); | |||
DERSequence gnDer = new DERSequence(gn); | |||
GeneralNames gns = GeneralNames.getInstance(gnDer); | |||
DistributionPointName dpn = new DistributionPointName(0, gns); | |||
DistributionPoint distp = new DistributionPoint(dpn, null, null); | |||
DERSequence distpDer = new DERSequence(distp); | |||
certificateGenerator.addExtension(Extension.cRLDistributionPoints, false, distpDer); | |||
} | |||
if (null != ocspUri) { | |||
int uri = GeneralName.uniformResourceIdentifier; | |||
GeneralName ocspName = new GeneralName(uri, ocspUri); | |||
AuthorityInformationAccess authorityInformationAccess = | |||
new AuthorityInformationAccess(X509ObjectIdentifiers.ocspAccessMethod, ocspName); | |||
certificateGenerator.addExtension(Extension.authorityInfoAccess, false, authorityInformationAccess); | |||
} | |||
if (null != keyUsage) { | |||
certificateGenerator.addExtension(Extension.keyUsage, true, keyUsage); | |||
} | |||
JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(signatureAlgorithm); | |||
signerBuilder.setProvider("BC"); | |||
X509CertificateHolder certHolder = | |||
certificateGenerator.build(signerBuilder.build(issuerPrivateKey)); | |||
/* | |||
* Next certificate factory trick is needed to make sure that the | |||
* certificate delivered to the caller is provided by the default | |||
* security provider instead of BouncyCastle. If we don't do this trick | |||
* we might run into trouble when trying to use the CertPath validator. | |||
*/ | |||
// CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); | |||
// certificate = (X509Certificate) certificateFactory | |||
// .generateCertificate(new ByteArrayInputStream(certificate | |||
// .getEncoded())); | |||
return new JcaX509CertificateConverter().getCertificate(certHolder); | |||
} | |||
private static X509CRL generateCrl(X509Certificate issuer, PrivateKey issuerPrivateKey) | |||
throws CertificateEncodingException, IOException, CRLException, OperatorCreationException { | |||
X509CertificateHolder holder = new X509CertificateHolder(issuer.getEncoded()); | |||
X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(holder.getIssuer(), new Date()); | |||
crlBuilder.setNextUpdate(new Date(new Date().getTime() + 100000)); | |||
JcaContentSignerBuilder contentBuilder = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC"); | |||
CRLNumber crlNumber = new CRLNumber(new BigInteger("1234")); | |||
crlBuilder.addExtension(Extension.cRLNumber, false, crlNumber); | |||
X509CRLHolder x509Crl = crlBuilder.build(contentBuilder.build(issuerPrivateKey)); | |||
return new JcaX509CRLConverter().setProvider("BC").getCRL(x509Crl); | |||
} | |||
private static OCSPResp createOcspResp(X509Certificate certificate, | |||
boolean revoked, X509Certificate issuerCertificate, | |||
X509Certificate ocspResponderCertificate, | |||
PrivateKey ocspResponderPrivateKey, String signatureAlgorithm, | |||
long nonceTimeinMillis) | |||
throws Exception { | |||
DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder() | |||
.setProvider("BC").build().get(CertificateID.HASH_SHA1); | |||
X509CertificateHolder issuerHolder = new X509CertificateHolder(issuerCertificate.getEncoded()); | |||
CertificateID certId = new CertificateID(digestCalc, issuerHolder, certificate.getSerialNumber()); | |||
// request | |||
//create a nonce to avoid replay attack | |||
BigInteger nonce = BigInteger.valueOf(nonceTimeinMillis); | |||
DEROctetString nonceDer = new DEROctetString(nonce.toByteArray()); | |||
Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, true, nonceDer); | |||
Extensions exts = new Extensions(ext); | |||
OCSPReqBuilder ocspReqBuilder = new OCSPReqBuilder(); | |||
ocspReqBuilder.addRequest(certId); | |||
ocspReqBuilder.setRequestExtensions(exts); | |||
OCSPReq ocspReq = ocspReqBuilder.build(); | |||
SubjectPublicKeyInfo keyInfo = new SubjectPublicKeyInfo | |||
(CertificateID.HASH_SHA1, ocspResponderCertificate.getPublicKey().getEncoded()); | |||
BasicOCSPRespBuilder basicOCSPRespBuilder = new BasicOCSPRespBuilder(keyInfo, digestCalc); | |||
basicOCSPRespBuilder.setResponseExtensions(exts); | |||
// request processing | |||
Req[] requestList = ocspReq.getRequestList(); | |||
for (Req ocspRequest : requestList) { | |||
CertificateID certificateID = ocspRequest.getCertID(); | |||
CertificateStatus certificateStatus = CertificateStatus.GOOD; | |||
if (revoked) { | |||
certificateStatus = new RevokedStatus(new Date(), CRLReason.privilegeWithdrawn); | |||
} | |||
basicOCSPRespBuilder.addResponse(certificateID, certificateStatus); | |||
} | |||
// basic response generation | |||
X509CertificateHolder[] chain = null; | |||
if (!ocspResponderCertificate.equals(issuerCertificate)) { | |||
// TODO: HorribleProxy can't convert array input params yet | |||
chain = new X509CertificateHolder[] { | |||
new X509CertificateHolder(ocspResponderCertificate.getEncoded()), | |||
issuerHolder | |||
}; | |||
} | |||
ContentSigner contentSigner = new JcaContentSignerBuilder("SHA1withRSA") | |||
.setProvider("BC").build(ocspResponderPrivateKey); | |||
BasicOCSPResp basicOCSPResp = basicOCSPRespBuilder.build(contentSigner, chain, new Date(nonceTimeinMillis)); | |||
OCSPRespBuilder ocspRespBuilder = new OCSPRespBuilder(); | |||
return ocspRespBuilder.build(OCSPRespBuilder.SUCCESSFUL, basicOCSPResp); | |||
} | |||
} |
@@ -16,14 +16,17 @@ | |||
==================================================================== */ | |||
package org.apache.poi.xslf.usermodel; | |||
import static org.apache.poi.POIDataSamples.TEST_PROPERTY; | |||
import static org.junit.Assert.assertArrayEquals; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertNotNull; | |||
import static org.junit.Assert.assertTrue; | |||
import java.awt.geom.Rectangle2D; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.HashMap; | |||
@@ -247,4 +250,24 @@ public class TestXSLFPictureShape { | |||
slideShow.close(); | |||
} | |||
@Test | |||
public void renderSvgImage() throws Exception { | |||
String dataDirName = System.getProperty(TEST_PROPERTY); | |||
final String SVG_FILE = (dataDirName != null ? "../" : "") + "src/documentation/resources/images/project-header.svg"; | |||
XMLSlideShow ppt = new XMLSlideShow(); | |||
XSLFSlide slide = ppt.createSlide(); | |||
XSLFPictureData svgPic = ppt.addPicture(new File(dataDirName, SVG_FILE), PictureType.SVG); | |||
XSLFPictureShape shape = XSLFPictureShape.addSvgImage(slide, svgPic, PictureType.JPEG, null); | |||
Rectangle2D anchor = shape.getAnchor(); | |||
anchor.setRect(100, 100, anchor.getWidth(), anchor.getHeight()); | |||
shape.setAnchor(anchor); | |||
// try (FileOutputStream fos = new FileOutputStream("svgtest.pptx")) { | |||
// ppt.write(fos); | |||
// } | |||
} | |||
} |
@@ -0,0 +1,45 @@ | |||
-----BEGIN PRIVATE KEY----- | |||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCr+sR6OR6R49ni | |||
/w/jjCSOewGvOjzJelKaGoT/cYmvx6JFrFwy3PV7FZEhRPQCKvE+cCOYAwBFTC7K | |||
uN0h/cen7tVF/idVWlzOTU/3Tdy/J7sb2KfToHvjxKkeMUl1SHvf1et+3HQHQSII | |||
Vf8A3cu0cL91sYSd32ZZYm2TeKxbTc1GO/R9OzvSOxCQaIVbAiMQvY04EQZb0F7/ | |||
eBRj8UAdMbsTRVYuOSWrXX2DL5U3/nq/c8F0ozeyUOEbzWacZ83OcDEFC0hCrGyy | |||
ptKtEQBWhbgSZ9BRc5nZ9q97VoDOLwrb24zBI45AsRBvwRAhf5DLV5cOw6X8MdQd | |||
p0pKcN9HAgMBAAECggEAVcPyNe3EZAcYQw7mMplSJcgMOAG4DNY22Wk+SFGr04Cg | |||
WVSyih8NQPupa8kCUw5tTrEH3ygn+2cZsrlsdiYkaog9zfEIVpWA0NVXesJWwvGi | |||
aymp0G0pO5Z4rHjx5E5okGETVynDp1aBDV0tlZYGn47WvG/x7fVaClt+v9ufQMyD | |||
snUEf2lpWhUtoUhlOnDZr791QPLUkVi0roLSD0M2vPM1cbV8EO4SdhOOyTDCNFvo | |||
IAcysKV0UBN/TJOyB7FyDw1cJtJ9Ja/X9YoLcW+0fTZNhlYq6VrowCQyEchlb8Pz | |||
bRhUJc6oGffpaxtrMk8pmTSScpSTV5PtB3E4t6mYYQKBgQDVPX+GdnuNyyNdvF3g | |||
SplJZdyAjW21mZ4gdYLxyGHIsgTrz4SKJ49HZoaDdwwpQRZWnQC7gxCXbJ2IHwMF | |||
+Qz7pp7eoRhQ3eBqQxfIypOeRSC6n3OIOYxIJlcV3O/PTGwu2eJhxck1ZG/j90DW | |||
NDo9hQYLFpD2xGiaWd611HncsQKBgQDOdzBZF/1PbUh/zz1nwAVPT85IM1vwU85k | |||
LIQUY/P+LFUvSa7gk3sabn6Zop5Cv0z+I1j5NW1lobLR4u02jqIb/dqeJ+XOt27n | |||
UMiYI/6WBA6Mkrf9j10BpFuSBWYotpkr8PZcog1v+FnODcOsG6Nl7fiJjkpPD6AF | |||
MCIHip4ZdwKBgC4fKhkMQXcOy+x3VJqxp+v/My0+6c7QlioRIKxpGfVNw9C5RsKX | |||
Ad+ApnGC60d1A37iYIkuNQV7gasygfXlw1Ae3tfqYhcDlomFT3ynjDw8WXLkEBoT | |||
0Gq+mDFrYxckQXX0vIlHPVjmC2l0TjrGex5ZSlBVpi8pljJkY85SUbVRAoGBAICq | |||
BCYxbfl2aAzGEEU8g9KWMD4MS4osH92LZE/0rhPCet96MpHfNoMVQq3pimicIJXr | |||
X0IGSoNgTjjACwXJwzpga0HOKUc2RtW+IRO2squ4IXz23dQU6GfijfIkjTJoAHJC | |||
urSlhGw3v3dKWptBqgUWVKEcXDCC0z0Ibtx2ROonAoGAWcRq3xpkbeR4K4yd+K9w | |||
4CBi8sdlz48tmZDSVigjq27Jk/6Ttk5K6x8M1rhUWYZ+ak1Y2NVV24oqnIdtIZrE | |||
BdgvzJCJN3Yx3FMYzUVqONFfGyppi0E7WvSYX0qlXKeZDcjgp1ORCOhiQL3ufE1+ | |||
LdXLunLfjJzNnvX7XoTAyyE= | |||
-----END PRIVATE KEY----- | |||
-----BEGIN CERTIFICATE----- | |||
MIICojCCAYqgAwIBAgIEXA5r3jANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhN | |||
dWogS2xpYzAeFw0xODEyMTAxMzM2MzBaFw0xOTEyMTAxMzM2MzBaMBMxETAPBgNV | |||
BAMMCE11aiBLbGljMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq/rE | |||
ejkekePZ4v8P44wkjnsBrzo8yXpSmhqE/3GJr8eiRaxcMtz1exWRIUT0AirxPnAj | |||
mAMARUwuyrjdIf3Hp+7VRf4nVVpczk1P903cvye7G9in06B748SpHjFJdUh739Xr | |||
ftx0B0EiCFX/AN3LtHC/dbGEnd9mWWJtk3isW03NRjv0fTs70jsQkGiFWwIjEL2N | |||
OBEGW9Be/3gUY/FAHTG7E0VWLjklq119gy+VN/56v3PBdKM3slDhG81mnGfNznAx | |||
BQtIQqxssqbSrREAVoW4EmfQUXOZ2fave1aAzi8K29uMwSOOQLEQb8EQIX+Qy1eX | |||
DsOl/DHUHadKSnDfRwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAJ5hi+ZinJ4QOA | |||
hC2sPuawdj6k+C3+CXNpRV5eMagBJZYPMsUfEPrP7ZjRiBpQVQyx6rktXqObrmTw | |||
e12yMor5cc8kGyQ6GmYoEoCKFS/S08fK5j5bwwy8KWfyH8tRGsEHeowPw3eIZCv7 | |||
gowUhb3SOsh3osAtafSe9aS0rGNTGBdSTFnJEiew8zpWbdIFKySYxU8nmHNpIPXh | |||
O/EuAYMwbcF3BhM20Gm5hxqrgSWe7S+q3KqSJbs+k95j0jr8xoNzwUd8NzI40Uu1 | |||
L1ejyqvGHjxhQooIej1Ea/MSp7v5ifpBWSp3yxlOjAnZPSEewCutMyHylIulS0sK | |||
2JQfgcdd | |||
-----END CERTIFICATE----- |
@@ -0,0 +1,45 @@ | |||
-----BEGIN PRIVATE KEY----- | |||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDMcC/+xW5l/R8i | |||
M9IKNdebiCQxkn/gfSjXD7oddrg42POCuPElhaGlHXPI+DCvNQ1+lbCTDJ7rk8F2 | |||
DbefV80uwzMkMKnn0i3GW2K8LIGrv74ACYIzueHK+Mrm3N90/Xb6IqSR430SYH9r | |||
RbIfCqCVa0LJbVS2Pw2vBmvJHE/DVoSjKXlO73EbN4G4lJnj80NaZp6I0DDsoVup | |||
htKJWLtYwB3CDpI0oDst8/Beq7esDyyHHWf3v0TOtH19lRWSoD9yEmg+bRMKYfnu | |||
T5rWj8tF+cRjh+SIyBjuIn7n4qwnAs62CQKFLjp/3PjOEbaD/aCX+6xMWvAtk0UT | |||
KIDwYsejAgMBAAECggEAX0h2hibconpMFXPTlGCt4dadD+G46hdLfpjc5Lidehlb | |||
vXSXKCbVRRCqE2PhxPdUF8iKqK68Lw4JoA0apRCWGBJwdpCbz+k83nNfXFbeBP9z | |||
/a5w2czr+N7mKp2NJIix+DlHPJurgnIpUQUF1MPuPlXkpd7oGZzfstCqUex5HJAU | |||
N03lL/Z5/hgq93sLh1ZgPWhwAOYnps0rsOnFYXlXW7WVXvL1xpyBidZstOw+BdVC | |||
7BQNvKVa4KGC/PdgW7K4327JeMjRsX1nDUzZbORM5wihLE5idIHmDbQUm4G8JZul | |||
tLeV2Ew28L91jcc55Pbe40tBeMSqgSuU2r6/dU4uCQKBgQDlUCI8vW1Q7J92m+zU | |||
Y8lHu5mS4nlwXJEpuUoCuD2falAZW40+sG3fqZ1tEiCEpEYSYKMP0vbFrNLQ68av | |||
HWhjXWCQFvuUfAK8BsMusRIBQ/ZJduSa5vSgsQA+ZVl/Pnj9PyMN70nhueHcXjnM | |||
SNo5X3BQMNERaec7Lr0mRjDGHwKBgQDkOvcbdt4gCwpAeARzCTh15uw+q8KWPz77 | |||
hPbaO4GJIMZZmRULhsrgBNZAsREasO4ZKJLI+wtukCXqgHAGzdSl6aK1fDLgFYqP | |||
Hp8UvgvIdF5yV7oAma9gRbK47sQlyBSFvQUyAuvA7wcviQ43/TIa173iBS07WfpA | |||
KlZu6+Ol/QKBgQDMJfglDRtKJS6eIJjKSQADzZ8eZmNoxfAyLhQWscGir7oZqSjo | |||
+5cFvPI7DR1IGRuM9t2Uk+M//uk8N/uNIOgzNglmnh9hhLnGfVq8scVuvPuBUciZ | |||
oy179bha1E3F+28pPlFN8Y9b1umeD4DzPpSQ6UeLDLrD/v1t8eFRNsHrTwKBgE7v | |||
4gW6wCrfBqWznP5YoxGMVAt9BqlGqLb/jw195ViTYGce3juFXGfM5HmthFfx9/f1 | |||
o4cl5RdRffu0foqr6C+WNjOFCGeeq7TCh4z6CkNDlGMB2pBYl2K52I3D702N/SMg | |||
dEqO3hF12stjIOrWhNzp33/sAG/1t+s3eXuV1L/VAoGAe0d/g5LcACFbl0sDRcSV | |||
WYsKHyR2dieALKf47mhzQtKmY506zM22n52s9sUT92j4jf/4dnP5OciJniSppgfW | |||
V64/856DuMNPoIssmNh3tQwjm8I0/t+iDGJJSHnQthG0AwD2NRe/D8Uty4P7ED64 | |||
0gEVrDJEgT60MrQt15f+1sM= | |||
-----END PRIVATE KEY----- | |||
-----BEGIN CERTIFICATE----- | |||
MIICrDCCAZSgAwIBAgIEXA/YwjANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1N | |||
eSBTZWNvbmQga2V5MB4XDTE4MTIxMTE1MzMyMloXDTE5MTIxMTE1MzMyMlowGDEW | |||
MBQGA1UEAwwNTXkgU2Vjb25kIGtleTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC | |||
AQoCggEBAMxwL/7FbmX9HyIz0go115uIJDGSf+B9KNcPuh12uDjY84K48SWFoaUd | |||
c8j4MK81DX6VsJMMnuuTwXYNt59XzS7DMyQwqefSLcZbYrwsgau/vgAJgjO54cr4 | |||
yubc33T9dvoipJHjfRJgf2tFsh8KoJVrQsltVLY/Da8Ga8kcT8NWhKMpeU7vcRs3 | |||
gbiUmePzQ1pmnojQMOyhW6mG0olYu1jAHcIOkjSgOy3z8F6rt6wPLIcdZ/e/RM60 | |||
fX2VFZKgP3ISaD5tEwph+e5PmtaPy0X5xGOH5IjIGO4ifufirCcCzrYJAoUuOn/c | |||
+M4RtoP9oJf7rExa8C2TRRMogPBix6MCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA | |||
n3rvm1DatfrrFdhNOfNwlYMF12ND1BIEFYYI13vIkZ5wmb+gNYZu32FqdTpX//qV | |||
VkNLXJj/DrR+rKWH2jZwabaYyv5XmJl9Y0/LqN3ExiqijYsPgpWEKCzhfyv+Qe5B | |||
Qzx8WCJjGh0UMQ3P8/FRYsUPf5AFMdz9jZK0uYCFFgkb4LDp23MbNYG4eRBB+/+R | |||
Arq/poWd+eb5G79+rnrGGCtqhVuxrMP8xkPy4Rkg8zr56CxQSHX7qXehYc9x/dTj | |||
CyHPrOUAw4W/pYShYtHsqORQivPvwnwQQ4upK6s2l5pbPwyEzV45clIPU+3tjvn7 | |||
67DLhc66Sz4/XkWeEXeeMg== | |||
-----END CERTIFICATE----- |