import org.apache.poi.poifs.crypt.dsig.services.TimeStampServiceValidator;\r
import org.apache.poi.util.POILogFactory;\r
import org.apache.poi.util.POILogger;\r
+import org.apache.xml.security.signature.XMLSignature;\r
import org.w3c.dom.events.EventListener;\r
\r
/**\r
*/\r
private HashAlgorithm xadesDigestAlgo = null;\r
private String xadesRole = null;\r
- private String xadesSignatureId = null;\r
+ private String xadesSignatureId = "idSignedProperties";\r
private boolean xadesSignaturePolicyImplied = true;\r
+ private String xadesCanonicalizationMethod = CanonicalizationMethod.EXCLUSIVE;\r
\r
/**\r
* Work-around for Office 2010 IssuerName encoding.\r
tspService.setSignatureConfig(this);\r
}\r
\r
- if (xadesSignatureId == null || xadesSignatureId.isEmpty()) {\r
- xadesSignatureId = "idSignedProperties";\r
- }\r
-\r
if (signatureFacets.isEmpty()) {\r
addSignatureFacet(new OOXMLSignatureFacet());\r
addSignatureFacet(new KeyInfoSignatureFacet());\r
public void setCanonicalizationMethod(String canonicalizationMethod) {\r
this.canonicalizationMethod = canonicalizationMethod;\r
}\r
+\r
+ /**\r
+ * @return The signature Id attribute value used to create the XML signature.\r
+ * Defaults to "idPackageSignature"\r
+ */\r
public String getPackageSignatureId() {\r
return packageSignatureId;\r
}\r
+\r
+ /**\r
+ * @param packageSignatureId The signature Id attribute value used to create the XML signature.\r
+ * A <code>null</code> value will trigger an automatically generated signature Id.\r
+ */\r
public void setPackageSignatureId(String packageSignatureId) {\r
this.packageSignatureId = nvl(packageSignatureId,"xmldsig-"+UUID.randomUUID());\r
}\r
+\r
+ /**\r
+ * @return the url of the timestamp provider (TSP)\r
+ */\r
public String getTspUrl() {\r
return tspUrl;\r
}\r
+\r
+ /**\r
+ * @param tspUrl the url of the timestamp provider (TSP)\r
+ */\r
public void setTspUrl(String tspUrl) {\r
this.tspUrl = tspUrl;\r
}\r
+ \r
+ /**\r
+ * @return if true, uses timestamp-request/response mimetype,\r
+ * if false, timestamp-query/reply mimetype \r
+ */\r
public boolean isTspOldProtocol() {\r
return tspOldProtocol;\r
}\r
+ \r
+ /**\r
+ * @param tspOldProtocol defines the timestamp-protocol mimetype\r
+ * @see {@link #isTspOldProtocol()}\r
+ */\r
public void setTspOldProtocol(boolean tspOldProtocol) {\r
this.tspOldProtocol = tspOldProtocol;\r
}\r
+ \r
+ /**\r
+ * @return the hash algorithm to be used for the timestamp entry.\r
+ * Defaults to the hash algorithm of the main entry\r
+ */\r
public HashAlgorithm getTspDigestAlgo() {\r
return nvl(tspDigestAlgo,digestAlgo);\r
}\r
+ \r
+ /**\r
+ * @param tspDigestAlgo the algorithm to be used for the timestamp entry.\r
+ * if <code>null</code>, the hash algorithm of the main entry\r
+ */\r
public void setTspDigestAlgo(HashAlgorithm tspDigestAlgo) {\r
this.tspDigestAlgo = tspDigestAlgo;\r
}\r
+\r
+ /**\r
+ * @return the proxy url to be used for all communications.\r
+ * Currently this affects the timestamp service\r
+ */\r
public String getProxyUrl() {\r
return proxyUrl;\r
}\r
+ \r
+ /**\r
+ * @param proxyUrl the proxy url to be used for all communications.\r
+ * Currently this affects the timestamp service\r
+ */\r
public void setProxyUrl(String proxyUrl) {\r
this.proxyUrl = proxyUrl;\r
}\r
+ \r
+ /**\r
+ * @return the timestamp service. Defaults to {@link TSPTimeStampService}\r
+ */\r
public TimeStampService getTspService() {\r
return tspService;\r
}\r
+ \r
+ /**\r
+ * @param tspService the timestamp service\r
+ */\r
public void setTspService(TimeStampService tspService) {\r
this.tspService = tspService;\r
}\r
+ \r
+ /**\r
+ * @return the user id for the timestamp service - currently only basic authorization is supported\r
+ */\r
public String getTspUser() {\r
return tspUser;\r
}\r
+ \r
+ /**\r
+ * @param tspUser the user id for the timestamp service - currently only basic authorization is supported\r
+ */\r
public void setTspUser(String tspUser) {\r
this.tspUser = tspUser;\r
}\r
+ \r
+ /**\r
+ * @return the password for the timestamp service\r
+ */\r
public String getTspPass() {\r
return tspPass;\r
}\r
+ \r
+ /**\r
+ * @param tspPass the password for the timestamp service\r
+ */\r
public void setTspPass(String tspPass) {\r
this.tspPass = tspPass;\r
}\r
+ \r
+ /**\r
+ * @return the validator for the timestamp service (certificate)\r
+ */\r
public TimeStampServiceValidator getTspValidator() {\r
return tspValidator;\r
}\r
+ \r
+ /**\r
+ * @param tspValidator the validator for the timestamp service (certificate)\r
+ */\r
public void setTspValidator(TimeStampServiceValidator tspValidator) {\r
this.tspValidator = tspValidator;\r
}\r
+\r
+ /**\r
+ * @return the optional revocation data service used for XAdES-C and XAdES-X-L.\r
+ * When <code>null</code> the signature will be limited to XAdES-T only.\r
+ */\r
public RevocationDataService getRevocationDataService() {\r
return revocationDataService;\r
}\r
+\r
+ /**\r
+ * @param revocationDataService the optional revocation data service used for XAdES-C and XAdES-X-L.\r
+ * When <code>null</code> the signature will be limited to XAdES-T only.\r
+ */\r
public void setRevocationDataService(RevocationDataService revocationDataService) {\r
this.revocationDataService = revocationDataService;\r
}\r
+\r
+ /**\r
+ * @return hash algorithm used for XAdES. Defaults to the {@link #getDigestAlgo()}\r
+ */\r
public HashAlgorithm getXadesDigestAlgo() {\r
return nvl(xadesDigestAlgo,digestAlgo);\r
}\r
+ \r
+ /**\r
+ * @param xadesDigestAlgo hash algorithm used for XAdES.\r
+ * When <code>null</code>, defaults to {@link #getDigestAlgo()}\r
+ */\r
public void setXadesDigestAlgo(HashAlgorithm xadesDigestAlgo) {\r
this.xadesDigestAlgo = xadesDigestAlgo;\r
}\r
+\r
+ /**\r
+ * @return the user agent used for http communication (e.g. to the TSP)\r
+ */\r
public String getUserAgent() {\r
return userAgent;\r
}\r
+ \r
+ /**\r
+ * @param userAgent the user agent used for http communication (e.g. to the TSP)\r
+ */\r
public void setUserAgent(String userAgent) {\r
this.userAgent = userAgent;\r
}\r
+\r
+ /**\r
+ * @return the asn.1 object id for the tsp request policy.\r
+ * Defaults to <code>1.3.6.1.4.1.13762.3</code>\r
+ */\r
public String getTspRequestPolicy() {\r
return tspRequestPolicy;\r
}\r
+ \r
+ /**\r
+ * @param tspRequestPolicy the asn.1 object id for the tsp request policy.\r
+ */\r
public void setTspRequestPolicy(String tspRequestPolicy) {\r
this.tspRequestPolicy = tspRequestPolicy;\r
}\r
+\r
+ /**\r
+ * @return true, if the whole certificate chain is included in the signature.\r
+ * When false, only the signer cert will be included \r
+ */\r
public boolean isIncludeEntireCertificateChain() {\r
return includeEntireCertificateChain;\r
}\r
+\r
+ /**\r
+ * @param includeEntireCertificateChain if true, include the whole certificate chain.\r
+ * If false, only include the signer cert\r
+ */\r
public void setIncludeEntireCertificateChain(boolean includeEntireCertificateChain) {\r
this.includeEntireCertificateChain = includeEntireCertificateChain;\r
}\r
+\r
+ /**\r
+ * @return if true, issuer serial number is included\r
+ */\r
public boolean isIncludeIssuerSerial() {\r
return includeIssuerSerial;\r
}\r
+\r
+ /**\r
+ * @param includeIssuerSerial if true, issuer serial number is included\r
+ */\r
public void setIncludeIssuerSerial(boolean includeIssuerSerial) {\r
this.includeIssuerSerial = includeIssuerSerial;\r
}\r
+\r
+ /**\r
+ * @return if true, the key value of the public key (certificate) is included\r
+ */\r
public boolean isIncludeKeyValue() {\r
return includeKeyValue;\r
}\r
+\r
+ /**\r
+ * @param includeKeyValue if true, the key value of the public key (certificate) is included\r
+ */\r
public void setIncludeKeyValue(boolean includeKeyValue) {\r
this.includeKeyValue = includeKeyValue;\r
}\r
+\r
+ /**\r
+ * @return the xades role element. If <code>null</code> the claimed role element is omitted.\r
+ * Defaults to <code>null</code>\r
+ */\r
public String getXadesRole() {\r
return xadesRole;\r
}\r
+\r
+ /**\r
+ * @param xadesRole the xades role element. If <code>null</code> the claimed role element is omitted.\r
+ */\r
public void setXadesRole(String xadesRole) {\r
this.xadesRole = xadesRole;\r
}\r
+\r
+ /**\r
+ * @return the Id for the XAdES SignedProperties element.\r
+ * Defaults to <code>idSignedProperties</code>\r
+ */\r
public String getXadesSignatureId() {\r
- return xadesSignatureId;\r
+ return nvl(xadesSignatureId, "idSignedProperties");\r
}\r
+\r
+ /**\r
+ * @param xadesSignatureId the Id for the XAdES SignedProperties element.\r
+ * When <code>null</code> defaults to <code>idSignedProperties</code>\r
+ */\r
public void setXadesSignatureId(String xadesSignatureId) {\r
this.xadesSignatureId = xadesSignatureId;\r
}\r
+\r
+ /**\r
+ * @return when true, include the policy-implied block.\r
+ * Defaults to <code>true</code>\r
+ */\r
public boolean isXadesSignaturePolicyImplied() {\r
return xadesSignaturePolicyImplied;\r
}\r
+\r
+ /**\r
+ * @param xadesSignaturePolicyImplied when true, include the policy-implied block\r
+ */\r
public void setXadesSignaturePolicyImplied(boolean xadesSignaturePolicyImplied) {\r
this.xadesSignaturePolicyImplied = xadesSignaturePolicyImplied;\r
}\r
+\r
+ /**\r
+ * Make sure the DN is encoded using the same order as present\r
+ * within the certificate. This is an Office2010 work-around.\r
+ * Should be reverted back.\r
+ * \r
+ * XXX: not correct according to RFC 4514.\r
+ *\r
+ * @return when true, the issuer DN is used instead of the issuer X500 principal\r
+ */\r
public boolean isXadesIssuerNameNoReverseOrder() {\r
return xadesIssuerNameNoReverseOrder;\r
}\r
+\r
+ /**\r
+ * @param xadesIssuerNameNoReverseOrder when true, the issuer DN instead of the issuer X500 prinicpal is used\r
+ */\r
public void setXadesIssuerNameNoReverseOrder(boolean xadesIssuerNameNoReverseOrder) {\r
this.xadesIssuerNameNoReverseOrder = xadesIssuerNameNoReverseOrder;\r
}\r
+\r
+ \r
+ /**\r
+ * @return the event listener which is active while xml structure for\r
+ * the signature is created.\r
+ * Defaults to {@link SignatureMarshalListener}\r
+ */\r
public EventListener getSignatureMarshalListener() {\r
return signatureMarshalListener;\r
}\r
+\r
+ /**\r
+ * @param signatureMarshalListener the event listener watching the xml structure\r
+ * generation for the signature\r
+ */\r
public void setSignatureMarshalListener(EventListener signatureMarshalListener) {\r
this.signatureMarshalListener = signatureMarshalListener;\r
}\r
+\r
+ /**\r
+ * @return the map of namespace uri (key) to prefix (value)\r
+ */\r
public Map<String, String> getNamespacePrefixes() {\r
return namespacePrefixes;\r
}\r
+\r
+ /**\r
+ * @param namespacePrefixes the map of namespace uri (key) to prefix (value)\r
+ */\r
public void setNamespacePrefixes(Map<String, String> namespacePrefixes) {\r
this.namespacePrefixes = namespacePrefixes;\r
}\r
+\r
+ /**\r
+ * helper method for null/default value handling\r
+ * @param value\r
+ * @param defaultValue\r
+ * @return if value is not null, return value otherwise defaultValue\r
+ */\r
protected static <T> T nvl(T value, T defaultValue) {\r
return value == null ? defaultValue : value;\r
}\r
+\r
+ /**\r
+ * Each digest method has its own IV (initial vector)\r
+ *\r
+ * @return the IV depending on the main digest method\r
+ */\r
public byte[] getHashMagic() {\r
// see https://www.ietf.org/rfc/rfc3110.txt\r
// RSA/SHA1 SIG Resource Records\r
return result;\r
}\r
\r
- public String getSignatureMethod() {\r
+ /**\r
+ * @return the uri for the signature method, i.e. currently only rsa is\r
+ * supported, so it's the rsa variant of the main digest\r
+ */\r
+ public String getSignatureMethodUri() {\r
switch (getDigestAlgo()) {\r
- case sha1: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;\r
- case sha224: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA224;\r
- case sha256: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256;\r
- case sha384: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA384;\r
- case sha512: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512;\r
- case ripemd160: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_RIPEMD160;\r
+ case sha1: return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;\r
+ case sha224: return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA224;\r
+ case sha256: return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256;\r
+ case sha384: return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA384;\r
+ case sha512: return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512;\r
+ case ripemd160: return XMLSignature.ALGO_ID_SIGNATURE_RSA_RIPEMD160;\r
default: throw new EncryptedDocumentException("Hash algorithm "\r
+getDigestAlgo()+" not supported for signing.");\r
}\r
}\r
\r
+ /**\r
+ * @return the uri for the main digest\r
+ */\r
public String getDigestMethodUri() {\r
return getDigestMethodUri(getDigestAlgo());\r
}\r
\r
+ /**\r
+ * @param digestAlgo the digest algo, currently only sha* and ripemd160 is supported \r
+ * @return the uri for the given digest\r
+ */\r
public static String getDigestMethodUri(HashAlgorithm digestAlgo) {\r
switch (digestAlgo) {\r
case sha1: return DigestMethod.SHA1;\r
}\r
}\r
\r
+ /**\r
+ * @param signatureFactory the xml signature factory, saved as thread-local\r
+ */\r
public void setSignatureFactory(XMLSignatureFactory signatureFactory) {\r
this.signatureFactory.set(signatureFactory);\r
}\r
\r
+ /**\r
+ * @return the xml signature factory (thread-local)\r
+ */\r
public XMLSignatureFactory getSignatureFactory() {\r
XMLSignatureFactory sigFac = signatureFactory.get();\r
if (sigFac == null) {\r
return sigFac;\r
}\r
\r
+ /**\r
+ * @param keyInfoFactory the key factory, saved as thread-local\r
+ */\r
public void setKeyInfoFactory(KeyInfoFactory keyInfoFactory) {\r
this.keyInfoFactory.set(keyInfoFactory);\r
}\r
\r
+ /**\r
+ * @return the key factory (thread-local)\r
+ */\r
public KeyInfoFactory getKeyInfoFactory() {\r
KeyInfoFactory keyFac = keyInfoFactory.get();\r
if (keyFac == null) {\r
return keyFac;\r
}\r
\r
- // currently classes are linked to Apache Santuario, so this might be superfluous \r
+ /**\r
+ * This method tests the existence of xml signature provider in the following order:\r
+ * <ul>\r
+ * <li>the class pointed to by the system property "jsr105Provider"</li>\r
+ * <li>the Santuario xmlsec provider</li>\r
+ * <li>the JDK xmlsec provider</li>\r
+ * </ul>\r
+ * \r
+ * For signing the classes are linked against the Santuario xmlsec, so this might\r
+ * only work for validation (not tested).\r
+ * \r
+ * @return the xml dsig provider\r
+ */\r
public Provider getProvider() {\r
Provider prov = provider.get();\r
if (prov == null) {\r
\r
return prov;\r
}\r
- \r
\r
+ /**\r
+ * @return the cannonicalization method for XAdES-XL signing.\r
+ * Defaults to <code>EXCLUSIVE</code>\r
+ * @see {@link CanonicalizationMethod}\r
+ */\r
+ public String getXadesCanonicalizationMethod() {\r
+ return xadesCanonicalizationMethod;\r
+ }\r
\r
+ /**\r
+ * @param xadesCanonicalizationMethod the cannonicalization method for XAdES-XL signing\r
+ * @see {@link CanonicalizationMethod}\r
+ */\r
+ public void setXadesCanonicalizationMethod(String xadesCanonicalizationMethod) {\r
+ this.xadesCanonicalizationMethod = xadesCanonicalizationMethod;\r
+ }\r
}\r
this.signaturePart = signaturePart;\r
}\r
\r
+ /**\r
+ * @return the package part containing the signature\r
+ */\r
public PackagePart getPackagePart() {\r
return signaturePart;\r
}\r
\r
+ /**\r
+ * @return the signer certificate\r
+ */\r
public X509Certificate getSigner() {\r
return signer;\r
}\r
\r
+ /**\r
+ * @return the certificate chain of the signer\r
+ */\r
public List<X509Certificate> getCertChain() {\r
return certChain;\r
}\r
\r
+ /**\r
+ * Helper method for examining the xml signature\r
+ *\r
+ * @return the xml signature document\r
+ * @throws IOException if the xml signature doesn't exist or can't be read\r
+ * @throws XmlException if the xml signature is malformed\r
+ */\r
public SignatureDocument getSignatureDocument() throws IOException, XmlException {\r
// TODO: check for XXE\r
return SignatureDocument.Factory.parse(signaturePart.getInputStream());\r
}\r
\r
+ /**\r
+ * @return true, when the xml signature is valid, false otherwise\r
+ */\r
public boolean validate() {\r
KeyInfoKeySelector keySelector = new KeyInfoKeySelector();\r
try {\r
}\r
}\r
\r
+ /**\r
+ * Constructor initializes xml signature environment, if it hasn't been initialized before\r
+ */\r
public SignatureInfo() {\r
initXmlProvider(); \r
}\r
\r
+ /**\r
+ * @return the signature config\r
+ */\r
public SignatureConfig getSignatureConfig() {\r
return signatureConfig;\r
}\r
\r
+ /**\r
+ * @param signatureConfig the signature config, needs to be set before a SignatureInfo object is used\r
+ */\r
public void setSignatureConfig(SignatureConfig signatureConfig) {\r
this.signatureConfig = signatureConfig;\r
}\r
\r
+ /**\r
+ * @return true, if first signature part is valid\r
+ */\r
public boolean verifySignature() {\r
// http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html\r
for (SignaturePart sp : getSignatureParts()){\r
return false;\r
}\r
\r
+ /**\r
+ * add the xml signature to the document\r
+ *\r
+ * @throws XMLSignatureException\r
+ * @throws MarshalException\r
+ */\r
public void confirmSignature() throws XMLSignatureException, MarshalException {\r
Document document = DocumentHelper.createDocument();\r
\r
postSign(document, signatureValue);\r
}\r
\r
+ /**\r
+ * Sign (encrypt) the digest with the private key.\r
+ * Currently only rsa is supported.\r
+ *\r
+ * @param digest the hashed input\r
+ * @return the encrypted hash\r
+ */\r
public byte[] signDigest(byte digest[]) {\r
Cipher cipher = CryptoFunctions.getCipher(signatureConfig.getKey(), CipherAlgorithm.rsa\r
, ChainingMode.ecb, null, Cipher.ENCRYPT_MODE, "PKCS1Padding");\r
}\r
}\r
\r
+ /**\r
+ * @return a signature part for each signature document.\r
+ * the parts can be validated independently.\r
+ */\r
public Iterable<SignaturePart> getSignatureParts() {\r
signatureConfig.init(true);\r
return new Iterable<SignaturePart>() {\r
};\r
}\r
\r
+ /**\r
+ * Initialize the xml signing environment and the bouncycastle provider \r
+ */\r
protected static synchronized void initXmlProvider() {\r
if (isInitialized) return;\r
isInitialized = true;\r
SignedInfo signedInfo;\r
try {\r
SignatureMethod signatureMethod = signatureFactory.newSignatureMethod\r
- (signatureConfig.getSignatureMethod(), null);\r
+ (signatureConfig.getSignatureMethodUri(), null);\r
CanonicalizationMethod canonicalizationMethod = signatureFactory\r
.newCanonicalizationMethod(signatureConfig.getCanonicalizationMethod(),\r
(C14NMethodParameterSpec) null);\r
writeDocument(document);\r
}\r
\r
+ /**\r
+ * Write XML signature into the OPC package\r
+ *\r
+ * @param document the xml signature document\r
+ * @throws MarshalException\r
+ */\r
protected void writeDocument(Document document) throws MarshalException {\r
XmlOptions xo = new XmlOptions();\r
Map<String,String> namespaceMap = new HashMap<String,String>();\r
sigsPart.addRelationship(sigPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE);\r
}\r
\r
+ /**\r
+ * Helper method for null lists, which are converted to empty lists\r
+ *\r
+ * @param other the reference to wrap, if null\r
+ * @return if other is null, an empty lists is returned, otherwise other is returned\r
+ */\r
@SuppressWarnings("unchecked")\r
private static <T> List<T> safe(List<T> other) {\r
return other == null ? Collections.EMPTY_LIST : other;\r