diff options
3 files changed, 454 insertions, 0 deletions
diff --git a/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmMapper.java b/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmMapper.java new file mode 100644 index 0000000..8af0db2 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmMapper.java @@ -0,0 +1,120 @@ +package at.gv.egiz.pdfas.algorithmSuite;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Utility for mapping signature and hash algorithms between URI and abbreviation.<br>
+ * Supported algorithms are defined in external resource {@value #SUPPORTED_ALGORITHMS_RESOURCE}<br>
+ *
+ * e.g. <code>sha1 <-> http://www.w3.org/2000/09/xmldsig#sha1</code>
+ *
+ * @author dferbas
+ *
+ */
+
+public class AlgorithmMapper {
+ private static final String SUPPORTED_ALGORITHMS_RESOURCE = "supportedAlgorithms.txt";
+ private static Map algorithmMap = new HashMap();
+ private static Log log = LogFactory.getLog(AlgorithmMapper.class);
+
+ /**
+ * read available algorithms from resource
+ */
+ static {
+ try {
+ log.debug("reading supported algorithms from " + SUPPORTED_ALGORITHMS_RESOURCE);
+ List lines = IOUtils.readLines(AlgorithmMapper.class.getResourceAsStream(SUPPORTED_ALGORITHMS_RESOURCE));
+ for (int i = 0; i < lines.size(); i++) {
+ String uri = (String)lines.get(i);
+ if (isValidLine(uri)) {
+ String abbr = parseAbbreviation(uri);
+ algorithmMap.put(abbr, uri);
+ if (log.isDebugEnabled()) {
+ log.debug("added supported algorithm: " + abbr + "=" + uri);
+ }
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("error loading supportetAlgorithms.properties file", e);
+ }
+ }
+
+ /**
+ * Get algorithm abbreviation (equals last part after namespace#) from uri<br>
+ *
+ * e.g. <code>sha1 from http://www.w3.org/2000/09/xmldsig#sha1</code>
+ *
+ * @param algorithmUri algorithm URI
+ * @return algorithm abbreviation
+ */
+ public static String getAbbreviation(String algorithmUri) {
+ if (!algorithmMap.containsValue(algorithmUri)) {
+ throw new RuntimeException("unsupported Algorithm " + algorithmUri);
+ }
+ return parseAbbreviation(algorithmUri);
+ }
+
+ private static boolean isValidLine(String uri) {
+ if (uri == null || uri.length() == 0) return false;
+ if (uri.trim().startsWith("#")) return false;
+ return true;
+ }
+
+ /**
+ * Get valid URI from algorithm abbreviation<br>
+ *
+ * e.g. <code>http://www.w3.org/2000/09/xmldsig#sha1 from sha1</code>
+ *
+ * @param algorithmShort
+ * @return URI
+ */
+ public static String getUri(String algorithmShort) {
+ if (!algorithmMap.containsKey(algorithmShort)) {
+ throw new RuntimeException("unsupported Algorithm " + algorithmShort);
+ }
+ return (String)algorithmMap.get(algorithmShort);
+ }
+
+ /**
+ * Get hash (abbreviation) from signature method (eg. sha1 from ecdsa-sha1).
+ * @param suite abbreviation or full qualified signature method
+ * @return digest method abbreviation
+ */
+ public static String getHashAbbrFromSuite(String suite) {
+ try {
+ int pos = suite.lastIndexOf('-');
+ return suite.substring(pos + 1);
+ } catch (Exception e) {
+ throw new RuntimeException("cannot parse algorithm string for hash method: " + suite);
+ }
+ }
+
+ /**
+ *
+ * Get hash method (URI) from signature method
+ * @param suite abbreviation or full qualified signature method
+ * @return digest method URI
+ *
+ */
+ public static String getHashUriFromSuite(String suite) {
+ return getUri(getHashAbbrFromSuite(suite));
+ }
+
+ private static String parseAbbreviation(String algorithmUri) {
+ try {
+ int pos = algorithmUri.lastIndexOf('#');
+ return algorithmUri.substring(pos + 1);
+ } catch (Exception e) {
+ throw new RuntimeException("invalid algorithm entry in " + SUPPORTED_ALGORITHMS_RESOURCE
+ + ": " + algorithmUri);
+ }
+ }
+
+}
diff --git a/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmSuiteObject.java b/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmSuiteObject.java new file mode 100644 index 0000000..d95516e --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmSuiteObject.java @@ -0,0 +1,141 @@ +package at.gv.egiz.pdfas.algorithmSuite;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Defines a used algorithm suite. Contains a {@link #signatureMethod} a
+ * {@link #dataDigestMethod} a {@link #propertiesDigestMethod} and a
+ * {@link #certDigestMethod}.<br>
+ *
+ * This structured object is representing a string like
+ * <code>ecdsa-sha256:sha256:sha256:ripemd160</code><br>
+ * meaning
+ * <code>signatureMethod:dataDigestMethod:propertiesDigestMethod:certDigestMethod</code>
+ *
+ * @author dferbas
+ *
+ */
+public class AlgorithmSuiteObject {
+ private static Log log = LogFactory.getLog(AlgorithmSuiteObject.class);
+
+ private String signatureMethod;
+ private String dataDigestMethod;
+ private String propertiesDigestMethod;
+ private String certDigestMethod;
+
+ /**
+ * Create emtpy algorithm suite object
+ */
+ public AlgorithmSuiteObject() {
+
+ }
+
+ /**
+ * Create object from parameter string like
+ * <code>etsi-moc-1.2:ecdsa-sha1:ripemd160@207c44ff</code>
+ *
+ * @param parameterString
+ */
+ public AlgorithmSuiteObject(String parameterString) {
+ parseFrom(parameterString);
+ }
+
+ /**
+ * Initializes object from parameter string like
+ * <code>etsi-moc-1.2:ecdsa-sha1:ripemd160@207c44ff</code>
+ *
+ * @param parameterString
+ */
+ public void parseFrom(String parameterString) {
+ log.debug("parsing algorithmSuite from " + parameterString);
+ if (parameterString != null) {
+ parameterString = parameterString.split("@")[0];
+ String[] arr = parameterString.split(":");
+ if (arr.length > 1) {
+ this.signatureMethod = arr[1];
+ this.dataDigestMethod = this.propertiesDigestMethod = this.certDigestMethod = AlgorithmMapper
+ .getHashAbbrFromSuite(arr[1]);
+
+ if (arr.length > 2) {
+ this.dataDigestMethod = this.propertiesDigestMethod = this.certDigestMethod = arr[2];
+ }
+ if (arr.length > 3) {
+ this.propertiesDigestMethod = this.certDigestMethod = arr[3];
+ }
+ if (arr.length > 4) {
+ this.certDigestMethod = arr[4];
+ }
+ log.debug("successfully parsed to: " + this.toString());
+ }
+ }
+ }
+
+ /**
+ * Returns if algorithm object is valid specified (values are available)
+ *
+ * @return
+ */
+ public boolean isSpecified() {
+ return this.signatureMethod != null;
+ }
+
+ /**
+ * Signature method abbreviation. E.g. rsa-sha1, ecdsa-sha256
+ *
+ * @return
+ */
+ public String getSignatureMethod() {
+ return this.signatureMethod;
+ }
+
+ /**
+ * Digest method for data (document). E.g. sha1, md5
+ *
+ * @return
+ */
+ public String getDataDigestMethod() {
+ return this.dataDigestMethod;
+ }
+
+ /**
+ * Digest method for properties. E.g. sha1, md5
+ *
+ * @return
+ */
+ public String getPropertiesDigestMethod() {
+ return this.propertiesDigestMethod;
+ }
+
+ /**
+ * Digest method for certificate digest. E.g. sha1, md5
+ *
+ * @return
+ */
+ public String getCertDigestMethod() {
+ return this.certDigestMethod;
+ }
+
+ public void setSignatureMethod(String signatureMethod) {
+ this.signatureMethod = signatureMethod;
+ }
+
+ public void setDataDigestMethod(String dataDigestMethod) {
+ this.dataDigestMethod = dataDigestMethod;
+ }
+
+ public void setPropertiesDigestMethod(String propertiesDigestMethod) {
+ this.propertiesDigestMethod = propertiesDigestMethod;
+ }
+
+ public void setCertDigestMethod(String certDigestMethod) {
+ this.certDigestMethod = certDigestMethod;
+ }
+
+ public String toString() {
+ return "AlgorithmSuiteObject [certDigestMethod=" + this.certDigestMethod
+ + ", dataDigestMethod=" + this.dataDigestMethod + ", propertiesDigestMethod="
+ + this.propertiesDigestMethod + ", signatureMethod=" + this.signatureMethod + "]";
+ }
+
+}
diff --git a/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmSuiteUtil.java b/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmSuiteUtil.java new file mode 100644 index 0000000..0a1a5e4 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/algorithmSuite/AlgorithmSuiteUtil.java @@ -0,0 +1,193 @@ +package at.gv.egiz.pdfas.algorithmSuite;
+
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import at.knowcenter.wag.egov.egiz.sig.connectors.ConnectorEnvironment;
+import at.knowcenter.wag.egov.egiz.sig.connectors.TemplateReplaces;
+import at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject;
+
+/**
+ * Utility class for handling dynamic algorithm suites
+ *
+ * @author dferbas
+ *
+ */
+public class AlgorithmSuiteUtil {
+ private static Log log = LogFactory.getLog(AlgorithmSuiteUtil.class);
+
+ /**
+ * Creates new verify_xml, evaluates algorithm suite and replaces methods in verify_xml
+ *
+ * @param algSuite <code>out-param</code> empty algorithm suite object
+ * @param environment connector environment to load verify template
+ * @param so signsignator object
+ * @return verify xml
+ */
+ public static String evaluateReplaceAlgs(AlgorithmSuiteObject algSuite, ConnectorEnvironment environment, SignSignatureObject so) {
+
+ String verify_template = environment.getVerifyTemplate();
+
+ String cert_alg;
+ String verify_xml = null;
+ X509Certificate cert = so.getX509Certificate();
+ String ids_string = so.getSigID();
+
+ //AlgorithmSuiteObject algSuite = new AlgorithmSuiteObject(ids_string);
+ algSuite.parseFrom(ids_string);
+ if (algSuite.isSpecified()) {
+ log.debug("Algorithm suite defined as parameter. Using new dynamic template replacement.");
+ cert_alg = AlgorithmMapper.getUri(algSuite.getSignatureMethod());
+
+ } else {
+ log.debug("NO algorithm suite defined as parameter. Using old static algorithm.");
+
+ cert_alg = environment.getCertAlgEcdsa();
+ if (cert.getPublicKey().getAlgorithm().indexOf("RSA") >= 0) //$NON-NLS-1$
+ {
+ cert_alg = environment.getCertAlgRsa();
+ }
+
+ // fix digest methods to sha1 for old algorithm
+ String oldDigest = "sha1";
+ algSuite.setCertDigestMethod(oldDigest);
+ algSuite.setDataDigestMethod(oldDigest);
+ algSuite.setPropertiesDigestMethod(oldDigest);
+
+ }
+
+ // cert alg replace
+ verify_xml = verify_template.replaceFirst(TemplateReplaces.CERT_ALG_REPLACE, cert_alg);
+
+ // digest method replaces
+ verify_xml = verify_xml.replaceFirst(TemplateReplaces.DATA_DIGEST_REPLACE,
+ AlgorithmMapper.getUri(algSuite.getDataDigestMethod()));
+ verify_xml = verify_xml.replaceFirst(TemplateReplaces.PROPERTIES_DIGEST_REPLACE,
+ AlgorithmMapper.getUri(algSuite.getPropertiesDigestMethod()));
+ verify_xml = verify_xml.replaceFirst(TemplateReplaces.CERT_DIGEST_REPLACE,
+ AlgorithmMapper.getUri(algSuite.getCertDigestMethod()));
+ return verify_xml;
+ }
+
+
+ /**
+ * Extract algorithm suite as optimized string from <code>CreateXMLSignatureResponse<code>
+ *
+ * @param xmlResponse
+ * @return algorith suite string
+ */
+ public static String extractAlgorithmSuiteString(String xmlResponse) {
+
+ String elem = findFirstElement(xmlResponse, "SignatureMethod");
+ System.err.println(elem);
+ String alg = findAttributeValue(elem, "Algorithm");
+ System.err.println(alg);
+
+ List digAlgs = findAllAttributeValues(xmlResponse, "DigestMethod", "Algorithm");
+
+ digAlgs.add(0, AlgorithmMapper.getHashUriFromSuite(alg));
+
+ reduceDigestAlgs(digAlgs);
+
+ digAlgs.remove(0);
+
+ return createSigDevString(alg, digAlgs);
+
+ }
+
+ public static boolean isDefaultCertAlg(String algsString, String defaultCertAlg) {
+ return AlgorithmMapper.getAbbreviation(defaultCertAlg).equals(algsString);
+ }
+
+ private static String createSigDevString(String suite, List digAlgs) {
+ StringBuilder sb = new StringBuilder(AlgorithmMapper.getAbbreviation(suite));
+ for (Iterator iterator = digAlgs.iterator(); iterator.hasNext();) {
+ String dig = (String) iterator.next();
+ sb.append(":").append(AlgorithmMapper.getAbbreviation(dig));
+ }
+ return sb.toString();
+ }
+
+ private static String findAttributeValue(String elemContent, String attrName) {
+
+ String sig_alg = removeAllWhitespace(elemContent);
+
+ attrName += "=\"";
+ int start = sig_alg.indexOf(attrName) + attrName.length();
+ int end = sig_alg.indexOf("\"", start);
+ return sig_alg.substring(start, end);
+
+ }
+
+ private static String findFirstElement(String xml, String elemName) {
+ Pattern p = Pattern.compile("<[\\w]*:?" + elemName);
+ Matcher m = p.matcher(xml);
+ if (m.find()) {
+ int start = m.start();
+ int end = xml.indexOf("/>", start) + 2;
+ return xml.substring(start, end);
+ }
+ return null;
+ }
+
+ private static List findElements(String xml, String elemName) {
+ Pattern p = Pattern.compile("<[\\w]*:?" + elemName);
+ Matcher m = p.matcher(xml);
+ ArrayList res = new ArrayList();
+ while (m.find()) {
+ int start = m.start();
+ int end = xml.indexOf("/>", start) + 2;
+ res.add(xml.substring(start, end));
+ }
+ return res;
+ }
+
+ private static String findAttributeValue(String xmlContent, String elemName, String attrName) {
+ String elemCont = findFirstElement(xmlContent, elemName);
+ return findAttributeValue(elemCont, attrName);
+ }
+
+ private static List findAllAttributeValues(String xmlContent, String elemName, String attrName) {
+ List elemConts = findElements(xmlContent, elemName);
+ ArrayList res = new ArrayList();
+ for (int i = 0; i < elemConts.size(); i++) {
+ res.add(findAttributeValue((String) elemConts.get(i), attrName));
+ }
+ return res;
+ }
+
+ /**
+ * Helper method to remove whitespaces from a string
+ * @param str
+ * @return
+ */
+ public static String removeAllWhitespace(String str) {
+ return str.replaceAll("\\s", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ private static void reduceDigestAlgs(List digestAlgs) {
+ if (digestAlgs == null)
+ return;
+ reduceDigestAlgsRec(digestAlgs, digestAlgs.size() - 1);
+ }
+
+ private static void reduceDigestAlgsRec(List digestAlgs, int act) {
+ if (act <= 0)
+ return;
+ else if (digestAlgs.get(act - 1).equals(digestAlgs.get(act))) {
+ // reduce
+ digestAlgs.remove(act);
+ reduceDigestAlgsRec(digestAlgs, act - 1);
+ } else {
+ return;
+ }
+ }
+
+}
|