898 lines
31 KiB
Java
898 lines
31 KiB
Java
/* ====================================================================
|
|
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 static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.OO_DIGSIG_NS;
|
|
import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.XADES_132_NS;
|
|
|
|
import java.security.PrivateKey;
|
|
import java.security.Provider;
|
|
import java.security.cert.X509Certificate;
|
|
import java.util.ArrayList;
|
|
import java.util.Date;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.UUID;
|
|
|
|
import javax.xml.crypto.URIDereferencer;
|
|
import javax.xml.crypto.dsig.CanonicalizationMethod;
|
|
import javax.xml.crypto.dsig.DigestMethod;
|
|
import javax.xml.crypto.dsig.XMLSignatureFactory;
|
|
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
|
|
|
|
import org.apache.poi.EncryptedDocumentException;
|
|
import org.apache.poi.openxml4j.opc.OPCPackage;
|
|
import org.apache.poi.poifs.crypt.HashAlgorithm;
|
|
import org.apache.poi.poifs.crypt.dsig.facets.KeyInfoSignatureFacet;
|
|
import org.apache.poi.poifs.crypt.dsig.facets.OOXMLSignatureFacet;
|
|
import org.apache.poi.poifs.crypt.dsig.facets.Office2010SignatureFacet;
|
|
import org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet;
|
|
import org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet;
|
|
import org.apache.poi.poifs.crypt.dsig.services.RevocationDataService;
|
|
import org.apache.poi.poifs.crypt.dsig.services.SignaturePolicyService;
|
|
import org.apache.poi.poifs.crypt.dsig.services.TSPTimeStampService;
|
|
import org.apache.poi.poifs.crypt.dsig.services.TimeStampService;
|
|
import org.apache.poi.poifs.crypt.dsig.services.TimeStampServiceValidator;
|
|
import org.apache.poi.util.POILogFactory;
|
|
import org.apache.poi.util.POILogger;
|
|
import org.apache.xml.security.signature.XMLSignature;
|
|
import org.w3c.dom.events.EventListener;
|
|
|
|
/**
|
|
* This class bundles the configuration options used for the existing
|
|
* signature facets.
|
|
* Apart of the thread local members (e.g. opc-package) most values will probably be constant, so
|
|
* it might be configured centrally (e.g. by spring)
|
|
*/
|
|
public class SignatureConfig {
|
|
|
|
private static final POILogger LOG = POILogFactory.getLogger(SignatureConfig.class);
|
|
|
|
public interface SignatureConfigurable {
|
|
void setSignatureConfig(SignatureConfig signatureConfig);
|
|
}
|
|
|
|
private ThreadLocal<OPCPackage> opcPackage = new ThreadLocal<OPCPackage>();
|
|
private ThreadLocal<XMLSignatureFactory> signatureFactory = new ThreadLocal<XMLSignatureFactory>();
|
|
private ThreadLocal<KeyInfoFactory> keyInfoFactory = new ThreadLocal<KeyInfoFactory>();
|
|
private ThreadLocal<Provider> provider = new ThreadLocal<Provider>();
|
|
|
|
private List<SignatureFacet> signatureFacets = new ArrayList<SignatureFacet>();
|
|
private HashAlgorithm digestAlgo = HashAlgorithm.sha1;
|
|
private Date executionTime = new Date();
|
|
private PrivateKey key;
|
|
private List<X509Certificate> signingCertificateChain;
|
|
|
|
/**
|
|
* the optional signature policy service used for XAdES-EPES.
|
|
*/
|
|
private SignaturePolicyService signaturePolicyService;
|
|
private URIDereferencer uriDereferencer = null;
|
|
private String canonicalizationMethod = CanonicalizationMethod.INCLUSIVE;
|
|
|
|
private boolean includeEntireCertificateChain = true;
|
|
private boolean includeIssuerSerial = false;
|
|
private boolean includeKeyValue = false;
|
|
|
|
/**
|
|
* the time-stamp service used for XAdES-T and XAdES-X.
|
|
*/
|
|
private TimeStampService tspService = new TSPTimeStampService();
|
|
/**
|
|
* timestamp service provider URL
|
|
*/
|
|
private String tspUrl;
|
|
private boolean tspOldProtocol = false;
|
|
/**
|
|
* if not defined, it's the same as the main digest
|
|
*/
|
|
private HashAlgorithm tspDigestAlgo = null;
|
|
private String tspUser;
|
|
private String tspPass;
|
|
private TimeStampServiceValidator tspValidator;
|
|
/**
|
|
* the optional TSP request policy OID.
|
|
*/
|
|
private String tspRequestPolicy = "1.3.6.1.4.1.13762.3";
|
|
private String userAgent = "POI XmlSign Service TSP Client";
|
|
private String proxyUrl;
|
|
|
|
/**
|
|
* the optional revocation data service used for XAdES-C and XAdES-X-L.
|
|
* When <code>null</code> the signature will be limited to XAdES-T only.
|
|
*/
|
|
private RevocationDataService revocationDataService;
|
|
/**
|
|
* if not defined, it's the same as the main digest
|
|
*/
|
|
private HashAlgorithm xadesDigestAlgo = null;
|
|
private String xadesRole = null;
|
|
private String xadesSignatureId = "idSignedProperties";
|
|
private boolean xadesSignaturePolicyImplied = true;
|
|
private String xadesCanonicalizationMethod = CanonicalizationMethod.EXCLUSIVE;
|
|
|
|
/**
|
|
* Work-around for Office 2010 IssuerName encoding.
|
|
*/
|
|
private boolean xadesIssuerNameNoReverseOrder = true;
|
|
|
|
/**
|
|
* The signature Id attribute value used to create the XML signature. A
|
|
* <code>null</code> value will trigger an automatically generated signature Id.
|
|
*/
|
|
private String packageSignatureId = "idPackageSignature";
|
|
|
|
/**
|
|
* Gives back the human-readable description of what the citizen will be
|
|
* signing. The default value is "Office OpenXML Document".
|
|
*/
|
|
private String signatureDescription = "Office OpenXML Document";
|
|
|
|
/**
|
|
* The process of signing includes the marshalling of xml structures.
|
|
* This also includes the canonicalization. Currently this leads to problems
|
|
* with certain namespaces, so this EventListener is used to interfere
|
|
* with the marshalling process.
|
|
*/
|
|
EventListener signatureMarshalListener = null;
|
|
|
|
/**
|
|
* Map of namespace uris to prefix
|
|
* If a mapping is specified, the corresponding elements will be prefixed
|
|
*/
|
|
Map<String,String> namespacePrefixes = new HashMap<String,String>();
|
|
|
|
/**
|
|
* Inits and checks the config object.
|
|
* If not set previously, complex configuration properties also get
|
|
* created/initialized via this initialization call.
|
|
*
|
|
* @param onlyValidation if true, only a subset of the properties
|
|
* is initialized, which are necessary for validation. If false,
|
|
* also the other properties needed for signing are been taken care of
|
|
*/
|
|
protected void init(boolean onlyValidation) {
|
|
if (opcPackage == null) {
|
|
throw new EncryptedDocumentException("opcPackage is null");
|
|
}
|
|
if (uriDereferencer == null) {
|
|
uriDereferencer = new OOXMLURIDereferencer();
|
|
}
|
|
if (uriDereferencer instanceof SignatureConfigurable) {
|
|
((SignatureConfigurable)uriDereferencer).setSignatureConfig(this);
|
|
}
|
|
if (namespacePrefixes.isEmpty()) {
|
|
/*
|
|
* OOo doesn't like ds namespaces so per default prefixing is off.
|
|
*/
|
|
// namespacePrefixes.put(XML_DIGSIG_NS, "");
|
|
namespacePrefixes.put(OO_DIGSIG_NS, "mdssi");
|
|
namespacePrefixes.put(XADES_132_NS, "xd");
|
|
}
|
|
|
|
if (onlyValidation) return;
|
|
|
|
if (signatureMarshalListener == null) {
|
|
signatureMarshalListener = new SignatureMarshalListener();
|
|
}
|
|
|
|
if (signatureMarshalListener instanceof SignatureConfigurable) {
|
|
((SignatureConfigurable)signatureMarshalListener).setSignatureConfig(this);
|
|
}
|
|
|
|
if (tspService != null) {
|
|
tspService.setSignatureConfig(this);
|
|
}
|
|
|
|
if (signatureFacets.isEmpty()) {
|
|
addSignatureFacet(new OOXMLSignatureFacet());
|
|
addSignatureFacet(new KeyInfoSignatureFacet());
|
|
addSignatureFacet(new XAdESSignatureFacet());
|
|
addSignatureFacet(new Office2010SignatureFacet());
|
|
}
|
|
|
|
for (SignatureFacet sf : signatureFacets) {
|
|
sf.setSignatureConfig(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param signatureFacet the signature facet is appended to facet list
|
|
*/
|
|
public void addSignatureFacet(SignatureFacet signatureFacet) {
|
|
signatureFacets.add(signatureFacet);
|
|
}
|
|
|
|
/**
|
|
* @return the list of facets, may be empty when the config object is not initialized
|
|
*/
|
|
public List<SignatureFacet> getSignatureFacets() {
|
|
return signatureFacets;
|
|
}
|
|
|
|
/**
|
|
* @param signatureFacets the new list of facets
|
|
*/
|
|
public void setSignatureFacets(List<SignatureFacet> signatureFacets) {
|
|
this.signatureFacets = signatureFacets;
|
|
}
|
|
|
|
/**
|
|
* @return the main digest algorithm, defaults to sha-1
|
|
*/
|
|
public HashAlgorithm getDigestAlgo() {
|
|
return digestAlgo;
|
|
}
|
|
|
|
/**
|
|
* @param digestAlgo the main digest algorithm
|
|
*/
|
|
public void setDigestAlgo(HashAlgorithm digestAlgo) {
|
|
this.digestAlgo = digestAlgo;
|
|
}
|
|
|
|
/**
|
|
* @return the opc package to be used by this thread, stored as thread-local
|
|
*/
|
|
public OPCPackage getOpcPackage() {
|
|
return opcPackage.get();
|
|
}
|
|
|
|
/**
|
|
* @param opcPackage the opc package to be handled by this thread, stored as thread-local
|
|
*/
|
|
public void setOpcPackage(OPCPackage opcPackage) {
|
|
this.opcPackage.set(opcPackage);
|
|
}
|
|
|
|
/**
|
|
* @return the private key
|
|
*/
|
|
public PrivateKey getKey() {
|
|
return key;
|
|
}
|
|
|
|
/**
|
|
* @param key the private key
|
|
*/
|
|
public void setKey(PrivateKey key) {
|
|
this.key = key;
|
|
}
|
|
|
|
/**
|
|
* @return the certificate chain, index 0 is usually the certificate matching
|
|
* the private key
|
|
*/
|
|
public List<X509Certificate> getSigningCertificateChain() {
|
|
return signingCertificateChain;
|
|
}
|
|
|
|
/**
|
|
* @param signingCertificateChain the certificate chain, index 0 should be
|
|
* the certificate matching the private key
|
|
*/
|
|
public void setSigningCertificateChain(
|
|
List<X509Certificate> signingCertificateChain) {
|
|
this.signingCertificateChain = signingCertificateChain;
|
|
}
|
|
|
|
/**
|
|
* @return the time at which the document is signed, also used for the timestamp service.
|
|
* defaults to now
|
|
*/
|
|
public Date getExecutionTime() {
|
|
return executionTime;
|
|
}
|
|
|
|
/**
|
|
* @param executionTime sets the time at which the document ought to be signed
|
|
*/
|
|
public void setExecutionTime(Date executionTime) {
|
|
this.executionTime = executionTime;
|
|
}
|
|
|
|
/**
|
|
* @return the service to be used for XAdES-EPES properties. There's no default implementation
|
|
*/
|
|
public SignaturePolicyService getSignaturePolicyService() {
|
|
return signaturePolicyService;
|
|
}
|
|
|
|
/**
|
|
* @param signaturePolicyService the service to be used for XAdES-EPES properties
|
|
*/
|
|
public void setSignaturePolicyService(SignaturePolicyService signaturePolicyService) {
|
|
this.signaturePolicyService = signaturePolicyService;
|
|
}
|
|
|
|
/**
|
|
* @return the dereferencer used for Reference/@URI attributes, defaults to {@link OOXMLURIDereferencer}
|
|
*/
|
|
public URIDereferencer getUriDereferencer() {
|
|
return uriDereferencer;
|
|
}
|
|
|
|
/**
|
|
* @param uriDereferencer the dereferencer used for Reference/@URI attributes
|
|
*/
|
|
public void setUriDereferencer(URIDereferencer uriDereferencer) {
|
|
this.uriDereferencer = uriDereferencer;
|
|
}
|
|
|
|
/**
|
|
* @return Gives back the human-readable description of what the citizen
|
|
* will be signing. The default value is "Office OpenXML Document".
|
|
*/
|
|
public String getSignatureDescription() {
|
|
return signatureDescription;
|
|
}
|
|
|
|
/**
|
|
* @param signatureDescription the human-readable description of
|
|
* what the citizen will be signing.
|
|
*/
|
|
public void setSignatureDescription(String signatureDescription) {
|
|
this.signatureDescription = signatureDescription;
|
|
}
|
|
|
|
/**
|
|
* @return the default canonicalization method, defaults to INCLUSIVE
|
|
*/
|
|
public String getCanonicalizationMethod() {
|
|
return canonicalizationMethod;
|
|
}
|
|
|
|
/**
|
|
* @param canonicalizationMethod the default canonicalization method
|
|
*/
|
|
public void setCanonicalizationMethod(String canonicalizationMethod) {
|
|
this.canonicalizationMethod = canonicalizationMethod;
|
|
}
|
|
|
|
/**
|
|
* @return The signature Id attribute value used to create the XML signature.
|
|
* Defaults to "idPackageSignature"
|
|
*/
|
|
public String getPackageSignatureId() {
|
|
return packageSignatureId;
|
|
}
|
|
|
|
/**
|
|
* @param packageSignatureId The signature Id attribute value used to create the XML signature.
|
|
* A <code>null</code> value will trigger an automatically generated signature Id.
|
|
*/
|
|
public void setPackageSignatureId(String packageSignatureId) {
|
|
this.packageSignatureId = nvl(packageSignatureId,"xmldsig-"+UUID.randomUUID());
|
|
}
|
|
|
|
/**
|
|
* @return the url of the timestamp provider (TSP)
|
|
*/
|
|
public String getTspUrl() {
|
|
return tspUrl;
|
|
}
|
|
|
|
/**
|
|
* @param tspUrl the url of the timestamp provider (TSP)
|
|
*/
|
|
public void setTspUrl(String tspUrl) {
|
|
this.tspUrl = tspUrl;
|
|
}
|
|
|
|
/**
|
|
* @return if true, uses timestamp-request/response mimetype,
|
|
* if false, timestamp-query/reply mimetype
|
|
*/
|
|
public boolean isTspOldProtocol() {
|
|
return tspOldProtocol;
|
|
}
|
|
|
|
/**
|
|
* @param tspOldProtocol defines the timestamp-protocol mimetype
|
|
* @see #isTspOldProtocol
|
|
*/
|
|
public void setTspOldProtocol(boolean tspOldProtocol) {
|
|
this.tspOldProtocol = tspOldProtocol;
|
|
}
|
|
|
|
/**
|
|
* @return the hash algorithm to be used for the timestamp entry.
|
|
* Defaults to the hash algorithm of the main entry
|
|
*/
|
|
public HashAlgorithm getTspDigestAlgo() {
|
|
return nvl(tspDigestAlgo,digestAlgo);
|
|
}
|
|
|
|
/**
|
|
* @param tspDigestAlgo the algorithm to be used for the timestamp entry.
|
|
* if <code>null</code>, the hash algorithm of the main entry
|
|
*/
|
|
public void setTspDigestAlgo(HashAlgorithm tspDigestAlgo) {
|
|
this.tspDigestAlgo = tspDigestAlgo;
|
|
}
|
|
|
|
/**
|
|
* @return the proxy url to be used for all communications.
|
|
* Currently this affects the timestamp service
|
|
*/
|
|
public String getProxyUrl() {
|
|
return proxyUrl;
|
|
}
|
|
|
|
/**
|
|
* @param proxyUrl the proxy url to be used for all communications.
|
|
* Currently this affects the timestamp service
|
|
*/
|
|
public void setProxyUrl(String proxyUrl) {
|
|
this.proxyUrl = proxyUrl;
|
|
}
|
|
|
|
/**
|
|
* @return the timestamp service. Defaults to {@link TSPTimeStampService}
|
|
*/
|
|
public TimeStampService getTspService() {
|
|
return tspService;
|
|
}
|
|
|
|
/**
|
|
* @param tspService the timestamp service
|
|
*/
|
|
public void setTspService(TimeStampService tspService) {
|
|
this.tspService = tspService;
|
|
}
|
|
|
|
/**
|
|
* @return the user id for the timestamp service - currently only basic authorization is supported
|
|
*/
|
|
public String getTspUser() {
|
|
return tspUser;
|
|
}
|
|
|
|
/**
|
|
* @param tspUser the user id for the timestamp service - currently only basic authorization is supported
|
|
*/
|
|
public void setTspUser(String tspUser) {
|
|
this.tspUser = tspUser;
|
|
}
|
|
|
|
/**
|
|
* @return the password for the timestamp service
|
|
*/
|
|
public String getTspPass() {
|
|
return tspPass;
|
|
}
|
|
|
|
/**
|
|
* @param tspPass the password for the timestamp service
|
|
*/
|
|
public void setTspPass(String tspPass) {
|
|
this.tspPass = tspPass;
|
|
}
|
|
|
|
/**
|
|
* @return the validator for the timestamp service (certificate)
|
|
*/
|
|
public TimeStampServiceValidator getTspValidator() {
|
|
return tspValidator;
|
|
}
|
|
|
|
/**
|
|
* @param tspValidator the validator for the timestamp service (certificate)
|
|
*/
|
|
public void setTspValidator(TimeStampServiceValidator tspValidator) {
|
|
this.tspValidator = tspValidator;
|
|
}
|
|
|
|
/**
|
|
* @return the optional revocation data service used for XAdES-C and XAdES-X-L.
|
|
* When <code>null</code> the signature will be limited to XAdES-T only.
|
|
*/
|
|
public RevocationDataService getRevocationDataService() {
|
|
return revocationDataService;
|
|
}
|
|
|
|
/**
|
|
* @param revocationDataService the optional revocation data service used for XAdES-C and XAdES-X-L.
|
|
* When <code>null</code> the signature will be limited to XAdES-T only.
|
|
*/
|
|
public void setRevocationDataService(RevocationDataService revocationDataService) {
|
|
this.revocationDataService = revocationDataService;
|
|
}
|
|
|
|
/**
|
|
* @return hash algorithm used for XAdES. Defaults to the {@link #getDigestAlgo()}
|
|
*/
|
|
public HashAlgorithm getXadesDigestAlgo() {
|
|
return nvl(xadesDigestAlgo,digestAlgo);
|
|
}
|
|
|
|
/**
|
|
* @param xadesDigestAlgo hash algorithm used for XAdES.
|
|
* When <code>null</code>, defaults to {@link #getDigestAlgo()}
|
|
*/
|
|
public void setXadesDigestAlgo(HashAlgorithm xadesDigestAlgo) {
|
|
this.xadesDigestAlgo = xadesDigestAlgo;
|
|
}
|
|
|
|
/**
|
|
* @return the user agent used for http communication (e.g. to the TSP)
|
|
*/
|
|
public String getUserAgent() {
|
|
return userAgent;
|
|
}
|
|
|
|
/**
|
|
* @param userAgent the user agent used for http communication (e.g. to the TSP)
|
|
*/
|
|
public void setUserAgent(String userAgent) {
|
|
this.userAgent = userAgent;
|
|
}
|
|
|
|
/**
|
|
* @return the asn.1 object id for the tsp request policy.
|
|
* Defaults to <code>1.3.6.1.4.1.13762.3</code>
|
|
*/
|
|
public String getTspRequestPolicy() {
|
|
return tspRequestPolicy;
|
|
}
|
|
|
|
/**
|
|
* @param tspRequestPolicy the asn.1 object id for the tsp request policy.
|
|
*/
|
|
public void setTspRequestPolicy(String tspRequestPolicy) {
|
|
this.tspRequestPolicy = tspRequestPolicy;
|
|
}
|
|
|
|
/**
|
|
* @return true, if the whole certificate chain is included in the signature.
|
|
* When false, only the signer cert will be included
|
|
*/
|
|
public boolean isIncludeEntireCertificateChain() {
|
|
return includeEntireCertificateChain;
|
|
}
|
|
|
|
/**
|
|
* @param includeEntireCertificateChain if true, include the whole certificate chain.
|
|
* If false, only include the signer cert
|
|
*/
|
|
public void setIncludeEntireCertificateChain(boolean includeEntireCertificateChain) {
|
|
this.includeEntireCertificateChain = includeEntireCertificateChain;
|
|
}
|
|
|
|
/**
|
|
* @return if true, issuer serial number is included
|
|
*/
|
|
public boolean isIncludeIssuerSerial() {
|
|
return includeIssuerSerial;
|
|
}
|
|
|
|
/**
|
|
* @param includeIssuerSerial if true, issuer serial number is included
|
|
*/
|
|
public void setIncludeIssuerSerial(boolean includeIssuerSerial) {
|
|
this.includeIssuerSerial = includeIssuerSerial;
|
|
}
|
|
|
|
/**
|
|
* @return if true, the key value of the public key (certificate) is included
|
|
*/
|
|
public boolean isIncludeKeyValue() {
|
|
return includeKeyValue;
|
|
}
|
|
|
|
/**
|
|
* @param includeKeyValue if true, the key value of the public key (certificate) is included
|
|
*/
|
|
public void setIncludeKeyValue(boolean includeKeyValue) {
|
|
this.includeKeyValue = includeKeyValue;
|
|
}
|
|
|
|
/**
|
|
* @return the xades role element. If <code>null</code> the claimed role element is omitted.
|
|
* Defaults to <code>null</code>
|
|
*/
|
|
public String getXadesRole() {
|
|
return xadesRole;
|
|
}
|
|
|
|
/**
|
|
* @param xadesRole the xades role element. If <code>null</code> the claimed role element is omitted.
|
|
*/
|
|
public void setXadesRole(String xadesRole) {
|
|
this.xadesRole = xadesRole;
|
|
}
|
|
|
|
/**
|
|
* @return the Id for the XAdES SignedProperties element.
|
|
* Defaults to <code>idSignedProperties</code>
|
|
*/
|
|
public String getXadesSignatureId() {
|
|
return nvl(xadesSignatureId, "idSignedProperties");
|
|
}
|
|
|
|
/**
|
|
* @param xadesSignatureId the Id for the XAdES SignedProperties element.
|
|
* When <code>null</code> defaults to <code>idSignedProperties</code>
|
|
*/
|
|
public void setXadesSignatureId(String xadesSignatureId) {
|
|
this.xadesSignatureId = xadesSignatureId;
|
|
}
|
|
|
|
/**
|
|
* @return when true, include the policy-implied block.
|
|
* Defaults to <code>true</code>
|
|
*/
|
|
public boolean isXadesSignaturePolicyImplied() {
|
|
return xadesSignaturePolicyImplied;
|
|
}
|
|
|
|
/**
|
|
* @param xadesSignaturePolicyImplied when true, include the policy-implied block
|
|
*/
|
|
public void setXadesSignaturePolicyImplied(boolean xadesSignaturePolicyImplied) {
|
|
this.xadesSignaturePolicyImplied = xadesSignaturePolicyImplied;
|
|
}
|
|
|
|
/**
|
|
* Make sure the DN is encoded using the same order as present
|
|
* within the certificate. This is an Office2010 work-around.
|
|
* Should be reverted back.
|
|
*
|
|
* XXX: not correct according to RFC 4514.
|
|
*
|
|
* @return when true, the issuer DN is used instead of the issuer X500 principal
|
|
*/
|
|
public boolean isXadesIssuerNameNoReverseOrder() {
|
|
return xadesIssuerNameNoReverseOrder;
|
|
}
|
|
|
|
/**
|
|
* @param xadesIssuerNameNoReverseOrder when true, the issuer DN instead of the issuer X500 prinicpal is used
|
|
*/
|
|
public void setXadesIssuerNameNoReverseOrder(boolean xadesIssuerNameNoReverseOrder) {
|
|
this.xadesIssuerNameNoReverseOrder = xadesIssuerNameNoReverseOrder;
|
|
}
|
|
|
|
|
|
/**
|
|
* @return the event listener which is active while xml structure for
|
|
* the signature is created.
|
|
* Defaults to {@link SignatureMarshalListener}
|
|
*/
|
|
public EventListener getSignatureMarshalListener() {
|
|
return signatureMarshalListener;
|
|
}
|
|
|
|
/**
|
|
* @param signatureMarshalListener the event listener watching the xml structure
|
|
* generation for the signature
|
|
*/
|
|
public void setSignatureMarshalListener(EventListener signatureMarshalListener) {
|
|
this.signatureMarshalListener = signatureMarshalListener;
|
|
}
|
|
|
|
/**
|
|
* @return the map of namespace uri (key) to prefix (value)
|
|
*/
|
|
public Map<String, String> getNamespacePrefixes() {
|
|
return namespacePrefixes;
|
|
}
|
|
|
|
/**
|
|
* @param namespacePrefixes the map of namespace uri (key) to prefix (value)
|
|
*/
|
|
public void setNamespacePrefixes(Map<String, String> namespacePrefixes) {
|
|
this.namespacePrefixes = namespacePrefixes;
|
|
}
|
|
|
|
/**
|
|
* helper method for null/default value handling
|
|
* @param value
|
|
* @param defaultValue
|
|
* @return if value is not null, return value otherwise defaultValue
|
|
*/
|
|
protected static <T> T nvl(T value, T defaultValue) {
|
|
return value == null ? defaultValue : value;
|
|
}
|
|
|
|
/**
|
|
* Each digest method has its own IV (initial vector)
|
|
*
|
|
* @return the IV depending on the main digest method
|
|
*/
|
|
public byte[] getHashMagic() {
|
|
// see https://www.ietf.org/rfc/rfc3110.txt
|
|
// RSA/SHA1 SIG Resource Records
|
|
byte result[];
|
|
switch (getDigestAlgo()) {
|
|
case sha1: result = new byte[]
|
|
{ 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e
|
|
, 0x03, 0x02, 0x1a, 0x04, 0x14 };
|
|
break;
|
|
case sha224: result = new byte[]
|
|
{ 0x30, 0x2b, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
|
|
, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x04, 0x1c };
|
|
break;
|
|
case sha256: result = new byte[]
|
|
{ 0x30, 0x2f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
|
|
, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x04, 0x20 };
|
|
break;
|
|
case sha384: result = new byte[]
|
|
{ 0x30, 0x3f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
|
|
, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x04, 0x30 };
|
|
break;
|
|
case sha512: result = new byte[]
|
|
{ 0x30, 0x4f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
|
|
, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x04, 0x40 };
|
|
break;
|
|
case ripemd128: result = new byte[]
|
|
{ 0x30, 0x1b, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24
|
|
, 0x03, 0x02, 0x02, 0x04, 0x10 };
|
|
break;
|
|
case ripemd160: result = new byte[]
|
|
{ 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24
|
|
, 0x03, 0x02, 0x01, 0x04, 0x14 };
|
|
break;
|
|
// case ripemd256: result = new byte[]
|
|
// { 0x30, 0x2b, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24
|
|
// , 0x03, 0x02, 0x03, 0x04, 0x20 };
|
|
// break;
|
|
default: throw new EncryptedDocumentException("Hash algorithm "
|
|
+getDigestAlgo()+" not supported for signing.");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @return the uri for the signature method, i.e. currently only rsa is
|
|
* supported, so it's the rsa variant of the main digest
|
|
*/
|
|
public String getSignatureMethodUri() {
|
|
switch (getDigestAlgo()) {
|
|
case sha1: return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
|
|
case sha224: return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA224;
|
|
case sha256: return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256;
|
|
case sha384: return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA384;
|
|
case sha512: return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512;
|
|
case ripemd160: return XMLSignature.ALGO_ID_SIGNATURE_RSA_RIPEMD160;
|
|
default: throw new EncryptedDocumentException("Hash algorithm "
|
|
+getDigestAlgo()+" not supported for signing.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return the uri for the main digest
|
|
*/
|
|
public String getDigestMethodUri() {
|
|
return getDigestMethodUri(getDigestAlgo());
|
|
}
|
|
|
|
/**
|
|
* @param digestAlgo the digest algo, currently only sha* and ripemd160 is supported
|
|
* @return the uri for the given digest
|
|
*/
|
|
public static String getDigestMethodUri(HashAlgorithm digestAlgo) {
|
|
switch (digestAlgo) {
|
|
case sha1: return DigestMethod.SHA1;
|
|
case sha224: return "http://www.w3.org/2001/04/xmldsig-more#sha224";
|
|
case sha256: return DigestMethod.SHA256;
|
|
case sha384: return "http://www.w3.org/2001/04/xmldsig-more#sha384";
|
|
case sha512: return DigestMethod.SHA512;
|
|
case ripemd160: return DigestMethod.RIPEMD160;
|
|
default: throw new EncryptedDocumentException("Hash algorithm "
|
|
+digestAlgo+" not supported for signing.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param signatureFactory the xml signature factory, saved as thread-local
|
|
*/
|
|
public void setSignatureFactory(XMLSignatureFactory signatureFactory) {
|
|
this.signatureFactory.set(signatureFactory);
|
|
}
|
|
|
|
/**
|
|
* @return the xml signature factory (thread-local)
|
|
*/
|
|
public XMLSignatureFactory getSignatureFactory() {
|
|
XMLSignatureFactory sigFac = signatureFactory.get();
|
|
if (sigFac == null) {
|
|
sigFac = XMLSignatureFactory.getInstance("DOM", getProvider());
|
|
setSignatureFactory(sigFac);
|
|
}
|
|
return sigFac;
|
|
}
|
|
|
|
/**
|
|
* @param keyInfoFactory the key factory, saved as thread-local
|
|
*/
|
|
public void setKeyInfoFactory(KeyInfoFactory keyInfoFactory) {
|
|
this.keyInfoFactory.set(keyInfoFactory);
|
|
}
|
|
|
|
/**
|
|
* @return the key factory (thread-local)
|
|
*/
|
|
public KeyInfoFactory getKeyInfoFactory() {
|
|
KeyInfoFactory keyFac = keyInfoFactory.get();
|
|
if (keyFac == null) {
|
|
keyFac = KeyInfoFactory.getInstance("DOM", getProvider());
|
|
setKeyInfoFactory(keyFac);
|
|
}
|
|
return keyFac;
|
|
}
|
|
|
|
/**
|
|
* This method tests the existence of xml signature provider in the following order:
|
|
* <ul>
|
|
* <li>the class pointed to by the system property "jsr105Provider"</li>
|
|
* <li>the Santuario xmlsec provider</li>
|
|
* <li>the JDK xmlsec provider</li>
|
|
* </ul>
|
|
*
|
|
* For signing the classes are linked against the Santuario xmlsec, so this might
|
|
* only work for validation (not tested).
|
|
*
|
|
* @return the xml dsig provider
|
|
*/
|
|
public Provider getProvider() {
|
|
Provider prov = provider.get();
|
|
if (prov == null) {
|
|
String dsigProviderNames[] = {
|
|
System.getProperty("jsr105Provider"),
|
|
"org.apache.jcp.xml.dsig.internal.dom.XMLDSigRI", // Santuario xmlsec
|
|
"org.jcp.xml.dsig.internal.dom.XMLDSigRI" // JDK xmlsec
|
|
};
|
|
for (String pn : dsigProviderNames) {
|
|
if (pn == null) continue;
|
|
try {
|
|
prov = (Provider)Class.forName(pn).newInstance();
|
|
break;
|
|
} catch (Exception e) {
|
|
LOG.log(POILogger.DEBUG, "XMLDsig-Provider '"+pn+"' can't be found - trying next.");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (prov == null) {
|
|
throw new RuntimeException("JRE doesn't support default xml signature provider - set jsr105Provider system property!");
|
|
}
|
|
|
|
return prov;
|
|
}
|
|
|
|
/**
|
|
* @return the cannonicalization method for XAdES-XL signing.
|
|
* Defaults to <code>EXCLUSIVE</code>
|
|
* @see <a href="http://docs.oracle.com/javase/7/docs/api/javax/xml/crypto/dsig/CanonicalizationMethod.html">javax.xml.crypto.dsig.CanonicalizationMethod</a>
|
|
*/
|
|
public String getXadesCanonicalizationMethod() {
|
|
return xadesCanonicalizationMethod;
|
|
}
|
|
|
|
/**
|
|
* @param xadesCanonicalizationMethod the cannonicalization method for XAdES-XL signing
|
|
* @see <a href="http://docs.oracle.com/javase/7/docs/api/javax/xml/crypto/dsig/CanonicalizationMethod.html">javax.xml.crypto.dsig.CanonicalizationMethod</a>
|
|
*/
|
|
public void setXadesCanonicalizationMethod(String xadesCanonicalizationMethod) {
|
|
this.xadesCanonicalizationMethod = xadesCanonicalizationMethod;
|
|
}
|
|
}
|